- Sau khi phát hiện mức sử dụng CPU bất thường trên máy chủ Hetzner do cá nhân vận hành, tác giả điều tra và phát hiện chương trình đào tiền mã hóa Monero (xmrig) đang chạy
- Nguyên nhân là container công cụ phân tích Umami chứa lỗ hổng thực thi mã từ xa của Next.js (CVE-2025-66478) đã bị tấn công
- May mắn là container đó chạy bằng người dùng không phải root (non-root) và không có host mount hay leo thang đặc quyền, nên cuộc xâm nhập bị giới hạn bên trong container
- Kẻ tấn công đã lợi dụng lỗi giải tuần tự không an toàn trong server component của Next.js để thực thi payload độc hại và cài đặt trình đào
- Trường hợp này cho thấy tầm quan trọng của quản lý dependency và cấu hình bảo mật container, và tác giả đã tăng cường các biện pháp bảo mật như bật tường lửa, siết chặt SSH và cập nhật định kỳ
Sự cố bị hack và phản ứng ban đầu
- Nhận được email cảnh báo từ Hetzner rằng máy chủ đang quét mạng bên ngoài
- Kèm theo thông báo rằng nếu không xử lý trong vòng 4 giờ thì máy chủ có thể bị chặn
- Sau khi SSH vào để kiểm tra, tác giả phát hiện một tiến trình dùng 819% CPU cùng nhiều tiến trình
xmrig tại đường dẫn /tmp/.XIN-unix/javae
- Xác nhận rằng việc đào tiền mã hóa đã diễn ra khoảng 10 ngày
Điều tra đường xâm nhập
- Tất cả tiến trình độc hại đều chạy dưới người dùng UID 1001, trùng khớp với container Umami
- Phát hiện tệp thực thi trình đào trong thư mục
/app/node_modules/next/dist/server/lib/xmrig-6.24.0/
- Lệnh thực thi có chứa địa chỉ mining pool
auto.c3pool.org:443 và khóa người dùng
Lỗ hổng Next.js và cách thức tấn công
- Nguyên nhân là lỗ hổng giải tuần tự React Server Components của Next.js (CVE-2025-66478)
- Khi kẻ tấn công gửi một yêu cầu HTTP đã bị chỉnh sửa, mã tùy ý có thể được thực thi trên máy chủ
- Kết quả là có thể cài đặt và chạy trình đào tiền mã hóa
- Tác giả từng nghĩ rằng “mình không trực tiếp dùng Next.js”, nhưng sau đó mới nhận ra Umami được xây dựng trên Next.js
Xác nhận khả năng cô lập của container
- Xác nhận rằng
/tmp/.XIN-unix/javae không tồn tại trên filesystem của host
- Đây chỉ là hiện tượng tiến trình của container xuất hiện trong đầu ra
ps của Docker, còn thực tế vẫn được cô lập
- Kết quả
docker inspect
- Người dùng:
nextjs
Privileged: false
Mounts: không có
- Vì vậy mã độc không thể truy cập host, đăng ký cron, tạo dịch vụ hệ thống hay cài rootkit
Khôi phục và tăng cường bảo mật
- Dừng và xóa container Umami bị nhiễm, sau đó mức sử dụng CPU trở lại bình thường
- Kích hoạt tường lửa UFW để chỉ cho phép SSH, HTTP và HTTPS
- Báo cáo kết quả điều tra cho Hetzner và ticket được đóng trong vòng 1 giờ
Bài học và điểm cần cải thiện
- Câu nói “tôi không dùng X” không bao gồm toàn bộ dependency
- Cần kiểm tra các công cụ mình dùng được cấu thành từ stack công nghệ nào
- Hiệu quả của việc cô lập container đã được chứng minh
- Người dùng non-root, chế độ không đặc quyền và không dùng volume không cần thiết đã ngăn thiệt hại lan rộng
- Sự cần thiết của phòng thủ nhiều lớp (Defense in Depth)
- Tường lửa, fail2ban, giám sát và cập nhật định kỳ là bắt buộc
- Nhấn mạnh tầm quan trọng của tự viết Dockerfile và giảm thiểu quyền của container
Các biện pháp sau sự cố
- Triển khai lại Umami lên phiên bản mới nhất và kiểm tra toàn bộ container bên thứ ba
- Rà soát người dùng chạy, mount, thời điểm cập nhật và mức độ cần thiết
- Chuyển sang xác thực bằng SSH key, vô hiệu hóa đăng nhập bằng mật khẩu, cấu hình fail2ban
- Tăng cường giám sát bằng Grafana và Node Exporter, áp dụng ngay các bản cập nhật bảo mật
Kết luận
- Do lỗ hổng Next.js của Umami, máy chủ đã bị lợi dụng để đào Monero suốt 10 ngày, nhưng
nhờ khả năng cô lập của container và cấu hình chạy non-root nên thiệt hại bị giới hạn
- Qua trải nghiệm này, tác giả đã thấm thía tầm quan trọng của việc nắm rõ dependency, cấu hình bảo mật và quản lý cập nhật
- Sự cố được xử lý trong khoảng 2 giờ và trở thành một trường hợp kiểm chứng hiệu quả thực tế của bảo mật container
1 bình luận
Ý kiến trên Hacker News
Trước đây người ta hay bật UFW, nhưng giờ tôi khuyên dùng firewalld
UFW càng về sau càng khó quản lý, còn firewalld dùng cấu hình dựa trên XML nên ổn định hơn nhiều
Có thể dùng lệnh
firewall-cmdđể cấu hình SSH, HTTPS, cổng 80, v.v., và nên dùng backend nftablesVới Docker, khá thường gặp trường hợp nó mở cổng bằng cách vượt qua các quy tắc tường lửa, nên sẽ an toàn hơn nếu đặt
StrictForwardPorts=yestrong/etc/firewalld/firewalld.conf8080:8080, mà nên bind vào IP riêng như192.168.0.1:8080:8080Tôi chạy Docker trên VM 10.0.10.11, chỉ cho truy cập qua WireGuard và đặt reverse proxy bằng Caddy nên khá tiện
Malware sẽ cố kết nối ra ngoài tới mining pool hoặc máy chủ C2, nên phải chặn quyền truy cập mạng của các binary không được phép
Việc gỡ quyền thực thi khỏi
/tmp,/var/tmp,/dev/shmcũng hữu íchnftables.confthôi cũng đã đủ đơn giản và rõ ràngiptables thực tế đã bị loại bỏ, nên không nhất thiết phải có thêm lớp như firewalld
Có vẻ đây là vấn đề liên quan đến “React2Shell CVE-2025-55182”
Thật lạ khi một lỗ hổng đã ảnh hưởng hơn 1 năm mà gần như không được chú ý
Nếu là web app triển khai bằng Next.js trong 12 tháng qua thì rất có thể đã là một phần của botnet
Thật bực khi cả ngành chỉ đưa ra lời khuyên kiểu “cứ dùng Docker đi”, “bật tường lửa lên”
Dạo này tôi ngày càng hoài nghi hệ sinh thái frontend, đến mức đang cân nhắc chuyển sự nghiệp sang C++
Hiện tại tổ hợp Next.js, React, Tailwind, Postgres đã gần như thành chuẩn suốt 5 năm qua
So với thời kỳ framework mọc loạn xạ cuối những năm 2000 đến đầu 2010 thì giờ ổn định hơn nhiều
Nếu ghét trào lưu và thay đổi thì mảng phát triển AI còn biến động nhanh hơn rất nhiều
Backend cứ dùng stack công nghệ vững chắc như .NET, Java, Go, còn frontend thì chọn tự do
Làm vậy sẽ giảm CVE và bớt mệt mỏi vì công nghệ hơn
Thảo luận GitHub liên quan
Nếu giới hạn mức dùng CPU của container Docker bằng
--cpus="0.5", thì dịch vụ lỗi hoặc miner sẽ không ảnh hưởng tới toàn bộ hệ thốngVận hành máy chủ mà không có tường lửa là một lựa chọn khá liều lĩnh
Nếu dùng thêm tường lửa bên ngoài của Hetzner thì khi lỡ cấu hình sai vẫn có thêm một lớp phòng vệ
Tôi chỉ cho phép SSH từ IP nhà mình, còn khi cần truy cập từ bên ngoài thì tạm mở qua website của Hetzner
Điều thực sự quan trọng là không chạy phần mềm có lỗ hổng RCE
Docker trông như vị cứu tinh thật ra phần lớn chỉ là do may mắn
Thay vào đó có thể dùng bastion host và kiểm soát vào/ra bằng HTTP proxy, nhưng cấu hình khá phức tạp
Chỉ riêng việc dùng cổng không chuẩn thôi cũng đã hiệu quả một cách bất ngờ
Không chắc có hiệu quả rõ rệt không, nhưng giống như một chiếc ô tâm lý vậy
Tôi từng thắc mắc nếu chạy container Docker bằng root thì có thể tấn công lên cả host không
Nếu muốn chạy mã không đáng tin cậy thì nên dùng VM (KVM/QEMU) hoặc công nghệ như gVisor(https://gvisor.dev/), Firecracker(https://firecracker-microvm.github.io/)
Nên hiểu Docker không phải sandbox, mà chỉ là môi trường thực thi được cô lập
Cấu hình mặc định của Docker không giới hạn RAM, CPU, dung lượng đĩa nên cũng dễ bị tấn công DoS
Hơn nữa, nhiều hướng dẫn còn khuyến nghị các tùy chọn nguy hiểm như
--privilegedhoặcCAP_SYS_PTRACECũng có thể khởi chạy container mới rồi leo thang lên quyền root
Docker hiện vẫn chưa để mặc định, nên nếu không cấu hình thì nguy cơ escape rất lớn
Trước đây phần lớn các vụ container escape đều có thể được chặn bằng user namespace
Nếu máy chủ không xử lý dữ liệu nhạy cảm thì chỉ cần dọn lại container cũng đã đủ
Để giảm bề mặt tấn công, những dịch vụ không cần công khai thì không nên phơi ra ngoài Internet
Ví dụ, các công cụ phân tích chỉ nên cho truy cập qua WireGuard hoặc SSH SOCKS proxy
Tôi cũng từng bị nhiễm Monero miner trên máy chủ Hetzner
May là nó chỉ xảy ra bên trong container LXC của Incus, và vì độ ưu tiên CPU thấp nên tôi không nhận ra
Một SSH key đã được thêm vào tài khoản root và một agent quản trị từ xa đã được cài vào
Cuối cùng tôi bỏ luôn container đó, nhưng cũng rút ra được vài bài học
Bài viết có chứa nội dung AI ảo giác chưa được con người biên tập lại
Nó liên tục khẳng định đây là lỗ hổng liên quan đến Puppeteer nhưng thực tế không phải vậy
Gần đây Monero miner lợi dụng lỗ hổng React 19 đang được cài khắp nơi
Tôi cũng gặp đúng vấn đề này
Nó hoạt động như một kiểu bug bounty tự động, tức là giúp báo cho bạn biết có lỗ hổng
Nếu giám sát mạng hoặc tiến trình tốt thì có thể phát hiện khá dễ
Nhờ đó mà tôi có dịp tốt để nâng cấp hệ thống backup
Nhìn những trường hợp như vậy làm tôi thấy mình thật đúng khi không tự vận hành VPS
Trước đây tôi đã thử rồi, nhưng cảm giác mình chỉ ở mức quản trị viên bình thường, rất khó duy trì bảo mật tốt
Nên giờ giao cho chuyên gia làm và trả tiền còn khiến tôi yên tâm hơn nhiều