Skip to content

[critical] fix: [website] stop injecting attribute values into inline onclick strings - #879

Open
elhoim wants to merge 1 commit into
MISP:mainfrom
elhoim:fix/068-mispparser-onclick-xss
Open

[critical] fix: [website] stop injecting attribute values into inline onclick strings#879
elhoim wants to merge 1 commit into
MISP:mainfrom
elhoim:fix/068-mispparser-onclick-xss

Conversation

@elhoim

@elhoim elhoim commented Aug 31, 2026

Copy link
Copy Markdown
Member

BLUF — mispParser.js spliced attribute values into onclick, a stored XSS; now a bound handler.

  • Problem — In website/app/static/js/mispParser.js, both parseMispObject and parseMispAttr build their query-as-same buttons by interpolating a MISP attribute value straight into an onclick attribute string, so any value containing a quote or backtick executes as JavaScript in the analyst's browser.
  • Fix — Bind a click handler with .on("click", ...) that receives the value through a closure, so it is passed as a JS value and never parsed as attribute-embedded source.
  • Effect — An attacker who plants a crafted value in a shared event or feed can no longer achieve stored XSS against every analyst who views it; the button still calls the same function with the same argument.

The defect

website/app/static/js/mispParser.js built the "query as same" button by string-interpolating an attribute value directly into an onclick attribute:

// parseMispObject/generate, line ~17
$query_same = $("<button>").attr({"onclick": `_${functionToCall.name}('${v.value}')`, ...})

// parseMispAttr, line ~84
$query_same = $("<button>").attr({"onclick": `_${query_as_same.name}('${misp_attr}')`, ...})

v.value / misp_attr come from MISP attribute data being displayed, and are spliced unescaped into an executable JS attribute string. Any attribute value containing a single quote, backtick, or other JS-breaking characters is executed verbatim as JavaScript in the analyst's browser when the element is rendered.

Impact

An attacker who can get a malicious MISP attribute value into a shared/queried event (or feed) can execute arbitrary JavaScript in the browser of any MISP analyst who views that attribute through this query UI — a stored XSS vector, not merely self-XSS.

The fix

Both sites now build the button without ever passing the value through an interpolated attribute string. Instead, a .on("click", function(){ window[_${name}](value) }) handler reads the value directly from the closure/parameter, so it is used as a JS value, never parsed as attribute-embedded source text — closing the injection vector regardless of what characters the value contains.

This is a pure defect fix with no behavior change: the button still calls the same window._<name>(value) function with the same value on click.

Note: the finding that prompted this also proposed an ownership check on the Flask /query/<sid> route (website/app/home.py). That file is intentionally untouched here — it's outside the file this fix targets, and adding real ownership checking would require inventing a user/session association model that doesn't currently exist in HomeModel/SessionModel, which is a larger design change out of scope for this minimal fix.

Found during a review of the repository; other findings are being submitted as separate PRs.

Verification

The website/ app has no automated test coverage in this repository. Verification was manual diff review of both call sites plus confirming the closure variable (v, a forEach callback parameter, and misp_attr, a function parameter) is correctly scoped per-iteration/per-call so no stale-value regression is introduced. The misp-modules module test suite was also run and is unaffected by this change: 161 passed, 4 skipped, 5 subtests passed in 20.32s.

🤖 Generated with Claude Code

…rings

parseMispObject and parseMispAttr built the "query as same" button by
splicing v.value / misp_attr straight into a backtick-quoted onclick
attribute string. Any attacker-controlled attribute value containing a
quote or backtick breaks out of the string and runs as arbitrary
JavaScript in the analyst's session as soon as the object or attribute
is rendered, and this view is reachable by anyone who has (or guesses)
a query session id, not just the value's own submitter, so it is not
merely self-XSS. The fix drops the onclick string and instead attaches
the same callback via jQuery's .on("click", ...), reading the value
from the existing closure variable rather than re-serializing it into
markup, so no value ever passes through string interpolation into an
executable attribute.

Verified with the full pytest suite against a live modules server on
port 6768: 161 passed, 4 skipped, 5 subtests passed, matching the
documented baseline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018dfYpyaSZd1nxSRLr8suj8
@elhoim elhoim changed the title fix: [website] stop injecting attribute values into inline onclick strings [critical] fix: [website] stop injecting attribute values into inline onclick strings Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant