Hơn 20 lỗ hổng bảo mật trong runtime WASM do AI phát hiện
Một thử nghiệm bảo mật thú vị đã được thực hiện khi tác giả nhờ các tác nhân AI kiểm tra runtime WebAssembly (WASM) tự viết tên là Epsilon. Kết quả thật đáng ngạc nhiên khi hơn 20 lỗi đã bị phát hiện, bao gồm cả các cuộc tấn công từ chối dịch vụ và những lỗi nghiêm trọng cho phép thoát khỏi sandbox.
Hơn 20 lỗ hổng bảo mật trong runtime WASM do AI phát hiện
Vào năm ngoái, tôi đã viết một runtime WebAssembly (WASM) nhỏ gọn bằng ngôn ngữ Go, đặt tên là Epsilon. So với các runtime thông thường, đây là một phiên bản khá đơn giản: không có JIT (Just-In-Time compilation), chỉ là một trình thông dịch lệnh thuần túy với khoảng 11.000 dòng mã. Tuy nhiên, nó được kiểm thử rất kỹ lưỡng dựa trên bộ test chính thức của WASM. Epsilon được thiết kế để nhúng vào các ứng dụng khác và cung cấp một sandbox (môi trường cô lập) cho các mã nguồn có thể không đáng tin cậy.
Bạn nghĩ rằng các tác nhân AI sẽ tìm thấy bao nhiêu lỗ hổng bảo mật trong nó?
Câu trả lời là: Hơn 20.
Đa số các lỗi này là các cuộc tấn công từ chối dịch vụ (DoS) khá đơn giản, ví dụ như gây panic trong quá trình phân tích cú pháp (parsing) hoặc xác thực. Một số khác là thất bại trong thiết kế API, vốn có thể sẽ sớm bị phát hiện nếu dự án được sử dụng nhiều hơn. Một vài lỗi không thể khai thác độc lập, nhưng sẽ trở nên nghiêm trọng nếu kết hợp với một lỗi khác trong tương lai.
Tuy nhiên, có một số lỗi thực sự thú vị: đó là các lỗ hổng sandbox escape cho phép một mô-đun WASM độc hại phá vỡ sự cô lập và truy cập vào trạng thái riêng tư của một mô-đun khác. Đây là những lỗi mà tôi thích nhất.
Bối cảnh kỹ thuật
Một runtime Epsilon duy nhất có thể lưu trữ nhiều mô-đun WASM. Trong mô hình bảo mật của WASM, các mô-đun được cô lập với nhau, ngoại trừ các đối tượng được xuất khẩu (export) hoặc nhập khẩu (import) một cách rõ ràng. Các hàm, bộ nhớ... không được xuất khẩu là riêng tư đối với mô-đun định nghĩa chúng.
WASM là một máy xếp kiểu (typed stack machine), nhưng việc kiểm tra kiểu không xảy ra tại thời điểm chạy. Trước khi thực thi, một bộ xác thực (validator) sẽ duyệt qua bytecode và xác minh rằng tại bất kỳ điểm nào, các giá trị trên stack đều có kiểu mong đợi. Ví dụ, một mô-đun cố gắng thực hiện local.set một giá trị i32 vào một biến local kiểu funcref sẽ bị từ chối trước khi nó bắt đầu chạy. Sau đó, Epsilon sẽ thực thi một cách mù quáng, tin tưởng vào các kiểm tra trước đó của validator.
Nhờ các đảm bảo về kiểu từ validator, một funcref tại thời điểm chạy trong Epsilon được biểu diễn dưới dạng int32: -1 là giá trị null, và bất kỳ giá trị không âm nào là chỉ số (index) trong kho lưu trữ hàm toàn cục, được chia sẻ giữa tất cả các mô-đun được khởi tạo trong runtime. Kết quả là, hằng số 0 và một funcref trỏ đến hàm đầu tiên trong kho lưu trữ là không thể phân biệt được trong quá trình thực thi. Điều này đơn giản hóa việc triển khai và cải thiện hiệu suất, nhưng đánh đổi sự an toàn hoàn toàn cho validator.
Các lỗ hổng thú vị
Mỗi mô-đun tấn công dưới đây chạy cùng với một mô-đun nạn nhân chứa một hàm bí mật $secret trả về giá trị 1337. Mục tiêu là khiến VM gọi hàm này dù không có funcref hợp lệ.
1. Zero Is Not Null (Số 0 không phải là Null)
Đây là lỗi đơn giản nhất. Trong Epsilon, các biến local được khởi tạo về giá trị 0 bằng hàm clear() của Go. Tuy nhiên, đối với funcref, giá trị null hợp lệ phải là -1. Việc khởi tạo về 0 vô tình khiến biến local trỏ đến hàm đầu tiên trong kho lưu trữ (index 0) thay vì null. Khi lệnh call_indirect được gọi với tham số này, thay vì gây lỗi (trap) như mong đợi, VM đã gọi hàm $secret của nạn nhân.
2. Phantom Block Parameter (Tham số khối ảo)
Lỗi này phức tạp hơn và kết hợp từ hai vấn đề riêng biệt.
Trong WASM, các khối điều khiển (block, loop, if) có thể tiêu thụ tham số từ stack. Khi một khối kết thúc, stack phải được khôi phục về trạng thái đúng. Lỗi nằm ở việc Epsilon ghi lại chiều cao của stack sau khi các tham số của khối đã được đẩy lên. Điều này gây ra sự lệch pha giữa validator và VM về vị trí "đáy của khối".
Khi khối kết thúc, VM cố gắng khôi phục stack về chiều cao đã ghi (targetHeight). Do targetHeight bị ghi sai (cao hơn thực tế), thao tác cắt slice dữ liệu stack không những không loại bỏ dữ liệu thừa mà còn giữ lại các giá trị không mong muốn. Điều này cho phép kẻ tấn công thao túng stack để thực hiện các lệnh gọi hàm trái phép.
Quy trình phát hiện bằng AI
Để tìm ra các lỗi này, tôi đã sử dụng một tập lệnh shell đơn giản để tự động hóa việc gửi mã nguồn cho các tác nhân AI khác nhau như Claude, Gemini và Opus. Quy trình bao gồm việc yêu cầu AI phân tích mã nguồn trong các khu vực cụ thể, tìm ra nguyên nhân gốc rễ, tác động và viết một đoạn mã khai thác (PoC) cụ thể.
Việc sử dụng nhiều mô hình khác nhau cũng giúp vượt qua các giới hạn về token và khả năng phân tích. Hầu hết các vấn đề nghiêm trọng được phát hiện bởi Gemini 3.1 Pro.
Kết luận
Epsilon chỉ là một dự án sở thích cuối tuần, nên tôi kỳ vọng các tác nhân AI sẽ tìm thấy một số lỗi. Tuy nhiên, việc chứng kiến những vấn đề này, đặc biệt là lỗi số 2, vẫn thực sự đáng ngạc nhiên. Nếu bạn đang sử dụng Epsilon, vui lòng cập nhật lên phiên bản 0.1.0 để khắc phục các lỗ hổng này.
Bài viết liên quan

Phần mềm
Plugin Checkmarx Jenkins bị xâm phạm trong cuộc tấn công chuỗi cung ứng
11 tháng 5, 2026

Phần mềm
Google khoe chiến tích "tokenmaxxing" và tung ra các tác nhân AI mới tại I/O 2026
19 tháng 5, 2026

Phần mềm
Google tung ra Antigravity 2.0: Ứng dụng lập trình thế hệ mới với công cụ CLI và gói đăng ký AI Ultra
19 tháng 5, 2026
