Skip to content

Soccer — Tiny File Manager Upload and dstat Plugin Privilege Escalation

Tools
rustscan, feroxbuster, netcat, curl, sqlmap, sqlmap-websocket-proxy, ssh, doas, dstat
Skill demonstrated
Linux web foothold and privilege escalation through upload execution, database injection, and a delegated doas rule
Tags
  • linux
  • web-security
  • websocket
  • sql-injection
  • privilege-escalation
Field Value
Difficulty Easy
Target environment Ubuntu Linux; nginx 1.18.0; Tiny File Manager 2.4.3
Starting position Unauthenticated network access
Objective From exposed file-management software to root through an executable upload, WebSocket SQL injection, and a delegated dstat rule
Outcome Web-service shell, SSH access as a lab user, and a root context through dstat plugin loading

Soccer is an Easy-rated Hack The Box Linux lab that chains an exposed file manager, an executable upload directory, a WebSocket SQL injection flaw, and a delegated privilege rule into root. Default credentials open Tiny File Manager, the upload directory runs PHP, local nginx configuration reveals a second application virtual host, and its ticket-checking WebSocket is injectable and discloses an SSH credential; a doas rule then permits dstat, whose Python plugin loading yields root. Target and attacker addresses, virtual hosts, account names, credentials, and the reverse-shell payload are replaced with role-based placeholders; command syntax is preserved.

Attack path: Tiny File Manager default access → upload-directory PHP execution → web-service shell → local nginx configuration → secondary virtual host → WebSocket ticket-check SQL injection → database credential recovery → SSH as lab user → delegated doas rule for dstat → Python plugin execution → root

  • Target: an Ubuntu Linux host running SSH, nginx, and an unidentified service on port 9091.
  • Exposed services: SSH (22), HTTP (80), and a high port (9091) later identified as the ticket-checking WebSocket.
  • Starting position: unauthenticated network access, with no provided credentials.
  • Objective: follow documented paths from web access to privileged execution, showing how default configuration, an executable upload, an unvalidated WebSocket endpoint, and a delegated rule combine.
  • Constraints: activity was confined to the Hack The Box lab environment.

Observation: a full TCP scan exposes three services, and HTTP redirects to a hostname-based site, so virtual-host handling is required before web enumeration is useful.

Terminal window
rustscan -a <TARGET_IP> --ulimit 5000 -- -Pn -sC -sV -oN <SCAN_OUTPUT>
feroxbuster --url http://<PRIMARY_VHOST> --wordlist <WORDLIST> -o <CONTENT_OUTPUT>

Truncated scan and discovery output:

22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux)
80/tcp open http nginx 1.18.0 (Ubuntu)
9091/tcp open xmltec-xmlmail?
301 GET /tiny => /tiny/

Significance: the redirect confines enumeration to the primary virtual host, and the discovered /tiny path exposes a file-management interface.

Result: SSH, nginx, and a high port are exposed, the primary virtual host is identified, and /tiny hosts an application.

2. Default-Credential Access to Tiny File Manager

Section titled “2. Default-Credential Access to Tiny File Manager”

Observation: /tiny hosts Tiny File Manager 2.4.3, and public documentation and common deployments ship with well-known default credentials.

Action: authenticated to the file manager with the documented default credentials, <DEFAULT_USER> / <DEFAULT_PASSWORD>.

Terminal window
curl -s -c cookies.txt -X POST http://<PRIMARY_VHOST>/tiny/ -d "fm_usr=<DEFAULT_USER>&fm_pwd=<DEFAULT_PASSWORD>"

The response confirmed the session:

You are logged in
Tiny File Manager 2.4.3

Significance: default administrative credentials grant full file-management access, and the footer identifies the deployed version.

Result: authenticated administrative access to Tiny File Manager is obtained.

Observation: the /tiny/uploads directory executes uploaded PHP files; a phpinfo() test confirmed that execution functions such as system, exec, shell_exec, and proc_open were available.

Action: with a listener ready, uploaded a PHP file and requested it over HTTP.

Terminal window
nc -lvnp <LISTENER_PORT>
Terminal window
curl http://<PRIMARY_VHOST>/tiny/uploads/<UPLOADED_PHP>

The request returned a shell as the web-service account:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

Significance: an upload directory that executes server-side code converts administrative file access into code execution under the web-service account.

Result: command execution as the web-service account is established.

Observation: local socket and nginx configuration inspection showed a loopback database listener and a second enabled site.

Terminal window
ss -tulpn
ls -la /etc/nginx/sites-enabled/
tcp LISTEN 0 151 127.0.0.1:3306 0.0.0.0:*
default
<SECONDARY_SITE>

Significance: reading web-server configuration exposes an application virtual host that was not reachable from initial external enumeration.

Result: a second application virtual host with login, signup, and ticket-checking functionality is identified.

Observation: the ticket check is sent as a WebSocket message to port 9091, and its identifier field is SQL-injectable.

Action: proxied HTTP requests into the WebSocket message format so the SQL-injection tool could target the endpoint.

Terminal window
sqlmap-websocket-proxy -u ws://<SECONDARY_VHOST>:9091 -d '{"id":"%param%"}' -H "Origin: http://<SECONDARY_VHOST>"
sqlmap -u "http://localhost:<PROXY_PORT>/?param=1" --dump
Database: <APPLICATION_DATABASE>
Table: <ACCOUNT_TABLE>
| <ID> | <LAB_USER_EMAIL> | <LAB_USER_PASSWORD> | <LAB_USER> |

Significance: the injectable identifier reached the backing database and disclosed an account credential; the WebSocket transport received the same lack of input handling as an HTTP parameter.

Result: a credential pair for <LAB_USER> is recovered from the application database.

Observation: the recovered credential authenticates the lab user over SSH.

Terminal window
ssh <LAB_USER>@<TARGET_HOST>
# password: <LAB_USER_PASSWORD>
<LAB_USER>@<TARGET_HOST>:~$

Significance: the secret disclosed by the web application also satisfies host authentication, so the database disclosure becomes an interactive host session.

Result: the recovered credential is validated through SSH and an authenticated shell as <LAB_USER> is obtained.

Observation: a custom doas binary is present, and its configuration permits a passwordless root invocation of dstat.

Terminal window
find / -name "doas*" 2>/dev/null
/usr/local/etc/doas.conf

The configuration grants the permission:

permit nopass <LAB_USER> as root cmd /usr/bin/dstat

Significance: the rule delegates root execution of a single program, so the effective privilege depends on that program’s behavior rather than its command name.

Result: a passwordless root doas rule for dstat is identified.

Observation: dstat loads external Python plugins from directories including /usr/local/share/dstat/.

Action: wrote a plugin into a directory dstat loads, then invoked the permitted program by plugin name through the doas rule.

Terminal window
echo '<PLUGIN_CODE>' > /usr/local/share/dstat/dstat_<PLUGIN_NAME>.py
doas -u root /usr/bin/dstat --<PLUGIN_NAME>
# whoami
root

Significance: because dstat executes plugin code while running as root, the delegated rule amounts to arbitrary root code execution.

Result: a root context is obtained through the permitted dstat command.

Challenge Decision Rationale
The ticket check is transported as a WebSocket message, which the selected SQL-injection tool does not speak natively Proxied HTTP requests into the WebSocket message format Needed to drive the tool against the endpoint
The doas rule permits only dstat, not a shell Invoked a plugin loaded by the permitted program The binary’s extension mechanism, not its command name, determined the available privilege

The evidence establishes default-credential file-manager access, code execution as the web-service account, a credential recovered through WebSocket SQL injection and validated over SSH, and root through dstat plugin loading under a delegated doas rule.

Each finding pairs the observed root cause with its demonstrated impact and a prioritized action. The actions are recommendations; none was validated in the lab.

  1. Default credentials on administrative software. Known default credentials granted full file-management access. Recommendation: remove or rotate default credentials before deployment and restrict administrative interfaces to trusted access paths. Detection: alert on logins using vendor-default accounts and on administrative interface access from unexpected sources.
  2. Executable upload directory. /tiny/uploads executed uploaded PHP, giving code execution as the web-service account. Recommendation: store uploads outside executable paths and explicitly disable server-side execution in upload directories. Detection: alert on newly written executable files under the web root.
  3. Unvalidated WebSocket endpoint. The ticket-checking message reached the database without input handling, disclosing an account credential. Recommendation: apply parameterized queries and input validation to WebSocket handlers as well as HTTP routes. Detection: log and review WebSocket payloads for injection patterns.
  4. Over-broad doas delegation. A passwordless root rule for dstat allowed arbitrary code execution through its plugin loading. Recommendation: review sudo and doas allowlists against the full behavior of each permitted program, and exclude binaries that load user-controlled extensions. Detection: audit delegation rules for plugin-capable or scriptable binaries and monitor plugin directories for unexpected files.
Edit page

Last updated: