Shibuya.XSS techtalk #11 の発表資料です。
for(;;){alert(``);} /* 2019/05/16 Shibuya.XSS techtalk #11*//* Masato Kinugawa */
View Slide
•••
•••••
••••
•••••••••••
while(1){alert(``);}
••
•
••> decodeURIComponent("%E3%81%82");< " "> decodeURIComponent("%FF");‣ Uncaught URIError: URI malformed
•••> encodeURIComponent(" ");< "%E3%81%82"> encodeURIComponent("\uDC00");‣ Uncaught URIError: URI malformed
https://www.ecma-international.org/ecma-262/9.0/index.html#sec-encode
(function a(){alert(``);a();})()
•••• / \/• " \"• \ \\<br/>userInput = "AAA\\\";alert(1)\/\/ <\/script>";<br/>displayContents();<br/>
•<br/>userInput = "AAA [ ] BBB";<br/>displayContents();<br/>
•<br/>userInput = "AAA<br/>BBB";<br/>displayContents();<br/>
•https://www.ecma-international.org/ecma-262/9.0/index.html#table-33
•<br/>userInput = "AAA [U+2028] BBB";<br/>displayContents();<br/>
<br/>userInput = "<!--<script>";<br/>displayContents();<br/>
••<br/>userInput = "<!--<script>";<br/>
••https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements
•<br/>userInput = "<%";<br/>%>https://html5sec.org/#91 <% %>
setInterval(`alert(\`\`)`,1);
•••userInfo = {"name": 123}//name = userInfo.name.toUpperCase()Uncaught TypeError:userInfo.name.toUpperCase is not a function
siteData ={"url":"https:// ...","title":{"toString":null},...};url = "url: " + siteData.url;title = "title: " + siteData.title;
•••({toString: function(){alert(1)} })+"";({valueOf : function(){alert(2)} })+"";
•••> ({toString:null})+"";‣ Uncaught TypeError:Cannot convert object to primitive value
•> typeof "aaa";< ‣ "string"> typeof 123;< ‣ "number"> typeof true;< ‣ "boolean"> typeof [];< ‣ "object"> typeof {};< ‣ "object"> typeof null;< ‣ "object"
••> Array.isArray([]);< ‣ true> Array.isArray({});< ‣ false> null === null< ‣ true
••> Object.prototype.toString.call("aaa");< ‣ "[object String]"> Object.prototype.toString.call(123);< ‣ "[object Number]"> Object.prototype.toString.call(true);< ‣ "[object Boolean]"> Object.prototype.toString.call([]);< ‣ "[object Array]"> Object.prototype.toString.call({});< ‣ "[object Object]"> Object.prototype.toString.call(null);< ‣ "[object Null]"
*{<br/>color:expression(<br/>alert("<br/>")<br/>)}<br/>
•••AAA
••function tellMeFruitColor(USER_INPUT){fruits = {"apple":"red","lemon":"yellow","peach":"pink"};if(fruits[USER_INPUT]){return USER_INPUT + ": " + fruits[USER_INPUT];}else{return "I don't know that fruit";}}
> tellMeFruitColor("apple");< "apple: red"> tellMeFruitColor("lemon");< "lemon: yellow"> tellMeFruitColor("strawberry");< "I don't know that fruit"> tellMeFruitColor("toString");< "toString: function toString() { [native code] }"> tellMeFruitColor("constructor");< "constructor: function Object() { [native code] }"> tellMeFruitColor("__proto__");< "__proto__: [object Object]"
https://qiita.com/howdy39/items/35729490b024ca295d6c
if(fruits["toString"]){return "toString" + ": " + fruits["toString"];}else{return "I don't know that fruit";}
> ({"toString":function(){return "a"}})+"";< " "> ({"prop":"a"})+"";< "[object Object]"
whiteListTags = {"span":funcForSanitizingSpanElem,"div": funcForSanitizingDivElem,"a":" funcForSanitizingAElem, ...}// whiteListTags[ ]whiteListTags["toString"]()
fileIcons = {"txt":"https://example.com/img/icon-txt.gif","png":"https://example.com/img/icon-png.gif","jpg":" https://example.com/img/icon-jpg.gif ", ...}// fileIcons[ ]dos.constructorfileIcons["constructor"]
••function tellMeFruitColor(USER_INPUT){fruits = { ... };- if(fruits[USER_INPUT]){+ if(Object.prototype.hasOwnProperty.call(fruits,USER_INPUT)){return USER_INPUT + ": " + fruits[USER_INPUT];}else{return "I don't know that fruit";}}
> tellMeFruitColor("apple");< "apple: red"> tellMeFruitColor("lemon");< "lemon: yellow"> tellMeFruitColor("strawberry");< "I don't know that fruit"> tellMeFruitColor("toString");< "I don't know that fruit"> tellMeFruitColor("constructor");< "I don't know that fruit"> tellMeFruitColor("__proto__");< "I don't know that fruit"
•• fruits["toString"] undefinedfunction tellMeFruitColor(USER_INPUT){- fruits = { ... };+ fruits = Object.create(null);+ fruits = Object.assign(fruits,{"apple":"red","lemon": ... })if(fruits[USER_INPUT]){return USER_INPUT + ": " + fruits[USER_INPUT];}else{return "I don't know that fruit";}}
••function tellMeFruitColor(USER_INPUT){- fruits = { ... };+ fruits = new Map(["apple","red"],+ ["lemon","yellow"],+ ["peach","pink"]);- if(fruits[USER_INPUT]){- return USER_INPUT + ": " + fruits[USER_INPUT];+ if(fruits.get(USER_INPUT)){+ return USER_INPUT + ": " + fruits.get(USER_INPUT);}else{return "I don't know that fruit";}}
({toString:function(){alert(``);this+"";}})+"";
•••USER_INPUT = {"length": 1e10,"constructor":{"name":"Array"}};if(USER_INPUT.constructor.name === "Array"){array = [];for (var i = 0; i < USER_INPUT.length; i++) {array.push(USER_INPUT[i]);}}
==== JS stack trace =========================================Security context: 00000099763A5549 1: /* anonymous */ [repl:~1] [pc=000000C1A3E8B9B9](this=00000382794865D9 Object>)5: /* anonymous */ [vm.js:65] [bytecode=000002EDD76E8421offset=87](this=0000021A2A00C731 00000203C25E1319>,options=0000021A2A00C709 )6: defaultEval [repl.js:244] [bytecode=000002EDD76E7089offset=445](this=0000021A2A00C7A1 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory1: node::DecodeWrite2: node_module_register3: v8::internal::FatalProcessOutOfMemory4: v8::internal::FatalProcessOutOfMemory5: v8::internal::Factory::NewUninitializedFixedArray6: v8::internal::WasmDebugInfo::SetupForTesting7: v8::internal::interpreter::BytecodeArrayRandomIterator::UpdateOffsetFromIndex8: 000000C1A3D043C1
for(;;){alert(``);} /* */