Bleeding Llama: Khi Ollama Local Lộ Bộ Nhớ Qua Mạng
CVE-2026-7482: GGUF độc hại rò heap khi quantize-prompt, key, chat hàng xóm. Tôi phân tích Bleeding Llama và cách khóa chặt Ollama local. 🔐
Mục lục
Nếu từng chỉ người thân cách “chạy Llama trên laptop” cho kín đáo, câu chuyện quyền riêng tư vẫn đúng-nhưng Ollama lộ trên mạng thì không còn là chuyện “chỉ trên máy mình” nữa. Đó là một daemon: kẻ ngoài chạm vào được nếu mình không nhốt nó lại.
Nhà nghiên cứu đặt tên Bleeding Llama (CVE-2026-7482, CVSS 9.1, CWE-125 đọc ngoài vùng) vì file GGUF độc hại cộng bước tạo model có thể kéo byte heap lân cận-nội dung chat, system prompt, đôi khi cả biến môi trường-vào artifact rồi đẩy ra ngoài bằng /api/push. API của /api/create và /api/push mặc định không xác thực; cộng thêm việc bind 0.0.0.0 (phổ biến trong hướng dẫn Docker/README), phạm vi thiệt hại tăng rất nhanh.
Bài này tôi viết như kể cho người nhà: cái gì hỏng, kẻ tấn công xâu chuỗi thế nào, và sáng thứ Hai tôi sẽ làm gì thật sự.
Nếu bạn còn upload model hoặc gọi /api/create trên bản cũ hơn Ollama 0.17.1, hãy nâng cấp trước khi đọc tiếp-bản vá nằm ở v0.17.1 (PR #14406). Phần dưới giả định bạn đã vá hoặc tách mạng hoàn toàn.
Hai câu về GGUF (vì lỗi nằm ở đó)
GGUF cất weight dưới dạng tensor-mảng có tên, shape, kiểu, và offset trong file. Khi Ollama quantize (ví dụ F16→F32), nó duyệt tensor và dùng số phần tử suy ra từ shape đó.
GGUF là file nhị phân do người gửi kiểm soát hoàn toàn. Nếu header khai hàng triệu phần tử mà dữ liệu thật chỉ bằng tem thư, vòng lặp không kiểm tra sẽ đọc vượt vùng đệm-đúng nghĩa heap out-of-bounds read.
Vì sao “Go an toàn bộ nhớ” vẫn rỉ
Go an toàn cho tới khi không. Ollama dùng unsafe để tối ưu trong WriteTo() (mô tả NVD: fs/ggml/gguf.go, server/quantization.go). Phân tích của Cyera chỉ rõ điểm nghẽn: bước chuyển kiểu như ConvertToF32 tin tưởng Elements()-tích của các chiều tensor-để xác định số phần tử cần đọc. Shape khai càng lớn thì số byte được đọc càng nhiều.
Để byte rò rỉ vẫn đọc được sau khi chuyển đổi kiểu dữ liệu, họ chọn phép chuyển không mất thông tin—F16→F32—nên nội dung bí mật không bị biến thành số vô nghĩa (Cyera Research).
Chuỗi ba API call mà tôi hình dung
Client không xác thực vẫn nói chuyện được với daemon của bạn:
- Upload file GGUF độc hại qua endpoint blob (
PUT /api/blobs/sha256:[digest]—digest khớp hash body của request). - Gọi
/api/createtrỏ tới blob đó và bậtquantizeđểWriteTo()chạy—đây là lúc OOB read xảy ra và byte heap của người khác trộn vào tensor đầu ra. - Tuồn ra ngoài bằng
/api/push. Cyera chỉ ra Ollama chấp nhận tên model trông giống URL và đẩy artifact tới registry do kẻ tấn công kiểm soát—dữ liệu rò rỉ ra khỏi mạng của bạn dưới dạng “model”.
Bleeding Llama - chuỗi khái niệm
View diagram source
sequenceDiagram
participant A as "Attacker"
participant O as "Ollama"
participant R as "Exfil host"
A->>O: PUT blob (crafted GGUF digest)
Note over O: Stores blob by SHA-256 key
A->>O: POST /api/create (quantize path)
O->>O: Quantization reads past buffer (heap leak into tensors)
A->>O: POST /api/push (model name as URL)
O->>R: Upload artifact with leaked bytes
Đây không phải phép màu-đây là ranh giới tin cậy tan: ai chạm được API là người có thể upload blob và yêu cầu quantize.
Thực sự lộ cái gì
Cyera phục hồi được prompt người dùng, system prompt, và biến môi trường-đúng thứ bạn sợ trên máy inference dùng chung: mảnh chat tenant khác, secret tích hợp trong env, có thể cả API key IDE nhét vào process. Bài báo phụ (SecurityWeek, The Hacker News) nhắc số instance lộ internet lớn-rủi ro của bạn phụ thuộc lộ cổng và đã vá hay chưa, không phải con số headline.
Nếu nối Claude Code hay tool tương tự vào inference local, đầu ra tool cũng có thể nằm trong heap-cùng lớp rò.
Mình có dính không? Kiểm tra trong 60 giây
Hai câu hỏi định blast radius: bản cũ? và bind hở? Một cái đã xấu; cả hai là kịch bản tệ nhất.
# 1. Phiên bản - dưới 0.17.1 là dính
ollama --version
# 2. API có trả lời ngoài loopback không?
ss -lntp | grep 11434
# Xấu: 0.0.0.0:11434 hoặc [::]:11434
# Tốt: 127.0.0.1:11434
# 3. Từ máy khác trong LAN, list được model không?
curl -sS --max-time 3 http://<lan-ip>:11434/api/tags | head
# 4. Docker - kiểm tra port không bind ra ngoài
docker inspect ollama --format '{{json .NetworkSettings.Ports}}'
Bước 3 mà trả JSON thay vì timeout, bạn đang phơi đúng bề mặt API không xác thực mà Bleeding Llama khai thác.
Việc tôi làm (defense-in-depth, không khẩu hiệu)
Vá ngay, đừng chần chờ. Echo (CVE record) trích v0.17.1 và commit 88d57d0.
Đừng để Ollama lộ ra internet. Bind 127.0.0.1, cô lập Docker trong mạng nội bộ, truy cập remote chỉ qua VPN.
Dựng auth ở tầng trước. Reverse proxy hoặc API gateway với mTLS, OIDC, hoặc ít nhất ACL mạng-coi daemon upstream là không có bảo vệ gì.
Thu nhỏ blast radius. Tách workload theo team/dự án; đừng nhét secret prod chung env với bot thử nghiệm.
Kiểm tra cổng đang mở. ss -lntp, security group cloud, NAT router-nếu TCP 11434 trả lời từ Wi-Fi quán cà phê, bạn đang phơi đúng thứ kẻ tấn công muốn.
Tôi coi Ollama mở ra mạng như một datastore ai cũng ghi được mà không cần đăng nhập: nếu không để Postgres mở toang thì cũng không để endpoint nhận blob và chạy quantize mở toang.
Cấu hình tôi paste thật
systemd drop-in - ghim loopback, kể cả khi gói cập nhật đổi mặc định:
# /etc/systemd/system/ollama.service.d/override.conf
[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"
Environment="OLLAMA_ORIGINS=http://127.0.0.1,http://localhost"
sudo systemctl daemon-reload && sudo systemctl restart ollama
Docker - đừng publish ra 0.0.0.0; bind rõ ràng:
# Xấu: -p 11434:11434 (bind 0.0.0.0)
# Tốt: -p 127.0.0.1:11434:11434
docker run -d --name ollama \
-p 127.0.0.1:11434:11434 \
-v ollama:/root/.ollama \
ollama/ollama:0.17.1
Caddy đứng phía trước - bearer token để cả người trong LAN cũng phải xác thực:
ollama.lan {
@auth header Authorization "Bearer {env.OLLAMA_TOKEN}"
handle @auth { reverse_proxy 127.0.0.1:11434 }
respond 401
}
v0.17.1 vá đúng cái gì
Bản vá (commit 88d57d0, PR #14406) đơn giản nhưng đúng trọng tâm: giới hạn read theo kích thước file thật, không theo header. Đường convert giờ đối chiếu Elements() với kích thước tensor thật trên đĩa trước khi cấp phát hay copy, nên header GGUF dù khai sai cũng không thể khiến WriteTo() đọc ra ngoài buffer. Không thêm auth, không thêm ACL-chỉ là cái length check vốn đã phải có từ block unsafe đầu tiên. Bài học chung: bất cứ khi nào truyền vào unsafe một con số lấy từ file, hãy coi file đó là kẻ tấn công cho đến khi chứng minh được ngược lại.
Việc khác: auto-update trên Windows
Lỗi rò heap trên Linux/macOS là vấn đề hoàn toàn khác với updater trên Windows. CERT Polska và Striga theo dõi CVE khác (CVE-2026-42248, CVE-2026-42249) về payload cập nhật không ký và path traversal trên Windows-đọc riêng nếu người nhà cài Ollama desktop (CERT Polska, Striga research). Timeline vá khác Bleeding Llama; coi như checklist incident riêng.
Nguồn tôi gửi người nhà
- CVE & vector: NVD - CVE-2026-7482 (CNA: Echo), CVE.org record
- Phân tích kỹ: Cyera Research - Bleeding Llama
- Bản vá upstream: Release v0.17.1, PR #14406, fix commit
- Bài báo phụ: SecurityWeek, The Hacker News
AI local vẫn cho quyền kiểm soát-nhưng kiểm soát có nghĩa là vá, chọn interface, và chứng minh daemon không hét qua internet. Tôi thích vặn nút đó hơn là lên báo.