Aug 29, 2026·6 min read·26 visits
Unescaped user input in the RestoreAction helper leads to stored XSS inside the Silverstripe CMS administrative interface during page restoration actions.
A Stored Cross-Site Scripting (XSS) vulnerability exists in the silverstripe/versioned package prior to version 3.2.1. When an administrator restores an archived page containing a crafted Title or URLSegment, the generated restoration message is rendered as CAST_HTML without proper sanitization. This allows malicious JavaScript to execute in the administrator's browser session, compromising the confidentiality and integrity of the CMS dashboard.
The affected component is the RestoreAction helper within the silverstripe/versioned module, which manages the lifecycle, tracking, and restoration of versioned models in Silverstripe CMS applications. When an administrator accesses the ArchiveAdmin console and restores a previously archived object, the module constructs an execution status message. This message incorporates the restored object's metadata, such as its Title, URLSegment, and CMSEditLink.
Because the system treats system messages as validated HTML under the CAST_HTML formatting configuration, it does not perform automatic output sanitization on the rendered output. This behavior exposes a stored cross-site scripting (XSS) attack surface. Specifically, any malicious input stored in the draft or published attributes of a versioned page will bypass standard framework output filters and execute in the target's browser.
The vulnerability is tracked under CVE-2026-55779. It represents a classic improper neutralization of input during web page generation (CWE-79). The security impact is determined by the permissions of the user restoring the content. Since page restoration is restricted to administrative roles, the vulnerability targets high-privilege sessions within the CMS control panel.
The root cause of CVE-2026-55779 is the lack of output encoding within the getRestoreMessage method located in src/RestoreAction.php. The Silverstripe framework relies on casting rules to distinguish between plain text and rich HTML. When generating notification payloads, the framework treats the output as trusted HTML, assigning it a casting type equivalent to CAST_HTML.
In the vulnerable implementation, the getRestoreMessage method directly interpolates the title of the restored object ($restoredItem->Title) and its corresponding CMS edit URL into the notification string. This interpolation is executed via sprintf and string concatenation. Because these values originate from user-controlled fields and are stored within the database without restrictive input filtering, they serve as persistent vectors for injection payloads.
When the database stores malicious markup and subsequently retrieves it for display in the ArchiveAdmin, the lack of sanitization allows the markup to be rendered directly into the administrative UI. This bypasses the browser's same-origin boundary protections, allowing the executed script to make requests on behalf of the authenticated administrator.
The vulnerable logic resides in src/RestoreAction.php inside the getRestoreMessage function. In unpatched releases, the raw property values were assigned and used without any defensive filtering.
// Vulnerable Code Path
public static function getRestoreMessage($originalItem, $restoredItem, $changedLocation = false)
{
// Raw Title is assigned directly without escaping
$restoredID = $restoredItem->Title ?: $restoredItem->ID;
$restoredType = Convert::raw2xml(strtolower($restoredItem->i18n_singular_name() ?? ''));
$editLink = $restoredItem->CMSEditLink();
if ($editLink) {
// Unescaped restoredID and editLink are concatenated into an HTML anchor tag
$restoredID = sprintf('<a href="%s">%s</a>', $editLink, $restoredID);
}
// ...
}The fixing patch introduces Convert::raw2xml calls to properly escape all dynamic parameters. This utility is the standard Silverstripe mechanism for converting unsafe characters into XML entities.
// Patched Code Path
public static function getRestoreMessage($originalItem, $restoredItem, $changedLocation = false)
{
// Escape the Title or ID property to prevent tag injection
$restoredID = Convert::raw2xml($restoredItem->Title ?: $restoredItem->ID);
$restoredType = Convert::raw2xml(strtolower($restoredItem->i18n_singular_name() ?? ''));
$editLink = $restoredItem->CMSEditLink();
if ($editLink) {
// Escape both the target URL and the inner anchor text
$restoredID = sprintf('<a href="%s">%s</a>', Convert::raw2xml($editLink), $restoredID);
}
// ...
}Additionally, the patch applies Convert::raw2xml to the values generated during difference analysis. When the original Title or URLSegment differs from the restored state, the system records the new values. These must be escaped because they are subsequently rendered to describe the property modifications.
An attacker can exploit this vulnerability using multiple vectors depending on the configured fields. The primary vector involves the Title attribute of a page. The attacker injects an iframe payload containing a nested script tag: <iframe srcdoc="<script>alert('xss')</script>"></iframe>. Once this page is archived, the payload resides in the historical versions tables.
The second vector exploits the change-tracking output when a page property is altered. By crafting a payload using the formaction attribute on a button, such as <button formaction="javascript:alert(1)">Click</button>, the attacker targets the administrator's UI interaction. If the restored page's Name differs from the original, this dynamic block is evaluated and rendered in the success message.
Both attack vectors require the victim to perform a page restoration action. While this introduces a dependency on user interaction, administrative actions of this type are routine during content maintenance cycles, making social engineering or passive waiting highly viable strategies.
The security impact of CVE-2026-55779 is rated as Medium, with a CVSS v3.1 base score of 5.4. Although the impact is localized to the client session, the administrative nature of the targeted workspace elevates the operational consequences. The injected script executes under the origin and session credentials of the active administrator.
The attacker can read, modify, or exfiltrate sensitive data, including CSRF tokens, session identifiers, and CMS settings. Because Silverstripe administrators have full write access to the underlying application database via the site tree and settings panels, the session context can be used to perform authorized actions. This includes the creation of backdoored administrator accounts, modifying site configurations, or injecting malicious scripts into public pages.
While the scope is classified as Unchanged because the execution is confined to the client browser, the administrative privileges of the compromised user effectively grant the attacker complete logical control over the CMS content. No active exploitation has been registered in the wild, but the existence of explicit regression tests in the codebase demonstrates a straightforward exploitation path.
The primary remediation for this vulnerability is upgrading the silverstripe/versioned package to version 3.2.1 or newer. This update ensures that all parameters processed by RestoreAction::getRestoreMessage are securely escaped prior to HTML assembly.
composer update silverstripe/versionedIf immediate package upgrade is not feasible, organizations can manually apply the hotfix by modifying src/RestoreAction.php. The modification requires wrapping all dynamic user-controlled strings, including $restoredItem->Title, $restoredItem->URLSegment, and $editLink, inside calls to the Convert::raw2xml helper.
To detect potential exploitation attempts or pre-existing payloads, security teams should execute database queries across historical version tables. The query targets instances of common HTML tags and attributes inside the Title and URLSegment columns:
SELECT ID, Title, URLSegment FROM "SiteTree_Versions"
WHERE Title LIKE '%<script%' OR Title LIKE '%<iframe%' OR Title LIKE '%formaction%';CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
silverstripe/versioned Silverstripe | < 3.2.1 | 3.2.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 5.4 (Medium) |
| EPSS Score | Not Available |
| Impact | Stored Cross-Site Scripting / Session Hijacking |
| Exploit Status | PoC Available |
| KEV Status | Not Listed |
The application fails to neutralize or escape user-controlled inputs before placing them into dynamic HTML messages rendering to administrators.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.
An out-of-bounds write vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio allows unauthenticated remote attackers to execute arbitrary code via crafted pixel-shader bytecode or auto-converted clipboard payloads.
ASP.NET Core contains a thread deadlock vulnerability (CWE-833) that allows an unauthenticated remote attacker to cause a denial of service over the network by exhausting server thread pool resources.
CVE-2026-70035 is a high-severity Remote Code Execution (RCE) vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio caused by improper handling during the deserialization of untrusted data (CWE-502). An unauthenticated attacker can execute arbitrary code within the context of the affected process by delivering a crafted serialized payload.