Skip to navigation Skip to main content Skip to footer

Technical Advisory: Cross-Site Scripting in Umbraco Rich Text Display

Technical Advisory: Cross-Site Scripting in Umbraco Rich Text Display

Vendor: Umbraco
Vendor URL: https://umbraco.com/
Versions affected: Umbraco CMS 14.3.1 or below
Systems Affected: All
Author: Liyun Li <liyun.li[at]nccgroup[dot]com>

Summary

Due to a lack of input sanitization on the server side, Umbraco CMS 14.3.1 or below is vulnerable to stored cross-site scripting (XSS) attacks through the rendering logic for rich text contents.

Impact

An attacker with the ability to modify or publish rich text contents in a Umbraco instance can steal a victim’s credentials and other private data, log their keystrokes or perform privileged actions in the context of a victim’s session.

Details

Umbraco is an open-source content management system (CMS) built on Microsoft’s .NET framework written in C#. It provides a flexible platform for developing websites, web applications, and other digital projects.

Site administrators of Umbraco can configure document types for display purposes. According to the documentation:

A Document Type acts as a data container in Umbraco, where you can add Properties (data fields or attributes) to store content. Each Property is assigned a Data Type, such as a text string, number, or rich text editor. Umbraco uses Templates to render this input data on the front end.

The issue lies in the application logic for rich text processing. For finding reproduction, first create a Document Type with Template, a document type that can be directly accessible via a URL, and configure it so content of the type can be created in the root of the content tree. The document type can then be specified as the default layout in the Template settings In other words, if the instance created is hosted on http://localhost:8080, the content created can be accessed at http://localhost:8080.

In the new document type, specify a group containing a property with a Rich Text Editor (RTE), so that the created content can be interpreted as HTML. Umbraco uses TinyMCE as the rich text editor and the editor performs input sanitization on the client side; however, the API server is lacking such security mechanism. An attacker can bypass the client side restriction by directly submitting HTTP requests (e.g. a HTTP PUT request to update an existing rich text document with the payload specified in the markup field of a rich text block) to the document endpoint at /umbraco/management/api/v1/document to store a malicious HTML payload as-is, and get the payload to be displayed on the CMS instance. This issues falls under the stored XSS category, in which the attacker persists the payload to be retrieved by browsers that visits the vulnerable page, increasing the likelihood of victims, such as other site users and administrators, being exposed to the exploit code.

Controller for updating a document:

    [HttpPut("{id:guid}")]
    [MapToApiVersion("1.0")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
    public async Task<IActionResult> Update(
        CancellationToken cancellationToken,
        Guid id,
        UpdateDocumentRequestModel requestModel)
        => await HandleRequest(id, requestModel, async () =>
        {
            ContentUpdateModel model = _documentEditingPresentationFactory.MapUpdateModel(requestModel);
            Attempt<ContentUpdateResult, ContentEditingOperationStatus> result =
                await _contentEditingService.UpdateAsync(id, model, CurrentUserKey(_backOfficeSecurityAccessor));

            return result.Success
                ? Ok()
                : ContentEditingOperationStatusResult(result.Status);
        });

Controller for creating a document

    [HttpPost]
    [MapToApiVersion("1.0")]
    [ProducesResponseType(StatusCodes.Status201Created)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
    public async Task<IActionResult> Create(
        CancellationToken cancellationToken,
        CreateDocumentRequestModel requestModel)
        => await HandleRequest(requestModel, async () =>
        {
            ContentCreateModel model = _documentEditingPresentationFactory.MapCreateModel(requestModel);
            Attempt<ContentCreateResult, ContentEditingOperationStatus> result =
                await _contentEditingService.CreateAsync(model, CurrentUserKey(_backOfficeSecurityAccessor));

            return result.Success
                ? CreatedAtId<ByKeyDocumentController>(controller => nameof(controller.ByKey), result.Result.Content!.Key)
                : ContentEditingOperationStatusResult(result.Status);
        });

As seen, the controllers above do not contain application logic for user input sanitization and is vulnerable to stored XSS. For a proof-of-concept code execution, first send a HTTP PUT request as an authenticated user to /umbraco/management/api/v1/document/DOCUMENT_ID with the following HTML code in the markup field of the JSON body to to update a created document (allowed to be stored in content root). The DOCUMENT_ID variable is the UUID of the document.

<script>alert(document.domain)</script>

The saved content can then be published by sending a subsequent HTTP PUT request to /umbraco/management/api/v1/document/DOCUMENT_ID/publish. After visiting the front page with the stored XSS payload, e.g. at http://localhost:8080, the payload will be triggered.

Due to the strict dependency sets of the available libraries for HTML input sanitization, Umbraco has opted out of implementing any specific server-side mitigation. Administrators of the CMS must implement their own sanitization solutions via the IHtmlSanitizer interface as described in the documentation (see the page Sanitizing the Rich Text Editor). However, without knowing the existence of this interface, the discrepancy in the frontend and the backend may cause developers to consider using the RTE safe by ensuring unsafe tags are filtered in Umbraco.CMS.RichTextEditor.ValidElements.

Umbraco has accepted this behavior as the majority of its customer base is unaffected.

Recommendation to Users

Identify a C/C++ HTML sanitization framework best suited for the organization if using RTE is mandatory. Seek alternative components in Umbraco for content rendering otherwise.

Acknowledgement

About NCC Group

NCC Group is a global expert in cybersecurity and risk mitigation, working with businesses to protect their brand, value and reputation against the ever-evolving threat landscape. With our knowledge, experience and global footprint, we are best placed to help businesses identify, assess, mitigate & respond to the risks they face. We are passionate about making the Internet safer and revolutionizing the way in which organizations think about cybersecurity.