Soccer HTB

Soccer HTB Box Overview Soccer is a Linux-based Hack The Box machine that focuses on web enumeration, insecure default credentials, authenticated file upload, WebSocket-based blind SQL injection, password reuse, and Linux privilege escalation. The initial foothold is obtained by discovering a Tiny File Manager instance, authenticating with the default administrative credentials, and uploading a PHP reverse shell to gain access as www-data. Further enumeration reveals an additional virtual host containing a ticket-validation feature that communicates over WebSockets. Exploiting a blind SQL injection in this functionality exposes the credentials of the player user, which are reused for SSH access. Privilege escalation to root is achieved through a misconfigured doas rule that allows player to execute dstat as root. Because a writable dstat plugin directory is available, a custom Python plugin can be loaded to spawn a root shell. Enumeration Nmap Scanning I began by running an Nmap scan against the target to identify open ports, exposed services, and available version information. The scan identified three exposed TCP ports. Port 22 was running OpenSSH, port 80 was running Nginx and redirecting requests to soccer.htb, and port 9091 exposed an unidentified HTTP-based service that returned Cannot GET / for requests to the root path. The redirect from port 80 indicated that the web server expected requests using the soccer.htb hostname. Adding the Hostname to /etc/hosts To ensure that the hostname resolved to the target IP address, I added the following entry to my local /etc/hosts file: This allowed my browser and enumeration tools to access the application using the hostname expected by the Nginx configuration. Web Application Enumeration Visiting http://soccer.htb revealed a soccer-themed website that appeared mostly static and did not immediately expose any interactive functionality. Screenshot 2026-08-05 at 21-32-47 Since the visible website did not provide a clear attack surface, I used Feroxbuster to search for hidden directories and files. The most important findings were: The /tiny path exposed a separate application, while the nested /tiny/uploads directory suggested that uploaded files might be stored in a web-accessible location. Initial Access Discovering Tiny File Manager Navigating to http://soccer.htb/tiny/ revealed a Tiny File Manager login page. Screenshot 2026-08-05 at 21-39-55 I reviewed the Tiny File Manager GitHub repository: GitHub – Tiny File Manager The documentation listed the following default credentials: I tested the administrative credentials against the login page: The credentials successfully authenticated and provided access to the Tiny File Manager dashboard. Screenshot 2026-08-05 at 21-45-00 The successful login confirmed that the application was using unchanged default administrative credentials. Since the dashboard provided file-upload functionality and the server processed PHP files, I investigated whether I could upload a PHP file into a web-accessible directory. Creating a PHP Web Shell I created a PHP file named cmd.php containing the following code: The system() function executes the operating-system command supplied through the cmd GET parameter. Initial Upload Failure I first attempted to upload cmd.php using the upload feature available in Tiny File Manager. Screenshot 2026-08-05 at 21-50-37 The upload failed because the active destination path was not writable. Screenshot 2026-08-05 at 21-50-37 The selected destination was: This failed attempt indicated that the web root itself did not allow uploads. I therefore examined the available directories to identify another location where write access was permitted. Identifying a Writable Upload Directory The Tiny File Manager dashboard contained a directory named tiny, which included an uploads subdirectory. Screenshot 2026-08-05 at 21-53-46 After opening that directory, the active destination changed from: to: Screenshot 2026-08-05 at 21-54-47 I attempted the upload again from this location, and the file was successfully uploaded. Screenshot 2026-08-05 at 21-55-37 The uploaded cmd.php file was now visible inside the /tiny/uploads directory. Screenshot 2026-08-05 at 21-56-01 Using the application’s permission-management option, I adjusted the permissions of the uploaded file. Screenshot 2026-08-05 at 21-57-33 Confirming Remote Command Execution Tiny File Manager provided a direct link to the uploaded file. Screenshot 2026-08-05 at 21-59-45 I tested the PHP file by passing the id command through the cmd parameter: Screenshot 2026-08-05 at 21-58-57 The response returned the output of the id command, confirming that the PHP file was being interpreted by the server and that I had obtained remote command execution through the authenticated file upload. Obtaining a Reverse Shell The PHP web shell provided basic command execution, but it was inconvenient for further enumeration. To obtain a more usable session, I used the PentestMonkey PHP reverse-shell script: PentestMonkey PHP Reverse Shell I modified the callback IP address in the script to match my tun0 address: Screenshot 2026-08-05 at 22-06-38 Before accessing the uploaded reverse shell, I started a Netcat listener on port 1234: A cleanup process appeared to remove files from the upload directory periodically, so the reverse-shell file needed to be uploaded and triggered before it was deleted. After uploading the reverse shell, adjusting its permissions, and accessing the direct link, the listener received a connection: The id and whoami output confirmed that the reverse shell was running as www-data. I executed /bin/bash -i to obtain a more usable Bash prompt. However, the messages about the missing TTY and unavailable job control showed that the session remained only partially interactive. At this stage, I had established the initial foothold as www-data and could continue with local enumeration. Privilege Escalation to Player After obtaining a foothold as www-data, I began enumerating the local system to identify a path toward the player user. Identifying Local Users I first inspected the /home directory: The presence of /home/player confirmed that player was a local user account on the system. Since my current shell was running as www-data, the next objective was to identify credentials or an application weakness that could provide access to this account. Enumerating Listening Services I listed the TCP services currently listening on the target: The output revealed two services that had not appeared in the external Nmap scan: Port 3000 was bound only to the loopback interface, while port 3306 indicated that a MySQL server was also running locally. These services were not directly reachable from my attacking machine because they were listening only on 127.0.0.1. Testing MySQL Authentication Since MySQL was running locally, I attempted to authenticate using the username admin and password admin: The MySQL server responded, confirming that the service was active, but the supplied credentials were rejected. Since direct database authentication did not succeed, I continued by investigating the local web-server configuration for references to the application running on port 3000. Reviewing the Nginx Configuration I inspected the enabled Nginx site configurations: The configuration revealed an additional virtual host: It also showed that requests for this hostname were being proxied through Nginx on port 80 to the application listening locally on port 3000. Screenshot 2026-08-05 at 22-19-06 This explained why port 3000 was not externally exposed while the application could still be accessed through the correct virtual host. Adding the New Virtual Host I added the newly discovered hostname to my local /etc/hosts file: This mapped both soc-player.soccer.htb and soccer.htb to the target IP address. Navigating to http://soc-player.soccer.htb displayed a similar soccer-themed application, but this version contained additional functionality compared with the original site. Screenshot 2026-08-05 at 22-23-45 The site included a match-related feature: Screenshot 2026-08-05 at 22-24-21 Creating an Application Account I created a new account through the registration interface: Screenshot 2026-08-05 at 22-25-06 After authenticating, the application redirected me to the /check page: Screenshot 2026-08-05 at 22-25-51 This page allowed users to submit a ticket identifier and determine whether the ticket was valid. Analysing WebSocket Communication I opened Burp Suite and inspected the application's traffic. The /check functionality did not submit the ticket identifier through a normal HTTP request. Instead, the application communicated with a WebSocket service. Screenshot 2026-08-05 at 22-36-57 The WebSockets history confirmed that the ticket identifier was sent as JSON to the service running on port 9091. Screenshot 2026-08-05 at 22-36-21 Screenshot 2026-08-05 at 22-37-42 The request structure contained an id value similar to: Because this value appeared to be processed by the backend when validating tickets, I tested it for SQL injection. Testing the WebSocket Parameter with SQLMap While researching SQLMap support for WebSocket endpoints, I found the following project, which demonstrated the required syntax: sqlmap-websocket-proxy The example syntax was: Using the request format observed in Burp Suite, I tested the id parameter directly with SQLMap: SQLMap confirmed that the JSON id parameter was vulnerable to both boolean-based blind and time-based blind SQL injection. The backend database management system was identified as MySQL. This also connected the WebSocket application to the local MySQL service previously observed on port 3306. Enumerating the Databases Because blind SQL injection can be slow, I increased the number of SQLMap threads to speed up data extraction and requested the available database names: SQLMap identified five databases. The application-specific database was: The remaining databases were standard MySQL system databases. Enumerating Tables in soccer_db I enumerated the tables inside the soccer_db database: The database contained a single table named: Since this table likely stored application users, I proceeded to dump its contents. Extracting the Player Credentials I used SQLMap to dump the accounts table: The table exposed plaintext credentials for the player account: Since the system already contained a local user named player and SSH was exposed on port 22, I tested whether the recovered password had been reused for the operating-system account. SSH Access as Player I connected to the target over SSH using the recovered credentials: The successful login confirmed that the application password had been reused for the local player account. This upgraded my access from the restricted www-data web-server account to an interactive SSH session as player. Capturing the User Flag From the player home directory, I read the user flag: Privilege Escalation to Root After obtaining an interactive SSH session as player, I began checking whether the account had any delegated administrative privileges. Checking Sudo Permissions I first reviewed the commands that player was permitted to execute through sudo: The output confirmed that player had no permitted sudo commands. The reference to localhost was simply the hostname against which the local sudo policy was evaluated; it did not by itself indicate a separate escalation path. Since sudo was unavailable, I continued by enumerating SUID binaries. Enumerating SUID Binaries I searched the filesystem for files with the SUID permission bit set: The most unusual entry was: doas is a utility that allows permitted users to execute commands as another user, similar to sudo. Its behaviour is controlled through a separate configuration file. Locating the Doas Configuration I searched for the doas configuration file: I then examined its contents: The rule granted the player user permission to run /usr