Skip to content

Commit 879ef27

Browse files
authored
Merge pull request #1825 from HackTricks-wiki/research_update_src_pentesting-web_xss-cross-site-scripting_js-hoisting_20260129_155244
Research Update Enhanced src/pentesting-web/xss-cross-site-s...
2 parents bbc4876 + 5a2b89c commit 879ef27

1 file changed

Lines changed: 24 additions & 0 deletions

File tree

src/pentesting-web/xss-cross-site-scripting/js-hoisting.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,20 @@ let config;` -
133133
trigger()
134134
```
135135

136+
### Hoisting to bypass exception handling
137+
138+
When the sink is wrapped in a `try { x.y(...) } catch { ... }`, **ReferenceError** will stop execution before your payload runs. You can pre-declare the missing identifier so the call survives and your injected expression executes first:
139+
140+
```javascript
141+
// Original sink (x and y are undefined, but you control INJECT)
142+
x.y(1,INJECT)
143+
144+
// Payload (ch4n3 2023) – hoist x so the call is parsed; use the first argument position for code exec
145+
prompt()) ; function x(){} //
146+
```
147+
148+
`function x(){}` is hoisted before evaluation, so the parser no longer throws on `x.y(...)`; `prompt()` executes before `y` is resolved, then a `TypeError` is thrown after your code has run.
149+
136150
### Preempt later declarations by locking a name with const
137151

138152
If you can execute before a top-level `function foo(){...}` is parsed, declaring a lexical binding with the same name (e.g., `const foo = ...`) will prevent the later function declaration from rebinding that identifier. This can be abused in RXSS to hijack critical handlers defined later in the page:
@@ -153,11 +167,21 @@ Notes
153167
- This relies on execution order and global (top-level) scope.
154168
- If your payload is executed inside `eval()`, remember that `const/let` inside `eval` are block-scoped and won’t create global bindings. Inject a new `<script>` element with the code to establish a true global `const`.
155169

170+
### Dynamic import() with user-controlled specifiers
171+
172+
Server-side rendered apps sometimes forward user input into `import()` to lazy-load components. If a loader such as `import-in-the-middle` is present, wrapper modules are generated from the specifier. Hoisted import evaluation fetches and executes the attacker-controlled module before subsequent lines run, enabling RCE in SSR contexts (see CVE-2023-38704).
173+
174+
### Tooling
175+
176+
Modern scanners started to add explicit hoisting payloads. **KNOXSS v3.6.5** lists "JS Injection with Single Quotes Fixing ReferenceError - Object Hoisting" and "Hoisting Override" test cases; running it against RXSS contexts that throw `ReferenceError`/`TypeError` quickly surfaces hoist-based gadget candidates.
177+
156178
## References
157179

158180
- [https://jlajara.gitlab.io/Javascript_Hoisting_in_XSS_Scenarios](https://jlajara.gitlab.io/Javascript_Hoisting_in_XSS_Scenarios)
159181
- [https://developer.mozilla.org/en-US/docs/Glossary/Hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)
160182
- [https://joaxcar.com/blog/2023/12/13/having-some-fun-with-javascript-hoisting/](https://joaxcar.com/blog/2023/12/13/having-some-fun-with-javascript-hoisting/)
161183
- [From "Low-Impact" RXSS to Credential Stealer: A JS-in-JS Walkthrough](https://r3verii.github.io/bugbounty/2025/08/25/rxss-credential-stealer.html)
184+
- [XSS Exception Bypass using Hoisting (ch4n3, 2023)](https://new-blog.ch4n3.kr/xss-exception-bypass-using-hoisting/)
185+
- [KNOXSS coverage – hoisting override cases](https://knoxss.pro/?page_id=766)
162186

163187
{{#include ../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)