总结

  • 这一次,主要解决与堆密切相关的漏洞
  • 还有很多其他类型的漏洞
    • 例如栈溢出,很有名并且很容易找到
    • 条件竞争,整数溢出这类稍微有点难找
  • 简单介绍一下其他漏洞

其他类型的漏洞

栈缓冲区溢出

  • 当输入数据超出栈上分配的内存末尾时,会产生这种溢出
  • 可以通过重写返回地址来控制后续操作
  • 如果启用了canary保护,那么会检测到stack被更改
  • 特别情况,仅能超出一个元素,被称为Off-By-One Stack Buffer Overflow。这种情况下只能重写ebp/rbp等,需要进行栈调整

堆缓冲区溢出

  • 当输入的数据超出堆上分配的内存末尾时,会产生这种溢出
  • 虽然不能重写返回地址,但是可以重写例如函数指针,数据等来控制后续操作
  • 如果可以重写内存管理区域,则可以通过重写链表的next地址或者prev地址,向任意内存地址写入任意数据(之后会详细说明)

用户可控数据

  • 这是导致溢出产生的现实原因,输入的数据由用户指定,而缓冲区是固定size,向固定size的缓冲区发送任意size的数据,就导致了溢出
  • 这也能够导致Buffer Over Read
  • 通过堆溢出,能够重写堆上的malloc管理数据(被称为chunk),通过free()的时候进行unlink处理,就更改了next或者prev到能够RWX的任意内存,可以写入任意数据
  • 还可以进行GOT Overwrite

fastbins attack

  • malloc/free时,可能进行fastbins attack
  • malloc的返回地址能够任意指定,就产生了漏洞

字符串末尾NULL消除(Unterminated Null-Terminated String)

  • C语言中的字符串是数组,如果没有结尾的NULL字符(=’\0’),那么在显示字符串时也会将后面的内容显示出来,造成信息泄漏
  • 在这种情况下嘛,可以获得堆,栈,libc或者PIE的基地址,并且在某些情况下,可能泄漏例如用于加密/解密的密钥之类的重要信息
  • 这可能是一个意外的长度,也会导致溢出

内存释放后的使用(Use After Free)

  • 在malloc的内存被释放后,再次向该地址呢写入数据会发生这种情况
  • 如果该地址已经被别的功能所使用,则可以重写改地址的数据

内存二次释放(Double Free)

  • malloc申请的内存进行释放后,再次释放相同的地址
  • 如果在第一次和第二次释放之间,这个地址被其他用途X获取到,那么第二次也会释放掉这个地址
  • 那么,X在使用它时,堆中的数据可能会严重受损,因为它使用已经被释放的地址

类型混淆(Type Confusion)

  • 在使用多种结构体/类的情况下,如果用户可以选择要应用的结构/类,错误的应用可能导致溢出及函数指针覆盖

格式化字符串漏洞(FSB: Format String Bug)

  • 在printf系列函数中,如果使用printf(input)这类写法,攻击者就能够控制参数格式。正确的写法应该是printf(“%s”,input).
  • 例如使用%p或者%x之类的转换说明符,可能泄漏stack上的值。即使是在ASLR/PIE环境下也可以置顶栈地址。
  • 另外,如果用户输入在栈上,则可以输出该值地址,并以此为基础,向栈的内存地址写入数据
  • 此外,%12345x这类指定字符宽度规范,可以自由指定printf输出的字符数。通过这个可能向任意内存地址写入任意值
  • 覆盖stack或者GOT,重写数据或者函数指针,利用方式有很多
  • FORTIFY_SOURCE(使用printf_chk之类的chk)情况下,无法使用%n

基于堆的FSB

  • 格式化字符串攻击困难,用户输入不在stack上的情况
  • 使用原本存在于stack上的二重引用(A→B→C)的指针,通过A修改B的低字节,通过B修改C,… 类似这样的攻略方式

条件竞争(Race Condition)

  • 当资源管理对于竞争的处理不正确时,会发生这种情况
  • 对一个对象同时进行多个操作,可能会导致该对象的状态出现问题
  • 例如,在多人共享数据的服务中,客户端A和B同时连接的情况下,B在A打开某个文件的时候对该文件进行删除操作,类似这种情况
  • 在某些情况下,数据可能被破坏,Use After Free或者Double Free,或者是堆溢出或栈溢出

TOCTOU条件竞争(Time-of-check Time-of-use Race Condition)

  • 在A和B两次操作之间,因为另一个操作改变了状态
  • 例如A[检查文件属性(确认它不是符号链接)],B[读取文件], 在A和B两次操作之间,文件被重写为符号链接,那么B将从符号链接读取内容

使用未初始化的数据(Using Uninitialized Variable)

  • 使用之前已经被使用过的数据可能会导致意外操作
  • 例如,申请的内存,未初始化的数据是”%p%p%p…”,如果未初始化直接使用printf将导致格式化字符串攻击之类的

负数处理问题

  • 在条件比较情况中,没有正确考虑signed和unsigned的差异,从而将负数是别成大的正数的问题
  • 它能够使得条件比较变得奇怪,以及更改指针索引等操作,也可能更改操作逻辑

整数溢出(Integer Overflow)

  • 在32位数字和64位数字中,如果超出最大值或最小值,则会发生溢出。
  • 例如,通过条件比较后故意溢出,可能导致申请很大的内存,或者使其很小

目录遍历

  • 当二进制程序处理PATH时,可能被利用
  • 使用../../../../../proc/self/maps 之类的可以用于获取内存映射信息

其他Unlink经典题目

Plaid CTF 2014 – Pwnables200 –

ezhp

基本信息:

HITCON CTF 2014 Pwnables 550 –

stkof

基本信息:

  • 读取用户输入
  • 可以输入1-3,调用对应函数
    • 三个函数命名为alloc_mem, write_mem, free_mem
    • alloc_mem获得的地址,存储在bss上的PTR_TABLE[IDX++]中
    • write_mem有明显的溢出问题
    • 可以通过重写next, prev进行fastbins attack

攻略方法:

Hack.lu CTF Exploiting400 oreo

基本信息:

  • 显示menu后,读取用户输入

  • 可以输入1-5,调用对应函数

    • 五个函数命名为Add_new_rifle, Show_added_rifles,Order_selected_rifles,Leave_a_Message_with_your_Order, Show_current_stats

    • 使用这样的结构体:

    • struct RIFLE {
          char description[25]; 
          char name[25];
          char align[2];
          RIFLE* next;
      }
    • Add_new_rifle有明显的溢出问题

    • 可以通过覆盖next进行fastbins attack

攻略方法: