Le Duy Khuong

Chuỗi: ai-security-supply-chain · Phần 1

Năng suất & công cụ dev

Khi AI Tool Bị Đầu Độc — Giải Phẫu Một Supply Chain Attack

97 triệu lượt download mỗi tuần. Ba giờ bị nhiễm. Security scanner trở thành vũ khí. Đây là cách LiteLLM bị compromise — và ý nghĩa với bất kỳ ai đang vận hành AI infrastructure.

2026-03-2615 phút đọcVI

Phần 1 của 714% hoàn thành

Khi AI Tool Bị Đầu Độc

Sáng thứ Hai, tôi đọc tin một package Python tên LiteLLM bị tấn công supply chain. 97 triệu lượt download mỗi tuần. Ba giờ bị nhiễm. Mọi người cài đặt trong window đó đều bị malware đánh cắp API keys, SSH keys, database credentials.

Phản ứng đầu tiên: "Mình không bị ảnh hưởng. Mình dùng Docker image, không pip install."

Phản ứng thứ hai — sau khi ngồi nghĩ kỹ: "Khoan đã."


Con Đường Tấn Công Mà Ít Ai Nghĩ Tới

Attack chain lần này đặc biệt. Không phải tấn công trực tiếp LiteLLM. Mà bắt đầu từ Trivy — một security scanner. Đúng vậy, công cụ bảo mật trở thành vector tấn công.

Trivy bị compromise. Credentials của Trivy CI bị đánh cắp. Credentials đó cho phép push phiên bản LiteLLM bị nhiễm lên PyPI. Và cứ thế — mỗi developer cài LiteLLM trong 3 giờ đó đều trở thành nạn nhân tiếp theo trong chuỗi.

Có một irony ở đây mà tôi nghĩ đáng dừng lại suy nghĩ: chúng ta tin tưởng security tools nhất — và chúng cũng có quyền access cao nhất. Khi chúng bị compromise, blast radius lớn hơn bất kỳ thứ gì khác.


Cái Mà Tôi Suýt Bỏ Sót

Hệ thống AI server cá nhân của tôi chạy LiteLLM như một Docker container. Phân tích ban đầu nói rằng tôi "likely safe" vì không pip install trong window bị nhiễm.

Nhưng nhìn kỹ hơn vào Docker Compose:

litellm:
  image: ghcr.io/berriai/litellm:main-latest

:main-latest. Một floating tag. Nó không phải phiên bản — nó là con trỏ thay đổi theo thời gian.

Và trên cùng server, Watchtower chạy mỗi ngày lúc 4 giờ sáng:

watchtower:
  volumes:
    - /var/run/docker.sock:/var/run/docker.sock
  environment:
    - WATCHTOWER_CLEANUP=true
    - WATCHTOWER_SCHEDULE=0 0 4 * * *

Bạn thấy vấn đề chưa?

Nếu attacker đẩy một Docker image bị nhiễm lên GitHub Container Registry — Watchtower sẽ tự kéo về lúc 4 giờ sáng, restart container, và malware có quyền access tất cả environment variables bên trong: ANTHROPIC_API_KEY, DATABASE_URL, LITELLM_MASTER_KEY.

Không cần ai pip install gì cả. Auto-updater làm hết.


Rà Soát Và Phát Hiện

Tôi quyết định không chỉ check LiteLLM mà rà soát toàn bộ hạ tầng. Kết quả:

18 trên 24 Docker services dùng floating tags. Ollama, Prometheus, Grafana, Portainer — tất cả :latest. Mỗi cái là một cánh cửa mở cho supply chain attack.

Zero dependency auditing. 14 CI/CD workflows, không workflow nào chạy pip-audit hay npm audit. Pre-commit hook scan secrets nhưng không scan dependencies. 3 file Python requirements dùng >= không giới hạn trên — nghĩa là pip install có thể kéo bất kỳ phiên bản nào, kể cả phiên bản bị nhiễm.

Không container hardening. AI server chạy mọi thứ với full Linux capabilities, ports bind 0.0.0.0, không resource limits. Trong khi đó, cùng hệ thống có PURO ERP đã hardened đúng cách — cap_drop ALL, read_only: true, 127.0.0.1 binding. Hai thế giới song song.

Kết quả verify cuối cùng: LiteLLM image từ ngày 08/02 — 6 tuần trước attack. Watchtower không chạy. Không bị compromise. Nhưng nếu Watchtower đang active — câu chuyện đã rất khác.


Những Gì Tôi Fix

Pin mọi thứ

# Trước
image: ghcr.io/berriai/litellm:main-latest
 
# Sau
image: ghcr.io/berriai/litellm:main-v1.82.6

18 services, tất cả được pin vào version cụ thể. :latest không phải version — nó là lời hứa rằng bạn sẽ luôn nhận được thứ mới nhất. Vấn đề là "mới nhất" có thể bao gồm malware.

Watchtower chuyển sang monitor-only

environment:
  - WATCHTOWER_MONITOR_ONLY=true
  - WATCHTOWER_CLEANUP=false
volumes:
  - /var/run/docker.sock:/var/run/docker.sock:ro

Watchtower vẫn chạy, nhưng chỉ thông báo khi có update — không tự pull. Docker socket mount read-only. Quyết định update nằm ở người, không phải máy.

Container hardening

Mọi service đều được thêm:

security_opt:
  - no-new-privileges:true
cap_drop:
  - ALL
read_only: true
ports:
  - "127.0.0.1:4000:4000"
deploy:
  resources:
    limits:
      memory: 2G

Localhost-only binding. Không privilege escalation. Filesystem read-only. Resource limits. Mẫu từ PURO ERP — đã chạy ổn định, chỉ cần copy-paste cho phần còn lại.

Dependency auditing

Pre-commit hook giờ cảnh báo khi commit file requirements.txt có floating versions. CI workflow chạy pip-auditnpm audit hàng tuần. Python deps chuyển từ >=2.0.0 (cho phép mọi thứ) sang ~=2.10.0 (chỉ patch updates).


Năm Bài Học

Floating tags + auto-updater = silent backdoor. Không phải backdoor ai cố ý cài. Mà là backdoor bạn tự tạo khi tin rằng "latest" luôn an toàn. Pin by version. Luôn luôn.

Security tools cũng là attack surface. Trivy là security scanner — và nó trở thành weapon. Defense-in-depth phải bao gồm chính các công cụ defense.

"Not affected" khác "not vulnerable." Lần này Watchtower tình cờ không chạy. Nhưng attack surface vẫn ở đó. Fix trước khi incident tiếp theo — không phải sau.

Compatible release (~=) là sweet spot. >=2.0.0 cho phép mọi thứ. ==2.10.3 miss security patches. ~=2.10.0 cho phép patches, chặn breaking changes. Đủ linh hoạt, đủ an toàn.

Copy-paste security tốt hơn reinvent security. Nếu bạn có một service đã hardened đúng cách, dùng nó làm template. Không cần nghĩ lại từ đầu cho mỗi service.


Supply Chain Attack — Không Phải Chuyện Mới

LiteLLM không phải nạn nhân đầu tiên. Pattern này lặp đi lặp lại.

Năm 2020, SolarWinds Orion bị cài backdoor trong build process — 18,000 tổ chức bị ảnh hưởng, bao gồm cả cơ quan chính phủ Mỹ. Năm 2021, Codecov bash uploader bị sửa — credentials của hàng nghìn CI/CD pipeline bị leak trong 2 tháng trước khi ai phát hiện. Năm 2023, CircleCI bị compromise — toàn bộ secrets lưu trong platform bị exposed.

Mỗi lần, vector tấn công đều giống nhau: không tấn công target trực tiếp, mà tấn công thứ target tin tưởng. Build system, CI/CD, package manager, security scanner. Chuỗi tin tưởng (chain of trust) dài bao nhiêu, attack surface lớn bấy nhiêu.

Điều khác biệt lần này: target là AI infrastructure. Và AI infrastructure đặc biệt hấp dẫn vì ba lý do.

Thứ nhất, credentials có giá trị trực tiếp. API key của Anthropic hay OpenAI không chỉ mở cửa vào hệ thống — nó tốn tiền thật mỗi giây nó được dùng. Attacker có thể bán access hoặc chạy inference trên tài khoản nạn nhân.

Thứ hai, AI tools có dependency graph phức tạp nhất trong software ecosystem hiện tại. LiteLLM alone kéo theo hàng chục transitive dependencies. LangChain còn nhiều hơn. ChromaDB, vLLM, mỗi tool là một cây dependency khổng lồ mà ít ai audit từng nhánh.

Thứ ba — và đây là điều đáng lo nhất — developer thường ít paranoid hơn với AI tools so với production infrastructure. "Nó chỉ là tool thử nghiệm" hay "Mình chỉ dùng local" — những câu như vậy tạo ra blind spots mà attacker khai thác.


MCP Plugins — Attack Surface Mà Bạn Không Thấy

Người phát hiện attack LiteLLM lần này là một developer dùng Cursor IDE. Anh ta không cài LiteLLM — nhưng một MCP plugin trong Cursor tự kéo nó về như transitive dependency.

Đây là attack surface mới mà hầu hết developer chưa nghĩ tới.

MCP (Model Context Protocol) cho phép AI agents connect tới external tools. Mỗi MCP server có thể yêu cầu dependencies riêng. Khi bạn cài một MCP plugin, bạn đang tin tưởng không chỉ plugin đó — mà toàn bộ dependency tree của nó. Và dependency tree đó có thể thay đổi bất kỳ lúc nào.

Bạn cài MCP plugin "AI Code Assistant"
  -> Plugin phụ thuộc vào litellm
  -> litellm bị poisoned trên PyPI
  -> pip install kéo version bị nhiễm
  -> Malware chạy ngay khi Python khởi động (litellm_init.pth)
  -> API keys, SSH keys, credentials bị exfiltrate

Bạn không bao giờ gõ pip install litellm. Bạn thậm chí không biết nó được cài. Nhưng nó ở đó — trong site-packages, với một file .pth tự chạy mỗi khi Python start.

File .pth là cơ chế ít ai biết trong Python. Bất kỳ file .pth nào trong site-packages đều được Python thực thi tự động khi khởi động — không cần import, không cần gọi. Đây là lý do phiên bản LiteLLM v1.82.8 đặc biệt nguy hiểm: malware chạy ngay cả khi bạn không import LiteLLM trong code.


Incident Response — Những Bước Tôi Thực Hiện

Khi đánh giá rủi ro cho hạ tầng cá nhân, tôi theo framework đơn giản:

Bước 1: Xác định exposure. Tôi có dùng LiteLLM không? Dùng ở đâu? Phiên bản nào? Cài khi nào?

Bước 2: Verify timeline. Image LiteLLM trên server được build ngày nào? Container tạo ngày nào? Có nằm trong attack window không?

Bước 3: Kiểm tra auto-update mechanisms. Watchtower có đang chạy không? Có pull image mới trong thời gian attack không?

Bước 4: Mở rộng scope. Không chỉ check LiteLLM — rà soát toàn bộ hạ tầng. Có bao nhiêu floating tags? Bao nhiêu dependencies không pin? Bao nhiêu services không hardened?

Kết quả: LiteLLM image từ ngày 08/02, Watchtower không chạy — không bị compromise. Nhưng rà soát mở rộng phát hiện 18/24 services dùng floating tags, zero dependency auditing, không container hardening. Hệ thống may mắn lần này, nhưng attack surface vẫn mở toang.


Bài Tiếp Theo

Bài này nói về cái gì đã xảy ratại sao nó nguy hiểm. Nhưng hiểu vấn đề mới là bước đầu — fix mới là bước quan trọng.

Bài tiếp theo trong series: Floating Tags & Auto-Updaters — Silent Backdoors Trong Docker. Đi sâu vào tại sao :latest là anti-pattern, Watchtower tạo implicit trust chain, và cách pin images đúng cách với configs thực tế.


Đây là bài 1/7 trong series "Bảo Mật Trong Kỷ Nguyên AI Agent" — từ một sự cố thực tế đến toàn cảnh bảo mật khi AI agents trở thành infrastructure core.

LDK

Le Duy Khuong

AI Transformation & Digital Strategy. Writing about agentic systems, engineering leadership, and building in public.