hacktricks/pentesting-web/xss-cross-site-scripting/other-js-tricks.md
2024-02-10 13:03:23 +00:00

23 KiB

Trucchi JS vari e informazioni rilevanti

Impara l'hacking di AWS da zero a esperto con htARTE (HackTricks AWS Red Team Expert)!

Fuzzing di Javascript

Caratteri di commento JS validi

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

Caratteri di nuova riga JS validi

In JavaScript, ci sono diversi caratteri che possono essere utilizzati per rappresentare una nuova riga. Questi caratteri possono essere utilizzati in vari contesti, come all'interno di stringhe o commenti, per creare una nuova riga di codice.

Ecco alcuni dei caratteri di nuova riga JS validi:

  • \n: rappresenta una nuova riga.
  • \r: rappresenta un ritorno a capo.
  • \u2028: rappresenta un separatore di linea Unicode.
  • \u2029: rappresenta un separatore di paragrafo Unicode.

È importante notare che questi caratteri possono essere utilizzati in combinazione con altri caratteri per creare sequenze di escape o per ottenere determinati effetti nel codice JavaScript.

Ad esempio, \n può essere utilizzato per creare una nuova riga all'interno di una stringa, come nel seguente esempio:

var message = "Ciao\nmondo!";
console.log(message);

Questo codice produrrà l'output:

Ciao
mondo!

In questo esempio, la sequenza di escape \n viene interpretata come una nuova riga, consentendo di separare le parole "Ciao" e "mondo!" su due righe separate.

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

Spazi JS validi nella chiamata di una funzione

Sometimes, when trying to bypass filters or WAFs that block certain characters or keywords, it can be useful to use valid JavaScript spaces in function calls. These spaces are not recognized as special characters by the JavaScript interpreter, allowing us to execute our desired code.

In JavaScript, there are several types of spaces that can be used:

  • Regular space: This is the standard space character that we use for separating words. It can be represented by the ASCII code 32 or the escape sequence \x20.

  • No-break space: This is a non-breaking space that prevents line breaks between words. It can be represented by the ASCII code 160 or the escape sequence \xa0.

  • Zero-width space: This is an invisible space that does not take up any visual space. It can be represented by the escape sequence \u200b.

By using these valid JavaScript spaces in function calls, we can obfuscate our code and make it more difficult for filters or WAFs to detect and block our malicious payloads.

Here is an example of how we can use valid JavaScript spaces in a function call:

alert\u200b('Hello\u200bWorld');

In this example, we are using the zero-width space \u200b to separate the function name alert and the string 'HelloWorld'. This code will execute the alert function and display the message "Hello World" in the browser.

Remember to always test your payloads and ensure that they are working as expected before using them in real-world scenarios.

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

Caratteri validi per generare stringhe

<p>Here is a list of valid characters that can be used to generate strings:</p>
<p>Ecco una lista di caratteri validi che possono essere utilizzati per generare stringhe:</p>
// 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

Questa tecnica non sarà molto utile per XSS, ma potrebbe essere utile per eludere le protezioni WAF. Questo codice Python riceve in input 2 byte e cerca una coppia di surrogati che ha il primo byte come gli ultimi byte della coppia di surrogati alti e l'ultimo byte come l'ultimo byte della coppia di surrogati bassi.

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

Ulteriori informazioni:

Fuzzing del protocollo javascript{}:

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

URL Fuzzing

URL Fuzzing è una tecnica utilizzata per individuare vulnerabilità nelle applicazioni web. Consiste nell'inserire dati di input non validi o imprevisti all'interno degli URL per testare la risposta dell'applicazione. Questo può includere l'inserimento di caratteri speciali, sequenze di escape, parametri aggiuntivi o modifiche nella struttura dell'URL. L'obiettivo è identificare eventuali errori o comportamenti anomali che potrebbero essere sfruttati per eseguire attacchi come l'iniezione di codice o il bypass di controlli di sicurezza.

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

Il fuzzing HTML è una tecnica utilizzata per individuare vulnerabilità di cross-site scripting (XSS) in un'applicazione web. Consiste nell'inserire dati di input non validi o imprevisti nei campi di input dell'applicazione per verificare se l'applicazione è in grado di gestirli correttamente.

Il fuzzing HTML può essere eseguito in diversi modi, ad esempio:

  • Inserendo caratteri speciali come "<", ">", "&" nei campi di input per verificare se l'applicazione esegue una corretta sanitizzazione dei dati.
  • Inserendo tag HTML o attributi non validi nei campi di input per verificare se l'applicazione li interpreta correttamente.
  • Inserendo script JavaScript nei campi di input per verificare se l'applicazione esegue una corretta sanitizzazione dei dati e previene l'esecuzione di codice dannoso.

Durante il fuzzing HTML, è importante monitorare le risposte dell'applicazione per individuare eventuali errori o comportamenti anomali. Inoltre, è consigliabile utilizzare strumenti di fuzzing automatizzati per accelerare il processo di individuazione delle vulnerabilità.

Una volta individuata una potenziale vulnerabilità di XSS, è necessario testarla ulteriormente per confermare la sua presenza e determinare il suo impatto. Ciò può essere fatto inserendo script JavaScript di prova per verificare se è possibile eseguire codice arbitrario nell'applicazione.

In conclusione, il fuzzing HTML è una tecnica efficace per individuare vulnerabilità di XSS in un'applicazione web. È importante eseguirlo in modo accurato e sistematico per garantire la sicurezza dell'applicazione.

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

Analisi degli attributi

Lo strumento Hackability inspector di Portswigger aiuta ad analizzare gli attributi di un oggetto javascript. Controlla: https://portswigger-labs.net/hackability/inspector/?input=x.contentWindow&html=%3Ciframe%20src=//subdomain1.portswigger-labs.net%20id=x%3E

File .map js

Assegnazione "--"

L'operatore di decremento -- è anche un'assegnazione. Questo operatore prende un valore e poi lo decrementa di uno. Se quel valore non è un numero, verrà impostato su NaN. Questo può essere utilizzato per rimuovere il contenuto delle variabili dall'ambiente.

Trucchi delle funzioni

.call e .apply

Il metodo .call di una funzione viene utilizzato per eseguire la funzione.
Il primo argomento che si aspetta di default è il valore di this e se niente viene fornito, window sarà quel valore (a meno che non venga utilizzato strict mode).

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

Funzioni freccia

Le funzioni freccia ti permettono di generare funzioni in una singola riga più facilmente (se le comprendi)

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

Quindi, la maggior parte delle funzioni precedenti sono in realtà inutili perché non le stiamo salvando da nessuna parte per poterle richiamare. Ad esempio, creiamo la funzione plusone:

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

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

Funzione bind

La funzione bind permette di creare una copia di una funzione modificando l'oggetto this e i parametri forniti.

//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" %} Nota che utilizzando bind puoi manipolare l'oggetto this che verrà utilizzato quando si chiama la funzione. {% endhint %}

Fuga di codice della funzione

Se puoi accedere all'oggetto di una funzione, puoi ottenere il codice di quella funzione.

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

Nei casi in cui la funzione non ha un nome, è comunque possibile stampare il codice della funzione da all'interno:

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

Alcuni modi casuali per estrarre il codice di una funzione (anche i commenti) da un'altra funzione:

(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 */ })()

Fuga dalla sandbox - Recupero dell'oggetto window

L'oggetto Window consente di accedere alle funzioni definite globalmente come 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 di interruzione sull'accesso al valore

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

Accesso automatico del browser per testare i payload

Sometimes, when testing for Cross-Site Scripting (XSS) vulnerabilities, it can be useful to automate the process of accessing the target website with different payloads. This can help in identifying potential XSS vulnerabilities and their impact.

In order to achieve this, you can use tools like Selenium WebDriver or Puppeteer. These tools allow you to control a web browser programmatically, enabling you to interact with web pages, fill out forms, and execute JavaScript code.

Here is an example of how you can use Selenium WebDriver with Python to automatically access a website and test XSS payloads:

from selenium import webdriver

# Create a new instance of the Firefox driver
driver = webdriver.Firefox()

# Navigate to the target website
driver.get("https://www.example.com")

# Find an input field on the page
input_field = driver.find_element_by_id("input_field")

# Clear the input field
input_field.clear()

# Enter an XSS payload into the input field
input_field.send_keys("<script>alert('XSS')</script>")

# Submit the form
input_field.submit()

# Close the browser
driver.quit()

This code snippet demonstrates how you can use Selenium WebDriver to automate the process of accessing a website, entering an XSS payload into an input field, and submitting the form. You can modify the code to test different payloads and explore the website for potential vulnerabilities.

By automating the browser access, you can save time and effort in testing for XSS vulnerabilities, allowing you to focus on analyzing the results and identifying potential security issues.

//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();
})();
Impara l'hacking di AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!