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  248 249  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×tamp=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×tamp=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)