Skip to content

Instantly share code, notes, and snippets.

@packz
Last active October 11, 2015 07:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save packz/3825835 to your computer and use it in GitHub Desktop.
Save packz/3825835 to your computer and use it in GitHub Desktop.
CRYPTO
// http://fail0verflow.com/blog/2013/megafail.html
// Mega's standard hash function
function h(s)
{
var a = [0,0,0,0];
var aes = new sjcl.cipher.aes([111111,222222,333333,444444]);
s += Array(16).join('X');
for (var i = s.length&-16; i--; )
{
a[(i>>2)&3] ^= s.charCodeAt(i)<<((7-i&3)<<3);
if (!(i&15)) a = aes.encrypt(a);
}
return a;
}
// The reverse hash function, used to backtrack across data.
// reverse_h(s, h(s)) == [0,0,0,0]
function reverse_h(s,a)
{
var aes = new sjcl.cipher.aes([111111,222222,333333,444444]);
for (var i = 0; i < s.length; i++)
{
if (!(i&15)) a = aes.decrypt(a);
a[(i>>2)&3] ^= s.charCodeAt(i)<<((7-i&3)<<3);
}
return a;
}
// The hash value that we want to forge
g_hash = [0,0,0,0]
$(document).ready(function(){
// Just fetches the file from disk and computes the standard Mega hash
$("#file").change(function() {
if (this.files.length > 0) {
var binaryReader = new FileReader();
binaryReader.onload = function(){
var blob = binaryReader.result;
g_hash = h(blob);
$("#hash").text("[" + g_hash.join(",") + "]");
};
binaryReader.readAsText(this.files[0]);
}
});
// Computes the forged file
$("#forge").click(function(){
// Initialize the AES engine
var aes = new sjcl.cipher.aes([111111,222222,333333,444444]);
// Append JS-friendly padding such that the file is a multiple of
// 16 bytes long and ends in "\n/*" (yes, this could be simplified
// by merging the two comments, but whatever).
var head = $("#forgedfile").text() + "\n//XXX";
head += Array(16).join('X');
head = head.substr(0, (head.length&-16) - 3);
head += "\n/*"
// Reverse the MAC to compute the intermediate MAC value for the
// head of the file, such that the final MAC value is what we want.
// (the head of the file is the tail of the hash data, since the MAC
// runs backwards). Decrypt it one last time; this decryption
// corresponds to the encryption performed at the end of the collision
// block (see below).
var headhash = aes.decrypt(reverse_h(head, g_hash));
// Create and MAC normally the last block in the file. This is just
// "*/\n" plus the standard XXXXX padding applied by h()
var tail = "*/\n";
var tailhash = h(tail);
// Finally, given one intermediate MAC value and what we want the next
// MAC value to be, just XOR them together to find the block that we
// need to insert.
var collision = "";
for (i = 0; i < 16; i++) {
var xor = headhash[i>>2] ^ tailhash[i>>2];
collision += String.fromCharCode((xor >> ((3-i&3)<<3)) & 0xff);
}
// Append everything together to form the new file
var data = head + collision + tail;
// Double check that the collision worked
var h2 = h(data);
if ( g_hash[0] == h2[0] && g_hash[1] == h2[1] &&
g_hash[2] == h2[2] && g_hash[3] == h2[3]) {
alert("Collision successful, hash value matches!");
var uri = "data:application/octet-stream," + encodeURIComponent(data);
window.open(uri, 'Your forged file');
} else {
alert("Collision failed! Got " + h2.join(","));
};
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment