Misc JS Tricks & Relevant Info

Tip

AWS ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°:HackTricks Training AWS Red Team Expert (ARTE)
GCP ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training GCP Red Team Expert (GRTE) Azure ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks μ§€μ›ν•˜κΈ°

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 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 곡백 ν•¨μˆ˜ 호좜

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

λ¬Έμžμ—΄ 생성을 μœ„ν•œ μœ νš¨ν•œ 문자

// 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 보호λ₯Ό μš°νšŒν•˜λŠ” 데 μœ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이 파이썬 μ½”λ“œλŠ” 2λ°”μ΄νŠΈλ₯Ό μž…λ ₯으둜 받아듀이고, 첫 번째 λ°”μ΄νŠΈκ°€ ν•˜μ΄ μ„œλŸ¬κ²Œμ΄νŠΈ 쌍의 λ§ˆμ§€λ§‰ λ°”μ΄νŠΈμ΄κ³  λ§ˆμ§€λ§‰ λ°”μ΄νŠΈκ°€ 둜우 μ„œλŸ¬κ²Œμ΄νŠΈ 쌍의 λ§ˆμ§€λ§‰ λ°”μ΄νŠΈμΈ μ„œλŸ¬κ²Œμ΄νŠΈ μŒμ„ κ²€μƒ‰ν•©λ‹ˆλ‹€.

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 퍼징

// 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.wiki`
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 퍼징

// 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λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ 객체의 속성을 λΆ„μ„ν•˜λŠ” 데 도움을 μ€λ‹ˆλ‹€. ν™•μΈν•˜μ„Έμš”: 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"])

ν™”μ‚΄ν‘œ ν•¨μˆ˜

ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” ν•¨μˆ˜λ₯Ό ν•œ μ€„λ‘œ 더 μ‰½κ²Œ 생성할 수 있게 ν•΄μ€λ‹ˆλ‹€(μ΄ν•΄ν•˜κ³  μžˆλ‹€λ©΄).

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

Bind ν•¨μˆ˜

bind ν•¨μˆ˜λŠ” 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")

Note

**bind**λ₯Ό μ‚¬μš©ν•˜λ©΄ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•  λ•Œ μ‚¬μš©λ  this 객체λ₯Ό μ‘°μž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ν•¨μˆ˜ μ½”λ“œ 유좜

ν•¨μˆ˜μ˜ 객체에 μ ‘κ·Όν•  수 μžˆλ‹€λ©΄ ν•΄λ‹Ή ν•¨μˆ˜μ˜ μ½”λ“œλ₯Ό κ°€μ Έμ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

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

Sandbox Escape - Recovering window object

Window κ°μ²΄λŠ” alert λ˜λŠ” evalκ³Ό 같은 μ „μ—­μ μœΌλ‘œ μ •μ˜λœ ν•¨μˆ˜μ— μ ‘κ·Όν•  수 있게 ν•΄μ€λ‹ˆλ‹€.

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

κ°’ μ ‘κ·Ό μ‹œ 쀑단점

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

μžλ™ λΈŒλΌμš°μ € μ•‘μ„ΈμŠ€λ₯Ό ν†΅ν•œ νŽ˜μ΄λ‘œλ“œ ν…ŒμŠ€νŠΈ

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

Tip

AWS ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°:HackTricks Training AWS Red Team Expert (ARTE)
GCP ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training GCP Red Team Expert (GRTE) Azure ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks μ§€μ›ν•˜κΈ°