# jQuery selector injection from user input

User input concatenated into jQuery selectors or callbacks without escaping; check for double-parsed values

_Area: Web injection and code execution. Seen in 3 disclosed reports; 2 confirmed bounties: $300 and $1,500; latest 2026-09-18. Updated 2026-10-23._

## How it works
A value the application treats as data (a slug, a callback name, a hash fragment) is concatenated into a jQuery selector string or used as a property name on `window.parent`. jQuery parses that string as selector syntax, so injected quotes, backslashes or combinators change which element is matched or which handler fires. The broken assumption is that the value is inert text; in fact it is re-parsed by a second parser (the selector engine, or the JS property lookup) that does not share the server's canonicalization.

## Where it shows up
- `$("..."+input+"...")` patterns, especially attribute selectors like `a[rel^='...']`
- Values canonicalized by a server API but kept raw in DOM/JS (slug, theme name) [E1](https://bugbountylibrary.com/r/blog%3Apwn-ai-blog-click2shell.md)
- Callback params reflected into `parent.<callback>(...)` or `eval` of XHR bodies [E2](https://bugbountylibrary.com/r/h1%3A142135.md)
- Sanitizers that escape `=` but not `\` before a quoted selector [E3](https://bugbountylibrary.com/r/h1%3A396493.md)
- HTML attribute injection into canonical/link elements feeding a JS sink [E3](https://bugbountylibrary.com/r/h1%3A396493.md)

## How to review
Trace each user-controlled value to every sink. The invariant: any value placed inside a selector string or used as a property name must be escaped for that parser, or replaced by a lookup (e.g. `document.getElementById`, data attributes, allowlist map). Check whether the same value passes through two parsers with different normalization; if the server normalizes but the client keeps raw punctuation, the client sink is unsafe [E1](https://bugbountylibrary.com/r/blog%3Apwn-ai-blog-click2shell.md). For callbacks, confirm the name is validated against a fixed allowlist, not merely reflected [E2](https://bugbountylibrary.com/r/h1%3A142135.md). For sanitizers, verify they escape the full metacharacter set including backslash and quotes [E3](https://bugbountylibrary.com/r/h1%3A396493.md).

## Variants seen
- Theme slug normalized by Themes API, raw in jQuery selector, auto-clicking Install [E1](https://bugbountylibrary.com/r/blog%3Apwn-ai-blog-click2shell.md)
- Callback name used as `window.parent` property plus `eval` of response [E2](https://bugbountylibrary.com/r/h1%3A142135.md)
- Backslash gap in prettyPhoto `hashRel` sanitizer enabling arbitrary selectors, chained with attribute injection [E3](https://bugbountylibrary.com/r/h1%3A396493.md)

## Why fixes fail
E3 shows a sanitizer that escaped some metacharacters but missed backslash, leaving the sink exploitable; the canonical-link attribute injection was also left unfixed, so the chain survived.

## Impact and severity
Led to preauth RCE via theme install chain [E1](https://bugbountylibrary.com/r/blog%3Apwn-ai-blog-click2shell.md), XSS on a major site [E2](https://bugbountylibrary.com/r/h1%3A142135.md), and reflected DOM XSS [E3](https://bugbountylibrary.com/r/h1%3A396493.md). Severity rises when the sink auto-triggers actions (clicks, installs) or chains with a second injection point.

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Apwn-ai-blog-click2shell.md
- E2: https://bugbountylibrary.com/r/h1%3A142135.md
- E3: https://bugbountylibrary.com/r/h1%3A396493.md
