HumHub 1.18.4 / 1.18.4-pl1 – Stored Cross-Site Scripting in oEmbed confirmation

7,4

High

Detected by

Fluid Attacks AI SAST Scanner

Disclosed by

Miguel Gómez

Summary

Full name

HumHub 1.18.4 / 1.18.4-pl1 – Stored Cross-Site Scripting in oEmbed confirmation rendering through malformed provider URL

Code name

State

Public

Release date

Affected product

HumHub

Vendor

HumHub

Affected version(s)

1.18.4

Fixed version(s)

1.18.5

Vulnerability name

Stored Cross-Site Scripting

Remotely exploitable

Yes

CVSS v4.0 vector string

CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:P/VC:H/VI:H/VA:N/SC:L/SI:L/SA:N

CVSS v4.0 base score

7.4

Exploit available

Yes

Description

HumHub Community Edition 1.18.4 and 1.18.4-pl1 contain a stored Cross-Site Scripting (XSS) vulnerability in the oEmbed confirmation rendering workflow.

A low-privilege authenticated Space member who can create posts can store a crafted rich-text oEmbed link in Post[message]. When another authenticated user views the post, HumHub treats a malformed URL containing youtube.com as a supported oEmbed URL, builds a local confirmation prompt from attacker-controlled URL text, and reconstructs injected <script> tags with the response's valid CSP nonce. The payload executes in the victim's HumHub browser session.

Vulnerability

Root cause

  1. Source - low-privileged post creation

    A user with the normal CreatePost permission can submit rich-text content to:

    POST /index.php?r=post%2Fpost%2Fpost&cguid=<SPACE_GUID>
    POST /index.php?r=post%2Fpost%2Fpost&cguid=<SPACE_GUID>
    POST /index.php?r=post%2Fpost%2Fpost&cguid=<SPACE_GUID>
    POST /index.php?r=post%2Fpost%2Fpost&cguid=<SPACE_GUID>

    PostController::actionPost() loads the request body into the Post model:

  2. Persistence and rich-text post-processing

    After the post is saved, Post::afterSave() calls:

    The oEmbed extension scans the stored Markdown-like link syntax and extracts the oembed: extension ID.

  3. Permissive oEmbed provider recognition

    The default YouTube provider pattern is:

    This checks whether youtube.com or youtu.be occurs anywhere in the supplied string. It does not require a valid http or https URL, a canonical host, or a host boundary.

    As a result, the following malformed value is treated as a supported YouTube oEmbed URL:

    https:youtube.com<script>alert`Pwned`</script>
    https:youtube.com<script>alert`Pwned`</script>
    https:youtube.com<script>alert`Pwned`</script>
    https:youtube.com<script>alert`Pwned`</script>
  4. Unsafe confirmation prompt construction

    When oEmbed confirmation is required and the domain is not already trusted, UrlOembed::getOEmbed() calls confirmationContent($url).

    confirmationContent() derives a display prefix with:

    
    
    
    
    
    
    
    

    For the malformed payload, parse_url() returns a scheme and path but no host, so $urlPrefix becomes the complete attacker-controlled string, including <script>.

    The value is then inserted as raw tag content:

    The URL-derived data is not HTML-encoded before it is included in the confirmation HTML.

  5. CSP nonce bypass through script reconstruction

    After the confirmation HTML is built, UrlOembed::getOEmbed() searches the result for <script> tags:

    preg_replace_callback('/<script\b([^>]*)>(.*?)<\/script>/is', ...)
    preg_replace_callback('/<script\b([^>]*)>(.*?)<\/script>/is', ...)
    preg_replace_callback('/<script\b([^>]*)>(.*?)<\/script>/is', ...)
    preg_replace_callback('/<script\b([^>]*)>(.*?)<\/script>/is', ...)

    For each matched script, HumHub registers JavaScript that creates a new <script> element, copies attributes, sets the script text, and appends Html::nonce() to the attributes. The attacker-controlled script is therefore recreated with a valid server-generated CSP nonce and executes in the victim's page.

Confirmed source-to-sink path

  1. Authenticated attacker submits Post[message] containing an oembed: rich-text link.

  2. The post is persisted by PostController::actionPost().

  3. Post::afterSave() triggers rich-text post-processing.

  4. OembedExtension records the malformed oEmbed extension ID because UrlOembed::hasOEmbedSupport() accepts it.

  5. When the post is rendered, OembedExtension::parseOembeds() passes the same value to UrlOembed::getOEmbed().

  6. The default YouTube regex matches the substring youtube.com in a malformed non-host URL.

  7. UrlOembed::confirmationContent() falls back to the raw URL as $urlPrefix.

  8. $urlPrefix is inserted into the confirmation prompt without output encoding.

  9. The resulting <script> is detected by the oEmbed script handler and recreated with Html::nonce().

  10. JavaScript executes in the authenticated victim's HumHub session.

Impact

An authenticated low-privileged Space member can store JavaScript in a post that executes when another authenticated user views the affected Space content.

Potential impact includes:

  • executing arbitrary JavaScript in the HumHub origin;

  • performing same-origin actions using the victim's authenticated browser session;

  • reading same-origin data exposed to the victim's session;

  • escalating practical impact when the victim is a global administrator or privileged Space user;

  • bypassing HumHub's nonce-based Content Security Policy for this injected script path.

PoC

Preconditions

  • HumHub Community Edition 1.18.4 or 1.18.4-pl1.

  • Default oEmbed providers are enabled.

  • The setting "Embedded content requires the user's consent to be loaded" remains enabled. This is the default behavior in OEmbedSettingsForm.

  • A regular Space member account with permission to create posts.

  • A victim account that can view the same Space.

Steps

  1. Log in as a regular Space member.

  2. Open a Space where the member can create posts.

  3. Create a normal post and intercept the submit request.

  4. Keep the original CSRF, container, and state parameters unchanged.

  5. Replace only Post[message] with:

[x](oembed:https:youtube.com<script>alert`Pwned`</script>)
[x](oembed:https:youtube.com<script>alert`Pwned`</script>)
[x](oembed:https:youtube.com<script>alert`Pwned`</script>)
[x](oembed:https:youtube.com<script>alert`Pwned`</script>)
  1. Forward the request and let HumHub create the post.

  2. Log in as another authenticated user who can access the same Space.

  3. Open the malicious post.

Expected result:

  • HumHub renders an oEmbed confirmation block for the malformed URL.

  • The injected <script> is reconstructed with a valid CSP nonce.

  • The browser executes the script and shows the Pwned alert.

Non-dialog validation payload:

[x](oembed:https:youtube.com<script>document.body.dataset.oembedXss='1'</script>)
[x](oembed:https:youtube.com<script>document.body.dataset.oembedXss='1'</script>)
[x](oembed:https:youtube.com<script>document.body.dataset.oembedXss='1'</script>)
[x](oembed:https:youtube.com<script>document.body.dataset.oembedXss='1'</script>)

Expected result:

document.body.dataset.oembedXss === "1"
document.body.dataset.oembedXss === "1"
document.body.dataset.oembedXss === "1"
document.body.dataset.oembedXss === "1"

Evidence of Exploitation

  • Video of exploitation:

  • Static evidence:

Our security policy

We have reserved the ID CVE-2026-18526 to refer to this issue from now on.

System Information

  • Humhub

  • Version 1.18.4

  • Operating System: Any

References

Mitigation

An updated version of HumHub is available on the vendor page.

Credits

The vulnerability was discovered by Miguel Gomez from Fluid Attacks' Offensive Team using the AI SAST Scanner.

Timeline

Vulnerability discovered

Vendor contacted

Vendor replied

Vendor confirmed

Vulnerability patched

Public disclosure

Does your application use this vulnerable software?

During our free trial, our tools assess your application, identify vulnerabilities, and provide recommendations for their remediation.

logo-fluidattacks-white-png

Las soluciones de Fluid Attacks permiten a las organizaciones identificar, priorizar y remediar vulnerabilidades en su software a lo largo del SDLC. Con el apoyo de la IA, herramientas automatizadas y pentesters, Fluid Attacks acelera la mitigación de la exposición al riesgo de las empresas y fortalece su postura de ciberseguridad.

Lee un resumen de Fluid Attacks

Suscríbete a nuestro boletín

Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.

logo-fluidattacks-white-png

Las soluciones de Fluid Attacks permiten a las organizaciones identificar, priorizar y remediar vulnerabilidades en su software a lo largo del SDLC. Con el apoyo de la IA, herramientas automatizadas y pentesters, Fluid Attacks acelera la mitigación de la exposición al riesgo de las empresas y fortalece su postura de ciberseguridad.

Suscríbete a nuestro boletín

Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.

Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.

logo-fluidattacks-white-png

Las soluciones de Fluid Attacks permiten a las organizaciones identificar, priorizar y remediar vulnerabilidades en su software a lo largo del SDLC. Con el apoyo de la IA, herramientas automatizadas y pentesters, Fluid Attacks acelera la mitigación de la exposición al riesgo de las empresas y fortalece su postura de ciberseguridad.

Suscríbete a nuestro boletín

Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.

Mantente al día sobre nuestros próximos eventos y los últimos blog posts, advisories y otros recursos interesantes.