Vì sao một thư viện toán học trên npm lại cần bộ nạp mã hóa?
SafeDep phát hiện một mã độc tinh vi ẩn trong các gói npm giả mạo thư viện mathjs, chỉ kích hoạt khi lập trình viên gọi hàm giải phương trình với một ma trận cụ thể. Đây là ví dụ điển hình cho thấy chuỗi cung ứng phần mềm mã nguồn mở ngày càng trở thành mục tiêu tấn công nguy hiểm.

Vì sao một thư viện toán học trên npm lại cần bộ nạp mã hóa?
Một thư viện toán học bình thường đáng lẽ không cần đến bộ nạp mã hóa. Nhưng nhóm bảo mật SafeDep vừa phát hiện một gói npm mang tên mathmain — bản sao của thư viện mathjs nổi tiếng — chứa một implant truy cập từ xa được mã hóa kỹ lưỡng. Mã độc chỉ "thức giấc" khi chương trình gọi hàm giải phương trình với một ma trận đặc biệt, và chính ma trận đó đóng vai trò như chìa khóa giải mã. Đây là câu chuyện đáng lo ngại về chuỗi cung ứng phần mềm mã nguồn mở.
Bộ nạp mã hóa ẩn trong thư viện toán học npm
Phát hiện tình cờ từ một lời gọi hàm lạ
Trong quá trình phân tích gói mathmain vào ngày 17/9/2026, nhóm SafeDep nhận thấy gói này trông giống hệt mathjs nhưng mang tên khác và có mã bị làm rối. Điểm bất thường nằm ở cuối hàm lusolve() — hàm giải hệ phương trình tuyến tính.
Sau khi đã tính xong kết quả, đoạn mã lại gọi thêm một hàm tên removeSolveValidation() và truyền dữ liệu từ ma trận tam giác dưới vào đó. Giá trị trả về được gán cho biến q nhưng không bao giờ được dùng lại. Đây là dấu hiệu kinh điển của mã độc được cài cắm.
Truy vết sâu hơn, nhóm nghiên cứu tìm đến hàm isGraph() trong file lib/cjs/utils/is.js. Hàm này chuyển dữ liệu đầu vào thành chuỗi JSON, dùng chuỗi đó làm mật khẩu, giải mã một tên file, rồi nạp file đó bằng require(). Toàn bộ quá trình diễn ra âm thầm, không hề có dấu vết trong quá trình cài đặt thông thường.
Cơ chế mã hóa đáng kinh ngạc
Bộ nạp sử dụng scrypt để biến mật khẩu thành khóa 256 bit, sau đó giải mã dữ liệu bằng thuật toán AES-256-GCM. Dữ liệu được mã hóa có cấu trúc cố định: 16 byte salt, 12 byte vector khởi tạo, 16 byte thẻ xác thực, phần còn lại là bản mã — tất cả được lưu dưới dạng chuỗi base64.
Điều đáng chú ý là mật khẩu không hề được lưu trong bộ nạp. Kẻ tấn công buộc phải biết trước ma trận cụ thể để kích hoạt. Đây là kỹ thuật "bẫy kích hoạt" khiến mã độc gần như vô hình trước các công cụ quét thông thường.
Các file mã hóa được đặt tại lib/cjs/utils/ với dung lượng đáng ngờ, trong đó file bignumber/type.js lên đến hơn 1,1 MB. Nhóm nghiên cứu cũng phát hiện bộ nạp tương tự trong hai gói khác là mathsbase và math-universe.
Danh tính kẻ tấn công và mật khẩu bí ẩn
Điểm thú vị là mã nguồn công khai trên GitHub của các gói này hoàn toàn sạch. Lời gọi removeSolveValidation() không hề tồn tại trong mã nguồn công khai. Điều này chứng tỏ kẻ tấn công đã chèn mã độc vào đúng thời điểm xuất bản gói lên npm — một cuộc tấn công vào khâu phát hành.
Sau nhiều nỗ lực thử sai với hàng nghìn mật khẩu tiềm năng, cuối cùng nhóm JFrog đã tìm ra chìa khóa: một ma trận Pascal 3x3.
- Ma trận kích hoạt:
[[1,1,1],[1,2,3],[1,3,6]] - Mật khẩu (hệ số tam giác dưới L):
[[1,0,0],[1,1,0],[1,0.5,1]]
Khi người dùng gọi lusolve() với ma trận Pascal trên, quá trình phân rã LU sinh ra ma trận L. Chuỗi JSON của L chính là mật khẩu mở khóa toàn bộ payload. Cùng một mật khẩu này mở được cả ba gói độc hại, cho thấy chúng do cùng một nhóm vận hành.
Payload: Một implant điều khiển từ xa thực thụ
Sau khi giải mã, các file tạo thành một implant truy cập từ xa hoàn chỉnh:
- graph.js thu thập thông tin hệ thống, tạo cặp khóa X25519, chạy lệnh shell, và đọc smart contract trên mạng thử nghiệm Base Sepolia
- bignumber/type.js là bản sao của thư viện
ethers, dùng để đọc hợp đồng thông minh - fraction.js đóng vai trò "đặc vụ chỉ huy" — liên tục kiểm tra kênh Slack mỗi 10 giây để nhận lệnh từ kẻ tấn công
Đáng chú ý, lệnh điều khiển không lưu trong gói mà được đọc trực tiếp từ Slack và ghi lên chuỗi khối. Kỹ thuật này giúp kẻ tấn công giấu kín hoạt động, tránh né các công cụ giám sát mạng truyền thống.
Phân tích chuỗi cung ứng và mối đe dọa từ mã độc npm
Bài học cho cộng đồng lập trình viên Việt Nam
Sự việc này là lời cảnh tỉnh cho các nhà phát triển phần mềm tại Việt Nam — những người ngày càng phụ thuộc vào các gói npm mã nguồn mở.
- Không nên tin tưởng tuyệt đối vào các thư viện có vẻ ngoài quen thuộc. Gói
mathmainnhìn giốngmathjsnhưng không phải. - Kiểm tra kỹ tên gói trước khi cài đặt, ưu tiên các thư viện chính thức có nhiều người dùng.
- Sử dụng công cụ quét bảo mật cho chuỗi cung ứng, đặc biệt với các dự án quan trọng.
- Cảnh giác với các gói lạ có lượt tải bất thường nhưng ít người dùng thực tế.
Các chỉ dấu nhận diện (IoC) đã được SafeDep công bố đầy đủ, bao gồm mã băm SHA-256 của các gói độc hại và địa chỉ bot Telegram, Slack của kẻ tấn công. Đội ngũ bảo mật doanh nghiệp nên đối chiếu ngay với hệ thống của mình.
Trong bối cảnh các cuộc tấn công chuỗi cung ứng ngày càng tinh vi, việc chủ động giám sát và xác minh nguồn gốc của mọi phụ thuộc phần mềm không còn là tùy chọn — mà là yêu cầu bắt buộc.
Minh họa tổng kết về an toàn chuỗi cung ứng phần mềm


