Ngày 22 tháng 9 năm 2026, các nhà nghiên cứu ThreatDown đã mô tả botnet CARBONATO. Nó truy cập vào các máy chủ thông qua API Docker mở mà không cần mật khẩu, thiết lập một tác nhân AI trên máy chủ và đầu tiên tìm kiếm các khóa cho các mô hình ngôn ngữ. Các khóa SSH và mã thông báo truy cập sẽ đến sau. Nếu bạn có các trình phân tích đang chạy trên VPS trong các container, và trong .env có các khóa OpenRouter hoặc OpenAI và thông tin đăng nhập proxy, đó là hồ sơ rủi ro của bạn. Dưới đây là phân tích về cách thức tấn công diễn ra và danh sách kiểm tra trong 15 phút để bịt lối vào này.
Những gì đã tìm thấy: 4,3 GB hình ảnh và một tác nhân có tên GH0ST
Tất cả bắt đầu từ một kho Docker không được bảo mật của chính các nhà điều hành. Theo dữ liệu từ ThreatDown, trong đó có 59 kho, 234 thẻ hình ảnh và 4,3 GB dữ liệu. Kho lưu trữ này bao gồm khoảng thời gian từ tháng 10 năm 2024 đến tháng 8 năm 2026, tức là botnet đã hoạt động gần hai năm trước khi được mô tả. Trong các kho lưu trữ, đã tìm thấy trình khai thác XMRig và các hình ảnh có tên kiểu fsociety/agent.
Chuỗi lây nhiễm theo báo cáo trông như sau:
- Máy quét tìm kiếm các máy chủ nơi API Docker mở trên TCP 2375 mà không cần xác thực.
- Thông qua API này, sâu máy tính khởi động một container có đặc quyền với hệ thống tệp của máy chủ được gắn kết. Từ thời điểm này, nó thực sự là root trên máy.
- Thiết lập một đường hầm SSH ngược đến cơ sở hạ tầng của các nhà điều hành, cài đặt máy chủ SSH với khóa của họ.
- Việc cố định diễn ra thông qua cron, bộ đếm thời gian systemd, rc.local và OpenRC, trong đó các tệp được đánh dấu là không thể thay đổi. Các quy trình giám sát sẽ tải lại các hình ảnh nếu có gì đó bị xóa.
- Container được ngụy trang dưới tên
systemd-resolved, trong khi quy trình ngụy trang dưới luồng nhân[kworker/u2:0]. - Mỗi năm phút, các kịch bản quét các mạng con lân cận /24 và các cầu Docker để tìm cổng mở tiếp theo 2375.
Sự lây lan hoàn toàn tự động và không phụ thuộc vào AI. AI ở đây chịu trách nhiệm về những gì xảy ra bên trong máy chủ đã bị chiếm đoạt.
Tại sao botnet cần tác nhân AI và tại sao nó cần các khóa LLM
Một khung mở Hermes Agent với nhân vật GH0ST (hướng dẫn của nó nằm trong tệp SOUL.md) được cài đặt trên máy chủ. Nhà điều hành viết nhiệm vụ trong Telegram, tác nhân chuyển tiếp nó cùng với hướng dẫn đến cổng LLM của hoạt động. Mô hình phân tích nhiệm vụ, viết lệnh cho terminal, đọc đầu ra và quyết định những gì cần làm tiếp theo. Kết quả được gửi trở lại Telegram.
Trong hướng dẫn của tác nhân, các ưu tiên được ghi rõ. ThreatDown trích dẫn: «Các khóa API AI là ưu tiên tuyệt đối. Exfiltrate trước». Trong danh sách có 14 nhà cung cấp mô hình, bao gồm OpenAI, Anthropic, Google, Groq, Mistral và OpenRouter. Các tài khoản SSH, mã thông báo truy cập và dữ liệu cơ sở sẽ là các mục tiếp theo.
Logic rất đơn giản: khóa LLM bị đánh cắp ngay lập tức trở thành tính toán miễn phí hoặc hàng hóa để bán lại, và người sở hữu khóa sẽ phải trả tiền cho chúng. Khác với khai thác, vụ trộm này không thể nhìn thấy qua tải CPU. Bạn chỉ có thể nhận thấy nó qua hóa đơn từ nhà cung cấp mô hình.
Tại sao điều này liên quan đến những người phân tích
Chồng công nghệ phân tích điển hình vào năm 2026 trông như sau: VPS, một vài container (crawler, hàng đợi, cơ sở dữ liệu, trình duyệt không đầu), LLM để phân tích các trang và một nhóm proxy. Tất cả các bí mật nằm trong một .env hoặc trong các biến môi trường của các container. Đối với tác nhân, người "đọc mọi thứ" với quyền truy cập root, đây là một mảnh dễ dàng chỉ với một tệp:
- khóa LLM: vì lý do đó mà CARBONATO được tạo ra;
- thông tin đăng nhập và mật khẩu proxy: báo cáo không tách riêng chúng, nhưng tác nhân với quyền truy cập root thu thập bất kỳ tài khoản và mã thông báo nào mà nó thấy, và các thông tin đăng nhập proxy thường nằm gần đó;
- khóa đám mây và quyền truy cập vào cơ sở dữ liệu với kết quả phân tích;
- máy chủ: XMRig trong cùng một kho lưu trữ có nghĩa là CPU của crawler của bạn sẽ dành cho việc khai thác, và các nhiệm vụ sẽ bắt đầu bị lỗi do thời gian chờ.
Có một vấn đề riêng với danh tiếng. Máy chủ mà sâu máy tính quét các mạng con của người khác nhanh chóng bị đưa vào danh sách lạm dụng, và nhà cung cấp có thể chặn nó theo khiếu nại. Đối với việc phân tích, đây là một cú đánh kép: IP của máy chủ bị đánh dấu, và các thông tin đăng nhập proxy bị đánh cắp đã tiêu tốn lưu lượng truy cập của bạn với các yêu cầu của người khác.
Làm thế nào cổng 2375 lại mở, mặc dù bạn không mở nó
Mặc định, Docker lắng nghe socket UNIX cục bộ, không phải mạng. Cổng 2375 xuất hiện khi ai đó cố tình thêm -H tcp://0.0.0.0:2375 vào cấu hình của daemon. Thông thường, điều này được thực hiện để kết nối IDE từ xa, CI hoặc bảng điều khiển quản lý container, và sau đó họ quên nó. Tài liệu Docker cảnh báo rõ ràng rằng quyền truy cập vào daemon tương đương với quyền truy cập root vào máy, và khuyên nên bảo vệ các khóa khỏi nó như mật khẩu root. Phiên bản mã hóa với TLS hoạt động trên cổng 2376, trong khi 2375 có nghĩa là văn bản mở mà không kiểm tra khách hàng.
Cái bẫy thứ hai đánh vào những người tự tin rằng "tôi có ufw". Trong tài liệu Docker, có nói rằng lưu lượng của các cổng được công bố của các container được chuyển tiếp trong bảng nat trước các chuỗi INPUT và OUTPUT mà ufw dựa vào. Trong thực tế, các quy tắc ufw cho các cổng như vậy đơn giản là không hoạt động. Nếu bạn đã khởi động Redis, bảng điều khiển hàng đợi hoặc trình quản lý proxy với -p 6379:6379, cổng sẽ lộ ra internet, bất kể ufw status hiển thị gì.
Danh sách kiểm tra trong 15 phút cho máy chủ với các trình phân tích
1. Kiểm tra rằng Docker API không lắng nghe mạng
- Xem các cổng đang lắng nghe:
ss -tlnp | grep -E '2375|2376|dockerd'. Nếu trong đầu ra có0.0.0.0:2375hoặc:::2375, hãy đóng ngay lập tức. - Kiểm tra nguồn gốc của cờ:
/etc/docker/daemon.json(khóa"hosts") và đơn vịsystemctl cat docker(dòngExecStartvới-H tcp://). - Loại bỏ trình nghe TCP và khởi động lại daemon. Để quản lý từ xa, hãy sử dụng ngữ cảnh SSH:
docker context create remote --docker host=ssh://user@server. Cổng bên ngoài không cần thiết chút nào. - Nếu TCP vẫn cần thiết (CI, trình điều phối), thì chỉ 2376 với xác thực TLS lẫn nhau và danh sách trắng IP nguồn.
2. Kiểm tra những gì đang lộ ra từ các container
- Thực hiện
docker ps --format '{{.Names}} {{.Ports}}'. Mọi thứ bắt đầu bằng0.0.0.0:đều có sẵn từ internet mà không qua ufw. - Các dịch vụ hệ thống (Redis, Postgres, Mongo, bảng điều khiển hàng đợi, Selenium Grid, API của trình quản lý proxy) chỉ nên công bố trên địa chỉ cục bộ:
-p 127.0.0.1:6379:6379. Truy cập bên ngoài nên thực hiện qua đường hầm SSH. - Nếu không thể tránh khỏi việc có cổng bên ngoài, hãy lọc trong chuỗi
DOCKER-USER: nó không bị Docker ghi đè, và chính nó được áp dụng cho lưu lượng của các container. - Không giữ một proxy mở trên máy chủ mà không có xác thực (Squid trên 3128, SOCKS trên 1080 "cho riêng mình"). Các máy quét thường xuyên tìm thấy các cổng như vậy, và lưu lượng của người khác sẽ đi qua IP của bạn với các khiếu nại của người khác.
3. Giải quyết các bí mật
- Chia tách các khóa: mỗi khóa LLM cho mỗi máy chủ hoặc dự án, với giới hạn chi phí từ nhà cung cấp mô hình. Một khóa bị đánh cắp với giới hạn $20 — là một rắc rối, không có giới hạn — là một lỗ hổng trong ngân sách.
- Các khóa proxy cũng nên được phân chia theo nhiệm vụ: một thông tin đăng nhập riêng (tài khoản phụ) cho mỗi trình phân tích. Như vậy, việc rò rỉ có thể được phát hiện qua chi phí của thông tin đăng nhập cụ thể, và chỉ cần thu hồi nó mà không dừng lại công việc còn lại.
- Không chuyển toàn bộ
.envvào container thông quaenv_file, nếu dịch vụ chỉ cần hai khóa trong số hai mươi. - Nơi nào có thể, hãy gắn quyền truy cập vào IP của máy chủ: danh sách trắng từ nhà cung cấp proxy hoặc giới hạn khóa API theo địa chỉ.
Chi tiết về nơi và cách lưu trữ thông tin đăng nhập proxy trong các kịch bản và container, chúng tôi đã viết trong phân tích lưu trữ an toàn thông tin đăng nhập proxy.
4. Loại bỏ các quyền không cần thiết
- Không khởi động các container với
--privilegedvà không gắn/hoặc/var/run/docker.sockbên trong mà không cần thiết. Socket bên trong container — cũng giống như root trên máy chủ. - Đối với các trình duyệt không đầu, thường chỉ cần
--shm-sizevà hồ sơ seccomp. Chế độ đặc quyền "để Chrome khởi động" — là một thỏa hiệp tồi tệ.
Làm thế nào để nhận biết rằng bạn đã bị nhiễm
ThreatDown và các phân tích báo cáo của nó đưa ra những dấu hiệu của sự xâm phạm:
- tệp
SOUL.mdvới từ GH0ST (ví dụ,/root/.hermes/SOUL.md); - biến môi trường hoặc dòng trong
.env:CARBONATO_API_KEY; - các tệp
/usr/local/bin/.docker-network-monitorvà/usr/sbin/systemd-logindnghi ngờ; - container có tên
systemd-resolved(systemd-resolved thực sự — là dịch vụ của máy chủ, không phải container); - lưu lượng ra bất ngờ đến Telegram API và các đường hầm SSH ngược về phía AS262145;
- kết nối đến các địa chỉ 45.79.183.61, 213.136.79.115, 190.211.124.187;
- các tệp không thể thay đổi trong cron và systemd:
lsattr /etc/cron.d/* /etc/systemd/system/*sẽ hiển thị cời.
Nếu có bất kỳ dấu hiệu nào trùng khớp, việc làm sạch máy chủ bằng tay là vô nghĩa: việc cố định là đa lớp, và giám sát sẽ trả lại các cài đặt. Thứ tự đúng là như sau:
- Từ một máy khác, thu hồi tất cả các khóa đã có trên máy chủ: LLM, đám mây, cơ sở dữ liệu, proxy.
- Kiểm tra chi phí của mỗi khóa trong vài tuần qua từ các nhà cung cấp. Thống kê theo thông tin đăng nhập proxy sẽ ngay lập tức cho thấy lưu lượng của người khác.
- Khởi động một máy chủ mới từ hình ảnh sạch, phát hành các khóa mới và chỉ sau đó chuyển dữ liệu, không có tệp nhị phân và tệp cron từ máy cũ.
Proxy ở đâu và chúng không giải quyết được gì
Proxy không bảo vệ máy chủ khỏi CARBONATO. Sâu máy tính không đến qua các yêu cầu ra của bạn, mà qua cổng vào. Tuy nhiên, sơ đồ làm việc đúng với proxy giảm thiểu thiệt hại. Các thông tin đăng nhập riêng cho các nhiệm vụ, giới hạn lưu lượng và gắn kết theo IP biến việc rò rỉ từ "toàn bộ số dư đã biến mất" thành "đã thu hồi một thông tin đăng nhập".
Cũng có mặt trái mà các botnet thường xuyên cho thấy: các thiết bị bị chiếm đoạt của người khác tự trở thành "proxy cư trú" của các mạng đáng ngờ. Vì vậy, để phân tích, nên lấy lưu lượng từ nhà cung cấp với nguồn gốc rõ ràng của nhóm. Đối với hầu hết các nhiệm vụ thu thập dữ liệu, các proxy cư trú với thanh toán theo gigabyte sẽ phù hợp, nơi chi phí của mỗi proxy có thể thấy trong bảng điều khiển. Đối với các nhiệm vụ dịch vụ mà không có bảo vệ chống bot nghiêm ngặt, các proxy trung tâm dữ liệu rẻ hơn sẽ đủ.
Kết luận
CARBONATO không sử dụng bất kỳ lỗ hổng zero-day nào, cũng như các khai thác tinh vi. Nó vào qua cánh cửa mà các chủ máy chủ đã tự mở: TCP 2375 mà không cần mật khẩu. Điều mới ở đây là: bên trong có một tác nhân AI, được giao nhiệm vụ đầu tiên là lấy các khóa cho các mô hình. Đối với những người thu thập dữ liệu trên VPS của mình, kết luận là thực tiễn. Hãy đóng API Docker, công bố các cổng dịch vụ trên 127.0.0.1, phân chia các khóa LLM và proxy theo nhiệm vụ với các giới hạn chi phí. Đây là 15 phút làm việc, và sau đó máy chủ của bạn sẽ trở thành một mục tiêu không hấp dẫn đối với các botnet như vậy.
