Skip to main content

Boteraser | Website and Server Security Solutions

🛡️ CVE-2026-42841 — grav

🟡 CVSS 4.8 — Medium ✅ No Known Exploit CWE-79 NVD
4.8
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

Grav CMS vulnerable to stored XSS via Markdown media attribute() action

Summary

An authenticated user with page editing permissions can inject an executable JavaScript event-handler attribute into rendered image HTML through Grav's Markdown media action syntax.

The issue is caused by Markdown image query parameters being converted into callable media actions. The public attribute() media method can be reached this way, allowing an editor to set an arbitrary HTML attribute name and value on the generated image element.

For example, this Markdown:

```markdown

![Quarterly market overview](market-overview.gif?attribute=onload,alert(document.domain))

```

is rendered as an image tag containing an executable onload handler:

```html

<img onload="alert(document.domain)" alt="Quarterly market overview" src="/user/pages/03.campaigns/market-overview.gif?...">

```

This results in stored XSS when another user views the affected page. In a multi-user Grav installation, a lower-privileged page editor could use this to target administrators or reviewers who preview or view editor-controlled content.

Tested versions:

  • Grav CMS: 1.7.49.5
  • Admin Plugin: 1.10.49.1

Suggested classification:

  • CWE-79: Improper Neutralization of Input During Web Page Generation
  • Stored Cross-Site Scripting
  • Suggested CVSS v4.0 score if page editing is considered high privilege: 6.9 Medium
  • Suggested CVSS v4.0 vector: CVSS:4.0/AV:N/AC:L/AT:P/PR:H/UI:P/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N
  • Suggested CVSS v3.1 score if page editing is considered high privilege: 6.9 Medium
  • Suggested CVSS v3.1 vector: CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:L/A:N

Details

The issue appears to come from this source-to-sink flow:

1. ParsedownGravTrait::inlineImage() processes Markdown images.

2. Excerpts::processImageExcerpt() resolves the referenced media object.

3. Excerpts::processMediaActions() parses the image URL query string into media actions.

4. call_user_func_array() invokes the requested action method on the media object.

5. MediaObjectTrait::attribute() stores the attacker-controlled attribute name and value.

6. The media object returns a Parsedown element containing the injected attribute.

7. Parsedown renders the attribute name into the final HTML.

Relevant code paths:

```text

system/src/Grav/Common/Markdown/ParsedownGravTrait.php

system/src/Grav/Common/Page/Markdown/Excerpts.php

system/src/Grav/Common/Media/Traits/MediaObjectTrait.php

system/src/Grav/Common/Page/Medium/StaticImageMedium.php

system/src/Grav/Common/Page/Medium/ImageMedium.php

vendor/erusev/parsedown/Parsedown.php

```

In system/src/Grav/Common/Markdown/ParsedownGravTrait.php, Markdown image excerpts are passed into Grav-specific media handling:

```php

if (isset($excerpt['element']['attributes']['src'])) {

$excerpt = $this->excerpts->processImageExcerpt($excerpt);

}

```

In system/src/Grav/Common/Page/Markdown/Excerpts.php, query string parameters are converted into media action calls. The query parameter name becomes the method name:

```php

$carry[] = ['method' => $parts[0], 'params' => $value];

```

The requested method is later invoked dynamically:

```php

$medium = call_user_func_array([$medium, $action['method']], $args);

```

For the payload:

```text

attribute=onload,alert(document.domain)

```

the method is attribute, and the arguments are onload and alert(document.domain).

In system/src/Grav/Common/Media/Traits/MediaObjectTrait.php, attribute() stores the caller-controlled attribute name directly:

```php

public function attribute($attribute = null, $value = '')

{

if (!empty($attribute)) {

$this->attributes[$attribute] = $value;

}

return $this;

}

```

The image media classes then return the collected attributes as attributes for an img element.

In system/src/Grav/Common/Page/Medium/StaticImageMedium.php:

```php

return ['name' => 'img', 'attributes' => $attributes];

```

The non-static image path in system/src/Grav/Common/Page/Medium/ImageMedium.php also returns image attributes in the same way.

Finally, in vendor/erusev/parsedown/Parsedown.php, the attribute value is escaped, but the attribute name is rendered as-is:

```php

$markup .= ' '.$name.'="'.self::escape($value).'"';

```

As a result, the attacker-controlled attribute name onload is emitted into the final HTML and executes as a browser event handler.

The Admin Plugin's save-time XSS detection does not appear to block this because the stored content is Markdown media syntax, not raw HTML:

```markdown

![Quarterly market overview](market-overview.gif?attribute=onload,alert(document.domain))

```

The dangerous HTML is generated later during Markdown/media rendering.

PoC

I reproduced this on a standard Grav CMS installation with the Admin Plugin enabled.

Configuration and prerequisites:

  • Grav CMS 1.7.49.5
  • Admin Plugin 1.10.49.1
  • Markdown processing enabled for pages
  • A user account with permission to create or edit pages

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs administrative privileges on the target. A user must be tricked into taking some action. The scope is changed, meaning a successful attack can affect components beyond the vulnerable one. Rated impact: confidentiality low, integrity low, availability none.

CVSS metrics in full

The score comes from this vector: CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N

  • Attack vector: Network — reachable from anywhere that can route to the service.
  • Attack complexity: Low — the attack works reliably, with no preparation.
  • Privileges required: High — administrative rights are needed first.
  • User interaction: Required — someone has to click, open or visit something.
  • Scope: Changed — a successful attack reaches components beyond the vulnerable one.
  • Confidentiality impact: Low — limited, and the attacker does not choose what is affected.
  • Integrity impact: Low — limited, and the attacker does not choose what is affected.
  • Availability impact: None.

Weakness class

CVE-2026-42841 is classified as CWE-79: Cross-site Scripting (XSS). User-supplied data is written into a page without escaping, so attacker script runs in the browser of anyone who views it.

Affected software

CVE-2026-42841 is recorded against 2 packages.

  • getgrav/grav (fixed in 2.0.0-beta.2)
  • grav

Timeline and source

Published on 5 May 2026 and last revised on 13 May 2026. No public exploit is currently recorded for this entry. A vendor advisory or fix has been published. Record sourced from NVD.

References

github.com (Web)
nvd.nist.gov (Advisory)
github.com (Web)
github.com (Package)

Other advisories for this package

getgrav/grav has other advisories on record. If you are patching this one, these are worth checking on the same host:

Same weakness in other software

These advisories are the same class of weakness (CWE-79: Cross-site Scripting (XSS)) in other software:

Details

Severity Medium
CVSS Score 4.8
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:L/I:L/A:N
CWE CWE-79
Public Exploit ✅ No
Source NVD
Published 2026-05-05
Updated 2026-08-20
Modified 2026-05-13

Affected Packages

Software From version Fixed in
getgrav/grav 2.0.0-beta.2
grav

Similar Threats

Vulnerability Monitoring

Track new vulnerabilities in grav

CVE-2026-42841 is rated CVSS 4.8 Medium. BotEraser monitors your WordPress installation and notifies you when software you use appears in our vulnerability database.

Set Up Free Alerts →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.

Browse related advisories

All advisoriesCVECVE 2026