daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

other-js-tricks.md (17695B)


      1 ---
      2 title: "Misc JS Tricks & Relevant Info"
      3 section: "Web Pentesting"
      4 sectionSlug: "pentesting-web"
      5 sourcePath: "src/pentesting-web/xss-cross-site-scripting/other-js-tricks.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/xss-cross-site-scripting/other-js-tricks.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Misc JS Tricks & Relevant Info
     14 
     15 ## Javascript Fuzzing
     16 
     17 ### Valid JS Comment Chars
     18 
     19 ```javascript
     20 //This is a 1 line comment
     21 /* This is a multiline comment*/
     22 #!This is a one-line comment, but "#!" must be at the beginning of the line
     23 -->This is a one-line comment, but "-->" must be at the beginning of the line
     24 
     25 
     26 for (let j = 0; j < 128; j++) {
     27   for (let k = 0; k < 128; k++) {
     28     for (let l = 0; l < 128; l++) {
     29       if (j == 34 || k ==34 || l ==34)
     30         continue;
     31       if (j == 0x0a || k ==0x0a || l ==0x0a)
     32         continue;
     33       if (j == 0x0d || k ==0x0d || l ==0x0d)
     34         continue;
     35       if (j == 0x3c || k ==0x3c || l ==0x3c)
     36         continue;
     37       if (
     38          (j == 47 && k == 47)
     39          ||(k == 47 && l == 47)
     40         )
     41         continue;
     42   try {
     43       var cmd = String.fromCharCode(j) + String.fromCharCode(k) + String.fromCharCode(l) + 'a.orange.ctf"';
     44       eval(cmd);
     45   } catch(e) {
     46       var err = e.toString().split('\n')[0].split(':')[0];
     47       if (err === 'SyntaxError' || err === "ReferenceError")
     48         continue
     49       err = e.toString().split('\n')[0]
     50   }
     51      console.log(err,cmd);
     52   }
     53   }
     54 }
     55 //From: https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#bounty-pl33z
     56 
     57 // From: Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 43). Kindle Edition.
     58 log=[];
     59 for(let i=0;i<=0xff;i++){
     60   for(let j=0;j<=0xfff;j++){
     61     try {
     62       eval(`${String.fromCodePoint(i,j)}%$£234$`)
     63       log.push([i,j])
     64     }catch(e){}
     65   }
     66 }
     67 console.log(log)//[35,33],[47,47]
     68 ```
     69 
     70 <sup>[[1]](#references)[[2]](#references)</sup>
     71 
     72 ### Valid JS New Lines Chars
     73 
     74 ```javascript
     75 //Javascript interpret as new line these chars:
     76 String.fromCharCode(10) //0x0a
     77 String.fromCharCode(13) //0x0d
     78 String.fromCharCode(8232) //0xe2 0x80 0xa8
     79 String.fromCharCode(8233) //0xe2 0x80 0xa8
     80 
     81 for (let j = 0; j < 65536; j++) {
     82   try {
     83     var cmd = '"aaaaa";' + String.fromCharCode(j) + '-->a.orange.ctf"'
     84     eval(cmd)
     85   } catch (e) {
     86     var err = e.toString().split("\n")[0].split(":")[0]
     87     if (err === "SyntaxError" || err === "ReferenceError") continue
     88     err = e.toString().split("\n")[0]
     89   }
     90   console.log(`[${err}]`, j, cmd)
     91 }
     92 //From: https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#bounty-pl33z
     93 ```
     94 
     95 <sup>[[1]](#references)</sup>
     96 
     97 ### Valid JS Spaces in function call
     98 
     99 ```javascript
    100 // Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (pp. 40-41). Kindle Edition.
    101 
    102 // Check chars that can be put in between in func name and the ()
    103 function x(){}
    104 
    105 log=[];
    106 for(let i=0;i<=0x10ffff;i++){
    107     try {
    108         eval(`x${String.fromCodePoint(i)}()`)
    109         log.push(i)
    110     }catch(e){}
    111 }
    112 
    113 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
    114 ```
    115 
    116 <sup>[[2]](#references)</sup>
    117 
    118 ### **Valid chars to Generate Strings**
    119 
    120 ```javascript
    121 // Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (pp. 41-42). Kindle Edition.
    122 
    123 // Check which pairs of chars can make something be a valid string
    124 log = []
    125 for (let i = 0; i <= 0x10ffff; i++) {
    126   try {
    127     eval(`${String.fromCodePoint(i)}%$£234${String.fromCodePoint(i)}`)
    128     log.push(i)
    129   } catch (e) {}
    130 }
    131 console.log(log) //34,39,47,96
    132 //single quote, quotes, backticks & // (regex)
    133 ```
    134 
    135 <sup>[[2]](#references)</sup>
    136 
    137 ### **Surrogate Pairs BF**
    138 
    139 This technique won't be very useful for XSS but it could be useful to bypass WAF protections. This python code receive as input 2bytes and it search a surrogate pairs that have the first byte as the the last bytes of the High surrogate pair and the the last byte as the last byte of the low surrogate pair.
    140 
    141 ```python
    142 def unicode(findHex):
    143     for i in range(0,0xFFFFF):
    144         H = hex(int(((i - 0x10000) / 0x400) + 0xD800))
    145         h = chr(int(H[-2:],16))
    146         L = hex(int(((i - 0x10000) % 0x400 + 0xDC00)))
    147         l = chr(int(L[-2:],16))
    148         if(h == findHex[0]) and (l == findHex[1]):
    149             print(H.replace("0x","\\u")+L.replace("0x","\\u"))
    150 ```
    151 
    152 More info:<sup>[[3]](#references)[[4]](#references)[[5]](#references)</sup>
    153 
    154 - [https://github.com/dreadlocked/ctf-writeups/blob/master/nn8ed/README.md](https://github.com/dreadlocked/ctf-writeups/blob/master/nn8ed/README.md)<sup>[[3]](#references)</sup>
    155 - [https://mathiasbynens.be/notes/javascript-unicode](https://mathiasbynens.be/notes/javascript-unicode) [https://mathiasbynens.be/notes/javascript-encoding](https://mathiasbynens.be/notes/javascript-encoding)<sup>[[4]](#references)[[5]](#references)</sup>
    156 
    157 ### `javascript{}:` Protocol Fuzzing
    158 
    159 ```javascript
    160 // Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 34). Kindle Edition.
    161 log=[];
    162 let anchor = document.createElement('a');
    163 for(let i=0;i<=0x10ffff;i++){
    164     anchor.href = `javascript${String.fromCodePoint(i)}:`;
    165     if(anchor.protocol === 'javascript:') {
    166         log.push(i);
    167     }
    168 }
    169 console.log(log)//9,10,13,58
    170 // You can also brute-force other positions containing repeated characters
    171 
    172 // Test one option
    173 let anchor = document.createElement('a');
    174 anchor.href = `javascript${String.fromCodePoint(58)}:alert(1337)`;
    175 anchor.append('Click me')
    176 document.body.append(anchor)
    177 
    178 // Another way to test
    179 <a href="https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/pentesting-web/xss-cross-site-scripting/%26#12;javascript:alert(1337)">Test</a>
    180 ```
    181 
    182 <sup>[[2]](#references)</sup>
    183 
    184 ### URL Fuzzing
    185 
    186 ```javascript
    187 // Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (pp. 36-37). Kindle Edition.
    188 
    189 // Before the protocol
    190 a = document.createElement("a")
    191 log = []
    192 for (let i = 0; i <= 0x10ffff; i++) {
    193   a.href = `${String.fromCodePoint(i)}https://hacktricks.wiki`
    194   if (a.hostname === "hacktricks.xyz") {
    195     log.push(i)
    196   }
    197 }
    198 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
    199 
    200 // Between the slashes
    201 a = document.createElement("a")
    202 log = []
    203 for (let i = 0; i <= 0x10ffff; i++) {
    204   a.href = `/${String.fromCodePoint(i)}/hacktricks.xyz`
    205   if (a.hostname === "hacktricks.xyz") {
    206     log.push(i)
    207   }
    208 }
    209 console.log(log) //9,10,13,47,92
    210 ```
    211 
    212 <sup>[[2]](#references)</sup>
    213 
    214 ### HTML Fuzzing
    215 
    216 ```javascript
    217 // Heyes, Gareth. JavaScript for hackers: Learn to think like a hacker (p. 38). Kindle Edition.
    218 
    219 // Fuzzing chars that can close an HTML comment
    220 
    221 let log = []
    222 let div = document.createElement("div")
    223 for (let i = 0; i <= 0x10ffff; i++) {
    224   div.innerHTML = `<!----${String.fromCodePoint(i)}><span></span>-->`
    225   if (div.querySelector("span")) {
    226     log.push(i)
    227   }
    228 }
    229 console.log(log) //33,45,62
    230 ```
    231 
    232 <sup>[[2]](#references)</sup>
    233 
    234 ## **Analyzing attributes**
    235 
    236 The tool **Hackability inspector** from Portswigger helps to **analyze** the **attributtes** of a javascript object. Check: [https://portswigger-labs.net/hackability/inspector/?input=x.contentWindow\&html=%3Ciframe%20src=//subdomain1.portswigger-labs.net%20id=x%3E](https://portswigger-labs.net/hackability/inspector/?input=x.contentWindow&html=%3Ciframe%20src=//subdomain1.portswigger-labs.net%20id=x%3E)<sup>[[6]](#references)</sup>
    237 
    238 ## **.map js files**
    239 
    240 - Trick to download .map js files: [https://medium.com/@bitthebyte/javascript-for-bug-bounty-hunters-part-2-f82164917e7](https://medium.com/@bitthebyte/javascript-for-bug-bounty-hunters-part-2-f82164917e7)<sup>[[7]](#references)</sup>
    241 - You can use this tool to analyze these files [https://github.com/paazmaya/shuji](https://github.com/paazmaya/shuji)<sup>[[8]](#references)</sup>
    242 
    243 ## "--" Assignment
    244 
    245 The decrement operator `--` is also an assignment. It converts a value to a number and decrements it by one; a nonnumeric value becomes `NaN`. This can be used to **replace variable contents in the environment**.
    246 
    247 ![Map JavaScript files using decrement assignment](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28993%29.png)
    248 
    249 ![Decrement assignment replacing an existing JavaScript value](https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/188de82beb54e70956b2952367a0af91d26758b8/src/images/image%20%28329%29.png)
    250 
    251 ## Functions Tricks
    252 
    253 ### .call and .apply
    254 
    255 The **`.call`** method of a function is used to **run the function**.\
    256 The **first argument** it expects by default is the **value of `this`** and if **nothing** is provided, **`window`** will be that value (unless **`strict mode`** is used).
    257 
    258 ```javascript
    259 function test_call() {
    260   console.log(this.value) //baz
    261 }
    262 new_this = { value: "hey!" }
    263 test_call.call(new_this)
    264 
    265 // To pass more arguments, just pass then inside .call()
    266 function test_call() {
    267   console.log(arguments[0]) //"arg1"
    268   console.log(arguments[1]) //"arg2"
    269   console.log(this) //[object Window]
    270 }
    271 test_call.call(null, "arg1", "arg2")
    272 
    273 // If you use the "use strict" directive "this" will be null instead of window:
    274 function test_call() {
    275   "use strict"
    276   console.log(this) //null
    277 }
    278 test_call.call(null)
    279 
    280 //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:
    281 function test_apply() {
    282   console.log(arguments[0]) //"arg1"
    283   console.log(arguments[1]) //"arg2"
    284   console.log(this) //[object Window]
    285 }
    286 test_apply.apply(null, ["arg1", "arg2"])
    287 ```
    288 
    289 ### Arrow functions
    290 
    291 Arrow functions allow you to generate functions in a single line more easily (if you understand them)
    292 
    293 ```javascript
    294 // Traditional
    295 function (a){ return a + 1; }
    296 // Arrow forms
    297 a => a + 100;
    298 a => {a + 100};
    299 
    300 // Traditional
    301 function (a, b){ return a + b + 1; }
    302 // Arrow
    303 (a, b) => a + b + 100;
    304 
    305 // Tradictional no args
    306 let a = 4;
    307 let b = 2;
    308 function (){ return a + b + 1; }
    309 
    310 // Arrow
    311 let a = 4;
    312 let b = 2;
    313 () => a + b + 1;
    314 ```
    315 
    316 So, most of the previous functions are actually useless because we aren't saving them anywhere to save and call them. Example creating the `plusone` function:
    317 
    318 ```javascript
    319 // Traductional
    320 function plusone(a) {
    321   return a + 1
    322 }
    323 
    324 //Arrow
    325 plusone = (a) => a + 100
    326 ```
    327 
    328 ### Bind function
    329 
    330 The bind function allow to create a **copy** of a **function modifying** the **`this`** object and the **parameters** given.
    331 
    332 ```javascript
    333 //This will use the this object and print "Hello World"
    334 var fn = function (param1, param2) {
    335   console.info(this, param1, param2)
    336 }
    337 fn("Hello", "World")
    338 
    339 //This will still use the this object and print "Hello World"
    340 var copyFn = fn.bind()
    341 copyFn("Hello", "World")
    342 
    343 //This will use the "console" object as "this" object inside the function and print "fixingparam1 Hello"
    344 var bindFn_change = fn.bind(console, "fixingparam1")
    345 bindFn_change("Hello", "World")
    346 
    347 //This will still use the this object and print "fixingparam1 Hello"
    348 var bindFn_thisnull = fn.bind(null, "fixingparam1")
    349 bindFn_change("Hello", "World")
    350 
    351 //This will still use the this object and print "fixingparam1 Hello"
    352 var bindFn_this = fn.bind(this, "fixingparam1")
    353 bindFn_change("Hello", "World")
    354 ```
    355 
    356 > [!TIP]
    357 > Note that using **`bind`** you can manipulate the **`this`** object that is going to be used when calling the function.
    358 
    359 ### Function code leak
    360 
    361 If you can **access the object** of a function you can **get the code** of that function
    362 
    363 ```javascript
    364 function afunc() {
    365   return 1 + 1
    366 }
    367 console.log(afunc.toString()) //This will print the code of the function
    368 console.log(String(afunc)) //This will print the code of the function
    369 console.log(this.afunc.toString()) //This will print the code of the function
    370 console.log(global.afunc.toString()) //This will print the code of the function
    371 ```
    372 
    373 In cases where the **function doesn't have any name**, you can still print the **function code** from within:
    374 
    375 ```javascript
    376 ;(function () {
    377   return arguments.callee.toString()
    378 })()(function () {
    379   return arguments[0]
    380 })("arg0")
    381 ```
    382 
    383 Some **random** ways to **extract the code** of a function (even comments) from another function:
    384 
    385 ```javascript
    386 ;(function () {
    387   return (retFunc) => String(arguments[0])
    388 })((a) => {
    389   /* Hidden comment */
    390 })()(function () {
    391   return (retFunc) => Array(arguments[0].toString())
    392 })((a) => {
    393   /* Hidden comment */
    394 })()(function () {
    395   return String(this)
    396 }).bind(() => {
    397   /* Hidden comment */
    398 })()((u) => String(u))((_) => {
    399   /* Hidden comment */
    400 })((u) => (_) => String(u))((_) => {
    401   /* Hidden comment */
    402 })()
    403 ```
    404 
    405 ## Sandbox Escape - Recovering window object
    406 
    407 The Window object allows to reach globally defined functions like alert or eval.
    408 
    409 ```javascript
    410 // Some ways to access window
    411 window.eval("alert(1)")
    412 frames
    413 globalThis
    414 parent
    415 self
    416 top //If inside a frame, this is top most window
    417 
    418 // Access window from document
    419 document.defaultView.alert(1)
    420 // Access document from a node object
    421 node = document.createElement('div')
    422 node.ownerDocument.defaultView.alert(1)
    423 
    424 // There is a path property on each error event whose last element is the window
    425 <img src onerror=event.path.pop().alert(1337)>
    426 // In other browsers the method is
    427 <img src onerror=event.composedPath().pop().alert(1337)>
    428 // In case of svg, the "event" object is called "evt"
    429 <svg><image href=1 onerror=evt.composedPath().pop().alert(1337)>
    430 
    431 // Abusing Error.prepareStackTrace to get Window back
    432 Error.prepareStackTrace=function(error, callSites){
    433 2   callSites.shift().getThis().alert(1337);
    434 3 };
    435 4 new Error().stack
    436 
    437 // From an HTML event
    438 // Events from HTML are executed in this context
    439 with(document) {
    440     with(element) {
    441         //executed event
    442     }
    443 }
    444 // Because of that with(document) it's possible to access properties of document like:
    445 <img src onerror=defaultView.alert(1337)>
    446 <img src onerror=s=createElement('script');s.append('alert(1337)');appendChild(s)>
    447 ```
    448 
    449 ## Breakpoint on access to value
    450 
    451 ```javascript
    452 // Stop when a property in sessionStorage or localStorage is set/get
    453 // via getItem or setItem functions
    454 sessionStorage.getItem = localStorage.getItem = function (prop) {
    455   debugger
    456   return sessionStorage[prop]
    457 }
    458 
    459 localStorage.setItem = function (prop, val) {
    460   debugger
    461   localStorage[prop] = val
    462 }
    463 ```
    464 
    465 ```javascript
    466 // Stop when anyone sets or gets the property "ppmap" in any object
    467 // For example sessionStorage.ppmap
    468 // "123".ppmap
    469 // Useful to find where weird properties are being set or accessed
    470 // or to find where prototype pollutions are occurring
    471 
    472 function debugAccess(obj, prop, debugGet = true) {
    473   var origValue = obj[prop]
    474 
    475   Object.defineProperty(obj, prop, {
    476     get: function () {
    477       if (debugGet) debugger
    478       return origValue
    479     },
    480     set: function (val) {
    481       debugger
    482       origValue = val
    483     },
    484   })
    485 }
    486 
    487 debugAccess(Object.prototype, "ppmap")
    488 ```
    489 
    490 ## Automatic Browser Access to test payloads
    491 
    492 ```javascript
    493 //Taken from https://github.com/svennergr/writeups/blob/master/inti/0621/README.md
    494 const puppeteer = require("puppeteer")
    495 
    496 const realPasswordLength = 3000
    497 async function sleep(ms) {
    498   return new Promise((resolve) => setTimeout(resolve, ms))
    499 }
    500 
    501 ;(async () => {
    502   const browser = await puppeteer.launch()
    503   const page = await browser.newPage()
    504   //Loop to iterate through different values
    505   for (let i = 0; i < 10000; i += 100) {
    506     console.log(`Run number ${i}`)
    507     const input = `${"0".repeat(i)}${realPasswordLength}`
    508     console.log(
    509       `  https://challenge-0621.intigriti.io/passgen.php?passwordLength=${input}&allowNumbers=true&allowSymbols=true&timestamp=1624556811000`
    510     )
    511     //Go to the page
    512     await page.goto(
    513       `https://challenge-0621.intigriti.io/passgen.php?passwordLength=${input}&allowNumbers=true&allowSymbols=true&timestamp=1624556811000`
    514     )
    515     //Call function "generate()" inside the page
    516     await page.evaluate("generate()")
    517     //Get node inner text from an HTML element
    518     const passwordContent = await page.$$eval(
    519       ".alert .page-content",
    520       (node) => node[0].innerText
    521     )
    522     //Transform the content and print it in console
    523     const plainPassword = passwordContent.replace("Your password is: ", "")
    524     if (plainPassword.length != realPasswordLength) {
    525       console.log(i, plainPassword.length, plainPassword)
    526     }
    527 
    528     await sleep(1000)
    529   }
    530   await browser.close()
    531 })()
    532 ```
    533 
    534 <sup>[[9]](#references)</sup>
    535 
    536 ## References
    537 
    538 - [1] [balsn.tw – HITCON CTF Quals 2019: bounty writeup](https://balsn.tw/ctf_writeup/20191012-hitconctfquals/#bounty-pl33z)
    539 - [2] [Heyes, Gareth – JavaScript for hackers: Learn to think like a hacker](https://www.goodreads.com/book/show/141410582-javascript-for-hackers)
    540 - [3] [dreadlocked – ctf-writeups: nn8ed](https://github.com/dreadlocked/ctf-writeups/blob/master/nn8ed/README.md)
    541 - [4] [Mathias Bynens – JavaScript has a Unicode problem](https://mathiasbynens.be/notes/javascript-unicode)
    542 - [5] [Mathias Bynens – JavaScript's internal character encoding](https://mathiasbynens.be/notes/javascript-encoding)
    543 - [6] [PortSwigger – Hackability inspector](https://portswigger-labs.net/hackability/inspector/?input=x.contentWindow&html=%3Ciframe%20src=//subdomain1.portswigger-labs.net%20id=x%3E)
    544 - [7] [bitthebyte – JavaScript for Bug Bounty Hunters (Part 2)](https://medium.com/@bitthebyte/javascript-for-bug-bounty-hunters-part-2-f82164917e7)
    545 - [8] [paazmaya/shuji – .map file analyzer](https://github.com/paazmaya/shuji)
    546 - [9] [svennergr – writeups: Intigriti 0621 challenge](https://github.com/svennergr/writeups/blob/master/inti/0621/README.md)