# Một bài thơ trên GitHub và một chuỗi CVE: botnet PoeLLM đào coin trên hạ tầng AI phơi ra Internet

## Tóm tắt

**PoeLLM** là malware mà Black Lotus Labs (Lumen) theo dõi từ tháng 4/2026, đã xâm nhập **hơn 3.400 server**, với đỉnh điểm hơn **800 máy hoạt động mỗi ngày**. Nạn nhân tập trung ở Mỹ và Tây Âu, và phần lớn đang chạy các dịch vụ AI hoặc mã nguồn mở tự host phơi ra Internet: **LiteLLM, Ollama, Gotenberg, Gitea**, cùng dấu hiệu nhắm cả Ivanti Sentry.

Hai chi tiết khiến chiến dịch này đáng đọc kỹ:

*   **Đường vào chính là** `CVE-2026-42271` trên LiteLLM. Horizon3 đã chứng minh lỗ hổng này nối được với `CVE-2026-48710` ("BadHost" của Starlette) để trở thành **RCE không cần xác thực, CVSS 10.0**.
    
*   **Địa chỉ C2 được giấu trong một bài thơ** đặt trong repo GitHub. Muốn đổi C2, kẻ tấn công chỉ cần sửa vài từ trong bài thơ, và toàn bộ máy bị nhiễm tự tính ra địa chỉ mới.
    

Mô hình kiếm tiền là đào coin qua XMRig và Iron, kết nối tới pool Kryptex. Nhưng mỗi máy bị nhiễm còn **bị biến thành scanner và exploit server**, nên botnet tự mở rộng mà không cần kẻ tấn công can thiệp thêm.

**Hành động ưu tiên:**

*   Vá LiteLLM lên **1.83.7 hoặc mới hơn** và Starlette lên **1.0.1 hoặc mới hơn**. Nếu chưa vá được, chặn ngay hai endpoint test ở mục dưới.
    
*   Xoay toàn bộ credential mà LiteLLM proxy đang giữ: API key của các nhà cung cấp model, secret và biến môi trường. Nếu instance từng phơi ra Internet, hãy coi chúng là đã bị lộ.
    

## Chuỗi lỗ hổng: từ "cần xác thực" thành "không cần gì cả"

`CVE-2026-42271` là lỗ hổng command injection ở hai endpoint MCP server của LiteLLM:

```plaintext
POST /mcp-rest/test/connection
POST /mcp-rest/test/tools/list
```

Hai endpoint này nhận **toàn bộ cấu hình của một MCP server**, bao gồm `command`, danh sách tham số và biến môi trường dùng cho stdio transport. Khi được gọi, LiteLLM **spawn lệnh đó thành subprocess ngay trên host của proxy**.

Ban đầu lỗ hổng được xếp là cần xác thực, vì truy cập hai endpoint này bị chặn bởi một API key hợp lệ của proxy.

**Rồi** `CVE-2026-48710` **xuất hiện.** Đây là lỗi kiểm tra Host header trong Starlette, công bố ngày 26/05/2026 dưới tên "BadHost". Horizon3 xác nhận với các triển khai LiteLLM có Starlette **≤ 1.0.0** trong cây phụ thuộc, lỗi này **bỏ qua hoàn toàn cơ chế xác thực**.

Kết quả: remote code execution không cần bất kỳ thông tin đăng nhập nào, chạy với quyền của tiến trình LiteLLM proxy. Horizon3 đánh giá chuỗi này ở mức **CVSS 10.0 Critical**.

Khai thác thành công cho phép kẻ tấn công:

*   Thực thi lệnh tùy ý trên host chạy LiteLLM
    
*   Truy cập credential của các nhà cung cấp model
    
*   Lấy API key và secret mà proxy đang lưu
    
*   Di chuyển ngang vào hạ tầng AI đã kết nối
    
*   Xâm nhập các hệ thống phía sau gateway
    

**Phiên bản ảnh hưởng:** LiteLLM **1.74.2 đến 1.83.6**, và mọi triển khai có Starlette ≤ 1.0.0.

## Mốc thời gian: chiến dịch chạy song song với nghiên cứu

Ghép timeline của Horizon3 với timeline của Black Lotus Labs cho ra một bức tranh đáng chú ý:

| Ngày | Diễn biến |
| --- | --- |
| 13/04/2026 | **Commit đầu tiên** của bài thơ chứa địa chỉ C2 lên GitHub. C2 đầu tiên là một router ở Brazil, dùng để thử nghiệm quy trình lây nhiễm |
| 20/04/2026 | `CVE-2026-42271` được công bố, mô tả là command injection **cần xác thực** |
| 08/05/2026 | LiteLLM phát hành bản vá 1.83.7, bổ sung kiểm soát phân quyền và cập nhật phụ thuộc Starlette |
| Tháng 5/2026 | Kẻ tấn công bắt đầu quét và khai thác diện rộng, nhắm LiteLLM và Gotenberg |
| 26/05/2026 | Công bố công khai `CVE-2026-48710` ("BadHost") |
| 01/06/2026 | **Horizon3 xác nhận chuỗi khai thác** và RCE không cần xác thực |
| Đầu tháng 6/2026 | BLL phát hiện hạ tầng PoeLLM qua một nạn nhân Ivanti Sentry (`CVE-2026-10520`) |
| Giữa tháng 6/2026 | Đỉnh điểm chiến dịch |
| 07/10/2026 | BLL công bố báo cáo và chặn toàn bộ traffic tới các C2 của PoeLLM |

Điểm cần chú ý: kẻ tấn công đã dựng hạ tầng từ **13/04**, trước cả khi CVE-2026-42271 được công bố ngày 20/04, và bắt đầu khai thác diện rộng trong tháng 5, **trước khi Horizon3 công bố chuỗi khai thác vào 01/06**. Chiến dịch không chạy theo sau một bài blog nghiên cứu; nó chạy song song.

Với đội phòng thủ, điều này nghĩa là khoảng thời gian giữa "CVE được công bố ở mức high, cần xác thực" và "thực tế đang bị khai thác không cần xác thực" có thể bằng không.

## PoeLLM: C2 giấu trong một bài thơ

Malware là một file ELF đặt tên `libgcrypt`. Cách nó tìm C2 là phần thú vị nhất của toàn bộ báo cáo.

Kẻ tấn công tạo một repo GitHub dưới tài khoản `ejejejdfbbebe`, **fork từ source code của website nodejs.org**, rồi thêm vào một file tên `dash.css` (file này không tồn tại trong repo gốc). Trong file đó là một bài thơ hai khổ tên *On the Nature of Connection*.

Malware có logic riêng để trích 4 từ hoặc cụm từ từ bài thơ, không phân biệt chữ hoa chữ thường:

```plaintext
Từ 1: đoạn text giữa "In the silent hum of " và ","
Từ 2: đoạn text giữa "each pulse of " và " threading"
Từ 3: đoạn text giữa "Beyond the wall of " và ","
Từ 4: tìm "of distant servers", lùi về whitespace trước đó,
       yêu cầu 4 byte trước từ đó phải là "the ",
       rồi lấy từ ngay sau "the"
```

Bốn từ này được tra trong một **từ điển hard-code trong thân malware** để ra bốn con số, ghép lại thành địa chỉ IPv4 của C2. BLL dẫn ví dụ cụ thể: một mẫu tìm được ngày 23/06/2026 giải ra C2 `92.119.165[.]74`.

Muốn đổi C2, kẻ tấn công chỉ cần **sửa vài từ khóa trong bài thơ trên GitHub**. Toàn bộ máy bị nhiễm tự tính ra địa chỉ mới ở lần kiểm tra tiếp theo. Bài thơ đã được sửa **11 lần** kể từ commit đầu tiên, mỗi lần trỏ sang một C2 mới. Mẫu trích xuất chưa từng thay đổi, chỉ có từ khóa đổi.

## Khả năng của malware và cách botnet tự mở rộng

Theo phân tích của BLL, `libgcrypt` chứa:

*   **Remote shell**
    
*   **Miner XMRig và Iron**, kết nối tới pool Kryptex
    
*   **Quét HTTP/S**
    
*   **Triển khai exploit** nhắm các mục tiêu dễ tổn thương
    

Phần quan trọng nhất về mặt vận hành: **mỗi máy bị nhiễm trở thành một worker trong chuỗi khai thác**. Máy nạn nhân quét Internet tìm mục tiêu, tập trung vào **port 3000 (Gotenberg)** và **port 4000 (LiteLLM)**. Khi tìm được mục tiêu, nó gửi một POST request được dựng riêng tới endpoint lỗi, hướng mục tiêu tải file từ C2 trên **port 81**.

Sau khi nhiễm, malware beacon về C2 trên một trong các port **3778, 5001, 5002 hoặc 9999**.

Gần đây BLL ghi nhận các nhóm nạn nhân bắt đầu nhắm **port SSH và các cổng đăng nhập khác**, dấu hiệu cho thấy kẻ tấn công đang thử nghiệm một framework brute-force phân tán. Tuy nhiên phần lớn mục tiêu của đợt thử nghiệm này lại là server ở Italy, nên BLL đánh giá năng lực này còn ở giai đoạn đầu.

## Hạ tầng C2 dựng trên router bị chiếm

Thay vì thuê server, kẻ tấn công **tái sử dụng router có trang quản trị phơi ra Internet** làm C2.

C2 đầu tiên (`191.37.28[.]160`) là một router ở Brazil chạy Boa web server trên port 2222, có lỗ hổng `CVE-2018-21027` và `CVE-2018-21028`, dù BLL không tìm được bằng chứng khai thác trực tiếp. C2 tiếp theo (`120.224.114[.]212`) ở Trung Quốc, cũng là Boa web server với trang quản trị router. Mẫu này lặp lại ở các C2 sau.

Một server ở Italy (`185.119.19[.]171`), từng chạy dashboard Prometheus và Uptime Kuma, nhận kết nối từ nhiều C2 khác nhau. BLL đánh giá ở mức **moderate confidence** rằng đây là giao diện quản trị của kẻ tấn công để điều hành hạ tầng C2 và botnet.

## Quy kết

BLL **không quy kết chắc chắn**, chỉ đánh giá ở mức **moderate confidence** rằng người vận hành nói tiếng Italy, dựa trên ba điểm:

*   **Comment bằng tiếng Italy** trong mẫu malware BLL phân tích
    
*   **Server quản trị đặt ở Italy**
    
*   Domain `malwarescan[.]xyz` đăng ký tháng 2/2026, đặt trên một server ở Italy mà C2 đầu tiên liên lạc tới khoảng một giờ sau commit đầu tiên
    

Đây là mức đánh giá nên giữ nguyên khi tổng hợp lại. Comment ngôn ngữ trong mã và vị trí server đều là chỉ dấu yếu nếu đứng riêng.

## Nhận định

**Hạ tầng AI tự host là mục tiêu kép.** BLL nói rõ điều này: server chạy LLM hấp dẫn vì vừa chứa dữ liệu và credential giá trị, vừa chạy trên GPU mạnh rất phù hợp để đào coin. Với một chiến dịch thuần kiếm tiền, GPU cluster là phần thưởng lớn hơn bất kỳ máy chủ web nào.

**Endpoint** `/mcp-rest/test/*` **là ví dụ điển hình của tính năng test lọt ra production.** Một endpoint nhận cả `command` và biến môi trường rồi spawn subprocess là chức năng hợp lý trong môi trường phát triển, nhưng không nên tồn tại trên một instance phơi ra Internet. Khi rà soát các công cụ AI tự host, câu hỏi nên đặt ra là "có endpoint nào nhận lệnh để chạy không", trước cả câu hỏi "phiên bản này có CVE nào".

**Xếp ưu tiên vá theo điểm CVSS gốc là một cái bẫy.** `CVE-2026-42271` được công bố ở mức high và "cần xác thực". Nhiều tổ chức sẽ xếp nó vào hàng đợi vá bình thường. Một tháng sau, một lỗ hổng trong **thư viện phụ thuộc** biến nó thành CVSS 10.0 không cần xác thực. Điểm số của một CVE không phản ánh được rủi ro thực khi nó nằm trong một cây phụ thuộc.

**C2 qua bài thơ không khó về kỹ thuật, nhưng rất khó takedown.** Cơ chế này không có gì tinh vi về mật mã: trích chuỗi theo mẫu cố định rồi tra từ điển. Giá trị của nó nằm ở chỗ khác. Thứ nhất, traffic tới GitHub là hợp lệ ở mọi môi trường dev. Thứ hai, nội dung bài thơ không chứa IP nào để quét hay chặn. Thứ ba, đổi C2 chỉ mất một commit, không cần cập nhật malware trên 3.400 máy. Takedown repo thì kẻ tấn công tạo repo khác.

**Dùng router bị chiếm làm C2 cho thấy ưu tiên là chi phí, không phải ẩn danh.** Router có Boa web server và trang quản trị phơi ra ngoài là thiết bị cũ, dễ chiếm và miễn phí. Nhưng chúng cũng để lại dấu vết rất dễ nhận: BLL lần ra toàn bộ hạ tầng phần nào nhờ mẫu "C2 nào cũng là router có admin page lỗi". Một nhóm có nguồn lực tốt hơn sẽ không làm vậy.

**Số liệu trong báo cáo gốc chưa nhất quán.** Phần key takeaways của BLL ghi hơn 3.400 nạn nhân, nhưng phần thân bài vẫn còn con số "gần 2.200" ở hai chỗ. BleepingComputer cũng ghi nhận đã sửa từ 2.100 lên 3.400 sau khi báo cáo được cập nhật. Khi dẫn lại, nên dùng 3.400 và nói rõ đây là số nạn nhân tích lũy, không phải số đang hoạt động.

## Liên hệ Việt Nam

*   **LiteLLM và Ollama đang được dựng rất nhiều trong các đội AI nội bộ.** Mô hình quen thuộc là một server GPU thuê ngoài hoặc một máy trong phòng lab, dựng LiteLLM làm gateway cho nhiều model, rồi mở port ra Internet để các thành viên truy cập từ xa cho tiện. Đây đúng là cấu hình mà PoeLLM đang quét tìm.
    
*   **Thiệt hại thấy ngay là chi phí, nhưng rủi ro lớn hơn là credential.** GPU bị chiếm để đào coin làm tăng hóa đơn điện và làm chậm workload thật. Nhưng LiteLLM proxy thường giữ API key của OpenAI, Anthropic, Google và các nhà cung cấp khác, cùng key nội bộ. Mất những key đó vừa là thiệt hại tài chính trực tiếp vừa là rủi ro dữ liệu.
    
*   **Gitea phổ biến trong các đội tự host Git** ở Việt Nam vì nhẹ và miễn phí. Một Gitea bị chiếm nghĩa là source code và CI secret nằm trong tay kẻ tấn công.
    
*   **Các công cụ AI thường nằm ngoài quy trình quản lý tài sản.** Đội AI dựng hạ tầng theo nhịp riêng, thường nhanh hơn chu kỳ rà soát của bộ phận bảo mật. Nhiều instance không có trong danh sách tài sản, nên cũng không có trong chu kỳ vá.
    
*   **Gotenberg có cảnh báo rõ ràng trong tài liệu cài đặt là không được phơi ra Internet.** Hàng trăm server bị nhiễm cho thấy cảnh báo trong tài liệu không phải một biện pháp bảo mật.
    

## Khuyến nghị

*   **Vá ngay:** LiteLLM lên **1.83.7+** và Starlette lên **1.0.1+**. Kiểm tra cả cây phụ thuộc, vì Starlette có thể được kéo vào qua package khác chứ không phải cài trực tiếp.
    
*   **Nếu chưa vá được:** chặn truy cập tới `/mcp-rest/test/connection` và `/mcp-rest/test/tools/list` ở tầng reverse proxy hoặc WAF, giới hạn truy cập mạng chỉ cho dải tin cậy, và xoay credential mà proxy đang giữ.
    
*   **Rà soát bề mặt phơi ra Internet của toàn bộ công cụ AI và dev tự host:** LiteLLM (port 4000), Ollama, Gotenberg (port 3000), Gitea. Đưa chúng vào danh sách tài sản và chu kỳ vá như mọi dịch vụ khác. Đây là biện pháp có giá trị vượt ra ngoài chiến dịch này.
    
*   **Săn dấu vết khai thác trong log:** request tới hai endpoint test nêu trên, **giá trị Host header bất thường** (dấu hiệu lạm dụng CVE-2026-48710), và tiến trình con bất ngờ được sinh ra từ tiến trình LiteLLM.
    
*   **Phát hiện hành vi sau khai thác:** kết nối ra port 81 để tải file, beacon tới port 3778, 5001, 5002 hoặc 9999, kết nối tới hạ tầng Kryptex, và GPU hoặc CPU sử dụng cao bất thường không khớp với workload đã biết. Thêm cảnh báo cho traffic **đi ra** nhắm port 3000 và 4000 của host khác, vì đó là dấu hiệu máy của bạn đã trở thành scanner cho botnet.
    
*   **Nạp IOC ở mục dưới vào SIEM và firewall**, lưu ý ba C2 còn hoạt động tại thời điểm BLL công bố.
    

## IOC

> Danh sách theo báo cáo của Black Lotus Labs. BLL duy trì bộ IOC cập nhật trên repository GitHub của họ.

**C2 còn hoạt động tại thời điểm công bố (07/10/2026)**

```plaintext
92.119.164.50      # từ 23/07/2026
103.249.201.108    # từ 13/08/2026
178.128.14.204     # từ 16/06/2026
```

**C2 đã dùng trước đó**

```plaintext
191.37.28.160      # 13/04 - 16/05/2026 (router Brazil, C2 đầu tiên)
89.39.253.46       # 14/04 - 24/06/2026
120.224.114.212    # 09/05 - 08/09/2026
5.78.73.122        # 08/06 - 22/08/2026
15.204.178.28      # 14/06 - 17/08/2026
92.119.165.74      # 01/07 - 23/07/2026
45.133.73.28       # 18/07 - 22/07/2026
185.132.53.158     # 19/07 - 12/09/2026
136.148.69.233     # 12/09 - 25/09/2026
```

**Hạ tầng quản trị và liên quan**

```plaintext
185.119.19.171     # giao diện quản trị của kẻ tấn công (moderate confidence), Italy
57.131.5.211:80    # server Italy, host malwarescan[.]xyz
malwarescan[.]xyz  # domain đăng ký 02/2026
```

**Hạ tầng đào coin (Kryptex)**

```plaintext
5.180.174.162:8029
46.21.245.211:7029    # edge-ru-07[.]kryptex[.]network
kryptex[.]ru          # pool
```

**Mẫu malware**

```plaintext
SHA-256: 6fab94577364beec314afae3b082dd680933f08a8349b9f35b92667e8231b501
Tên file: libgcrypt (ELF)
```

**URL phát tán tiêu biểu**

```plaintext
hxxp://5.78.73[.]122:81/private/python3.6
hxxp://5.78.73[.]122:81/private/bins.sh
```

Mẫu đường dẫn `/private/` trên **port 81** lặp lại ở nhiều C2 khác, bao gồm `120.224.114[.]212`.

**Port cần giám sát**

| Port | Vai trò |
| --- | --- |
| 81 | C2 phục vụ file tải về |
| 3778, 5001, 5002, 9999 | Port beacon của malware |
| 3000 | Gotenberg, bị quét tìm mục tiêu |
| 4000 | LiteLLM, bị quét tìm mục tiêu |
| 2222 | Boa web server trên router C2 đầu tiên |

**Repo GitHub chứa bài thơ**

```plaintext
Tài khoản:  ejejejdfbbebe
Repo:       fork từ source code của nodejs.org
File:       dash.css  (không tồn tại trong repo gốc)
Nội dung:   bài thơ "On the Nature of Connection"
```

**CVE liên quan**

```plaintext
CVE-2026-42271    # LiteLLM command injection qua MCP test endpoints
CVE-2026-48710    # Starlette "BadHost" Host header validation bypass
CVE-2026-10520    # Ivanti Sentry, đường vào của nạn nhân đầu tiên BLL phát hiện
CVE-2018-21027    # router Boa web server, C2 đầu tiên
CVE-2018-21028    # router Boa web server, C2 đầu tiên
```

## Tài liệu tham khảo

*   Horizon3 — *CVE-2026-42271 Chained with CVE-2026-48710: LiteLLM Unauthenticated Remote Code Execution via Starlette Host Header Validation Bypass*: https://horizon3.ai/attack-research/vulnerabilities/cve-2026-42271-chained-with-cve-2026-48710/
    
*   Black Lotus Labs (Lumen) — *Canto incognito: tracking the PoeLLM malware* (07/10/2026): https://www.lumen.com/blog/en-us/canto-incognito-tracking-the-poellm-malware
    
*   BleepingComputer (Bill Toulas) — *PoeLLM malware infects exposed AI servers in cryptomining attacks* (07/10/2026): https://www.bleepingcomputer.com/news/security/poellm-malware-infects-exposed-ai-servers-in-cryptomining-attacks/
    
*   Help Net Security — *Cryptomining botnet hides C2 addresses in GitHub poem, infects over 3,400 servers* (08/10/2026): https://www.helpnetsecurity.com/2026/10/08/poellm-malware-github-poem-ai-servers/
