BÀI VIẾT
Bốn giờ: chuỗi cung ứng của LiteLLM bị tấn công thế nào, và token lộ từ đâu
Tường trình sự cố của chính LiteLLM lần ngược về ba token OAuth GitHub bị đánh cắp, một trong số đó lộ ra vì Claude Code gắn nó vào một response body bị ghi log. Dòng thời gian, phạm vi ảnh hưởng, và việc cần kiểm tra nếu bạn đã pull bản v1.107.0.

Trong bài viết
10:08:29 UTC, ngày 21/10. Đó là giây chính xác ai đó dùng một token OAuth của GitHub CLI bị đánh cắp để tắt branch protection trên repo của LiteLLM, theo dòng thời gian chính LiteLLM công bố. Đến 14:20 UTC, khoảng bốn giờ sau, việc dọn dẹp đã xong: package độc hại bị gỡ khỏi PyPI, các tag Docker Hub được chuyển lại, token bị chiếm bị thu hồi. Bốn giờ thực ra là phản ứng nhanh. Chỉ là bốn giờ trước 10:08 mới là phần đáng nói.
Những gì bị chiếm: cả bảy wheel PyPI của litellm==1.107.0, cộng các tag v1.107.0, latest, main-latest và main-stable trên cả Docker Hub và GHCR. Nếu bạn pull LiteLLM qua bất kỳ kênh nào trong số đó vào ngày 21/10, bạn đã pull đúng bản độc hại, không có ngoại lệ. Số liệu chính LiteLLM dẫn từ ClickPy cho thấy bản độc hại đó có khoảng 49.000 lượt download trước khi bị gỡ.
Kẻ tấn công dùng ba token OAuth GitHub CLI bị chiếm, trong đó có một token thuộc về chủ sở hữu tổ chức (org owner), để tắt branch protection và force-push một workflow release đã bị sửa. Workflow đó chỉ vào một repo giả mạo, một bản fork gần giống tên thật chỉ lệch một dấu gạch ngang, và kéo code độc hại từ đó về. Mười commit được đẩy vào, xếp chồng lên nhau để trông không có gì bất thường, trong đó có một commit chỉ thêm một dòng vô hại, data = “cook”, vào pyproject.toml. Payload thật nằm trong một loader sitecustomize.py tách biệt, kiểm tra một file con trỏ lưu trên GitHub mỗi giờ để nhận lệnh. Đây là phần đáng dừng lại suy nghĩ: malware này không chạy trọn vẹn ngay từ đầu, nó đi kèm một đường dây gọi về GitHub mà kẻ tấn công có thể dùng để đẩy hành vi mới vào mọi máy đã cài, miễn là package còn nằm đó.
Payload làm gì khi đã bám được vào máy: đánh cắp credential, sau đó tải công cụ thực thi code từ xa (RCE) theo lệnh. Không phải một lần lấy dữ liệu rồi xong. Một điểm bám lâu dài, tiếp tục nhận lệnh sau khi cài xong.
Ba token OAuth đã rơi vào tay kẻ tấn công thế nào

Đây là chi tiết đáng khiến bạn dừng đọc nếu đang dùng bất kỳ công cụ coding agentic nào: tường trình sự cố của chính LiteLLM viết rằng “Claude Code will embed a user’s GitHub CLI OAuth token into a response body, and in situations where response bodies are logged, these tokens may appear in LiteLLM logs” — Claude Code gắn token OAuth của GitHub CLI vào response body, và trong các trường hợp response body bị ghi log, token đó có thể xuất hiện trong log của LiteLLM. Đọc lại câu đó hai lần. Một công cụ coding agentic đặt một token OAuth còn sống vào body của một HTTP response. Một gateway nằm trên đường request, được cấu hình ghi log response body, lưu token đó ra đĩa dưới dạng plaintext. Kẻ tấn công không cần phishing ai, không cần brute-force mật khẩu. Họ chỉ đọc log mà một công cụ phổ biến đã lặng lẽ nhồi đầy credential mà chẳng ai biết đang nằm ở đó.
Mình không nghĩ đây là lỗi riêng của LiteLLM, và LiteLLM cũng không trình bày theo hướng đó. Đây là một dạng vấn đề áp dụng cho bất kỳ gateway, proxy hay lớp observability nào nằm giữa một công cụ agentic và các dịch vụ nó gọi, ngay khi có ai bật log chi tiết để debug một chuyện khác chẳng liên quan. Agent đã làm đúng việc nó được xây để làm: mang credential qua request để lần gọi tiếp theo xác thực được. Lớp logging cũng làm đúng việc nó được xây để làm: ghi lại body để debug. Không thành phần nào độc hại cả. Sự kết hợp giữa hai thứ đó tạo ra một token mà một con người có thể lấy ra chỉ bằng cách đọc file log.
Chiêu repo giả mạo đáng được ghi riêng một mục trong threat model CI/CD của bất kỳ ai. Một file workflow thường trỏ về đúng org của bạn có thể bị force-push để trỏ sang nơi khác hoàn toàn, và trừ khi có ai diff file workflow ở mỗi lần push, không chỉ review pull request, cú chuyển hướng đó cứ nằm yên làm việc của nó. Branch protection vốn là lớp chặn cuối cho đúng kiểu tấn công này. Nó chỉ hoạt động nếu credential có thể tắt nó khó đánh cắp hơn credential bảo vệ database production của bạn, và vào ngày 21/10 điều đó không đúng.
Việc nên kiểm tra ngay trong tuần này
Nếu bạn chạy litellm==1.107.0 từ PyPI, hoặc đã pull bất kỳ tag Docker nào trong bốn tag kể trên vào ngày 21/10, hãy coi như đã bị chiếm và build lại từ một bản sạch, không chỉ upgrade. Xoay vòng mọi credential mà container hay môi trường đó có quyền truy cập; một điểm bám RCE lâu dài đã có thời gian đọc bất cứ thứ gì nó chạm tới được, và bạn không có quyền giả định nó chưa làm vậy. LiteLLM nói khách hàng enterprise dùng private registry không bị ảnh hưởng, vì các artifact bị chiếm chỉ chạm tới kênh PyPI và Docker Hub/GHCR công khai, điều này thu nhỏ phạm vi ảnh hưởng nếu đó là setup của bạn, nhưng nên xác nhận lại chứ không nên giả định.
Tách riêng khỏi sự cố cụ thể này, hãy kiểm tra xem cấu hình logging của chính bạn đang ghi lại gì từ bất kỳ gateway nào nằm giữa công cụ coding agentic và các API nó gọi. Ghi log response body là một mặc định debug hợp lý, cho đến ngày một công cụ bạn chưa từng audit gắn một credential vào đâu đó trong body mà bạn đang hốt trọn. Lọc token ngay ở lớp logging, không chỉ ở điểm một con người có thể gõ token vào prompt.
Cú force-push xảy ra lúc 10:08:29 UTC. Có người viết lại được đúng cái giây đó vì họ dựng lại được từ log. Hãy chắc log của chính bạn cũng cho bạn làm được điều tương tự, nhưng vì lý do đúng đắn.
Nguồn
LiteLLM: Security Incident, October 21, 2026 (tường trình sự cố gốc, truy cập 28/10/2026).


Thảo luận
Bình luận được duyệt trước khi công khai. Email của bạn được giữ riêng tư.