hacktricks/pentesting-web/xss-cross-site-scripting/other-js-tricks.md
2023-08-03 19:12:22 +00:00

26 KiB
Raw Blame History

杂项JS技巧和相关信息

☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Javascript模糊测试

有效的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换行字符

In JavaScript, there are several characters that can be used to represent a new line. These characters can be useful in certain situations, such as when trying to obfuscate code or bypass input filters. Here are some of the valid JS new line characters:

  • \n: This is the most commonly used new line character in JavaScript. It represents a line feed.
  • \r: This character represents a carriage return.
  • \u2028: This is the Unicode character for line separator.
  • \u2029: This is the Unicode character for paragraph separator.

When using these characters, it's important to keep in mind that different platforms and browsers may interpret them differently. Therefore, it's recommended to test the code on the target platform to ensure compatibility.

有效的JS换行字符

在JavaScript中有几个字符可以用来表示换行。这些字符在某些情况下非常有用比如在尝试混淆代码或绕过输入过滤器时。以下是一些有效的JS换行字符

  • \n这是JavaScript中最常用的换行字符。它表示换行。
  • \r:这个字符表示回车。
  • \u2028这是行分隔符的Unicode字符。
  • \u2029这是段落分隔符的Unicode字符。

在使用这些字符时,需要注意不同的平台和浏览器可能会以不同的方式解释它们。因此,建议在目标平台上测试代码,以确保兼容性。

//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 necessary to use valid JavaScript spaces in a function call. These spaces can help obfuscate the payload and make it more difficult for security measures to detect.

在某些情况下为了绕过过滤器或规避检测可能需要在函数调用中使用有效的JavaScript空格。这些空格可以帮助混淆有效载荷使其更难被安全措施检测到。

Here are some examples of valid JavaScript spaces that can be used:

以下是一些可以使用的有效JavaScript空格的示例

  • No-Break Space: This is a non-breaking space character that can be used instead of a regular space. It can be represented by the Unicode character \u00A0.

    • 不间断空格这是一个非间断空格字符可以用来替代普通空格。它可以用Unicode字符\u00A0表示。
  • Zero-Width Space: This is a non-printable character that has no width when displayed. It can be represented by the Unicode character \u200B.

    • 零宽度空格这是一个不可打印的字符在显示时没有宽度。它可以用Unicode字符\u200B表示。
  • Zero-Width Non-Joiner: This is another non-printable character that has no width when displayed and is used to prevent the joining of two adjacent characters. It can be represented by the Unicode character \u200C.

    • 零宽度非连接符这是另一个不可打印的字符在显示时没有宽度并且用于防止两个相邻字符的连接。它可以用Unicode字符\u200C表示。

By using these valid JavaScript spaces in a function call, you can add an extra layer of obfuscation to your payload and increase the chances of bypassing security measures.

通过在函数调用中使用这些有效的JavaScript空格您可以为有效载荷添加额外的混淆层并增加绕过安全措施的机会。

// 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 in various contexts:

以下字符可用于在不同的上下文中生成字符串:

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

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

  • Whitespace characters (space, tab, newline)

  • Unicode characters

  • 字母数字字符a-zA-Z0-9

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

  • 空白字符(空格,制表符,换行符)

  • Unicode字符

These characters can be combined and manipulated to create strings that serve various purposes, such as injecting malicious code or bypassing input validation.

可以组合和操作这些字符,以创建用于注入恶意代码或绕过输入验证的字符串。

// 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代理对暴力破解

这种技术对于XSS来说并不是很有用但是它可以用于绕过WAF的保护。这段Python代码接收两个字节作为输入并搜索具有第一个字节作为高代理对的最后一个字节和最后一个字节作为低代理对的最后一个字节的代理对。

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模糊测试

URL模糊测试是一种常用的Web应用程序渗透测试技术用于发现可能存在的漏洞。通过对URL进行模糊测试可以尝试各种不同的输入和参数组合以寻找可能导致安全漏洞的情况。

URL模糊测试的目标是发现应用程序中的潜在漏洞例如路径遍历、文件包含、SQL注入等。通过构造特定的URL请求可以触发应用程序中的漏洞并获取敏感信息或执行恶意操作。

URL模糊测试可以使用各种工具和技术来实现。其中一种常见的方法是使用字典文件包含各种可能的URL路径和参数组合。通过将这些字典文件与目标URL进行组合可以生成大量的URL请求以便进行测试。

URL模糊测试的关键是选择合适的字典文件和参数组合。根据应用程序的特点和已知的漏洞类型可以选择不同的字典文件和参数组合来进行测试。同时还可以使用自动化工具来加快测试过程并提供更全面的测试覆盖。

URL模糊测试是一项重要的渗透测试技术可以帮助发现Web应用程序中的安全漏洞。通过对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

HTML模糊测试

HTML模糊测试是一种测试Web应用程序的技术旨在发现和利用HTML中的漏洞。通过向输入字段注入恶意或异常数据可以揭示潜在的安全问题。以下是一些常见的HTML模糊测试技术

  • 标签闭合测试在输入字段中注入不完整的HTML标签以测试应用程序是否正确处理标签闭合。例如<script>标签注入为<script><script></script>

  • 属性注入测试在HTML标签的属性中注入恶意代码以测试应用程序是否正确过滤和转义用户输入。例如<img>标签的src属性注入为" onerror="alert('XSS')" />

  • 特殊字符测试:在输入字段中注入特殊字符,如尖括号、引号和斜杠,以测试应用程序是否正确处理和转义这些字符。例如,将<注入为&lt;

  • 编码绕过测试:尝试绕过应用程序对特殊字符的编码和转义机制,以执行恶意操作。例如,使用十六进制编码绕过<字符的过滤。

  • 标签嵌套测试在输入字段中嵌套HTML标签以测试应用程序是否正确处理和解析嵌套标签。例如<b>标签嵌套在<i>标签内。

  • 事件处理程序测试在HTML标签的事件处理程序中注入恶意代码以测试应用程序是否正确过滤和处理用户输入。例如<img>标签的onerror事件注入为alert('XSS')

  • URL注入测试在URL参数中注入恶意代码以测试应用程序是否正确处理和解析URL。例如<script>标签注入为URL参数的一部分。

HTML模糊测试是一种有用的技术可以帮助发现和修复Web应用程序中的安全漏洞特别是跨站脚本攻击XSS漏洞。

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

"--" 赋值

递减运算符--也是一种赋值操作。该运算符接受一个值,然后将其递减一。如果该值不是一个数字,它将被设置为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"])

箭头函数

箭头函数允许您更轻松地在一行中生成函数(如果您理解它们的话)

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

以下是从另一个函数中提取函数代码(包括注释)的一些随机方法:

1. 使用`toString()`方法
```javascript
var functionCode = anotherFunction.toString();
  1. 使用正则表达式:
var functionCode = anotherFunction.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1];
  1. 使用Function.prototype.toString()方法:
var functionCode = Function.prototype.toString.call(anotherFunction);
  1. 使用new Function()构造函数:
var functionCode = new Function('', anotherFunction.toString());

请注意,这些方法可能会受到浏览器的限制或安全策略的影响。

(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 %}

在访问值时设置断点

// 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 is necessary to automate the process of accessing a web page with a payload. This can be achieved using various methods, such as using a headless browser or a browser automation tool.

Headless Browsers

Headless browsers are web browsers without a graphical user interface (GUI). They can be controlled programmatically to access web pages and execute JavaScript code. Some popular headless browsers include Puppeteer, PhantomJS, and Selenium WebDriver with headless mode enabled.

To automate browser access using a headless browser, you can write a script that navigates to the target web page and injects the XSS payload. The script can then capture any resulting behavior or responses from the web page.

Browser Automation Tools

Browser automation tools, such as Selenium WebDriver, allow you to control web browsers programmatically. These tools provide APIs that enable you to interact with web pages, fill out forms, click buttons, and perform other actions.

To automate browser access using a browser automation tool, you can write a script that uses the tool's API to navigate to the target web page and inject the XSS payload. The script can then capture any resulting behavior or responses from the web page.

Automating browser access can save time and effort when testing for XSS vulnerabilities, as it eliminates the need for manual interaction with the web page. However, it is important to use these techniques responsibly and with proper authorization, as unauthorized access to websites can be illegal and unethical.

//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 云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥