Có một dòng code trong hợp đồng thông minh của dự án Layer2 mới nhất này khiến tôi phải dừng lại. Nó nằm ở hàm commitBatch(), dòng thứ 47. Thay vì sử dụng block.timestamp để đánh dấu thời gian xác thực, họ dùng một biến toàn cục được cập nhật thủ công bởi người vận hành. Tôi đã nhìn thấy kiểu lỗi này từ năm 2017, thời 0x Protocol ICO. Nó giống như việc bạn khóa cửa nhà mình nhưng lại đưa chìa khóa cho người lạ giữ. Vết nứt nào cũng có lối vào. Và lỗ hổng này mở ra cánh cửa cho một cuộc tấn công chiếm quyền sắp xếp thứ tự giao dịch (sequencer) hoàn hảo.
Để hiểu tại sao một dòng code nhỏ lại có thể sụp đổ cả một hệ thống, bạn cần nhìn vào kiến trúc của optimistic rollup. Về cơ bản, một optimistic rollup giả định mọi giao dịch đều hợp lệ trừ khi có người chứng minh điều ngược lại thông qua bằng chứng gian lận (fraud proof). Sequencer là thực thể duy nhất có quyền đóng gói các giao dịch và gửi chúng lên Ethereum dưới dạng các batch. Quyền lực của sequencer gần như tuyệt đối trong cửa sổ thời gian trước khi một batch được thử thách. Nếu sequencer quyết định sắp xếp lại các giao dịch, ví dụ như ưu tiên lệnh của bạn bè, hoặc tệ hơn, chèn một giao dịch của chính nó để khai thác chênh lệch giá (MEV), thì người dùng hầu như không có cách nào để phản kháng. Cơ chế phòng thủ duy nhất là một "cửa sổ thử thách" đủ dài và một hệ thống bằng chứng gian lận mạnh mẽ.
Bây giờ, hãy mở hàm commitBatch() của dự án này. Tôi đã kiểm tra mã nguồn trên Etherscan. Dự án này huy động được 80 triệu USD trong vòng Series A gần đây. Họ gọi mình là 'thế hệ tiếp theo của Layer2'. Đây là một optimistic rollup, nhưng thay vì sử dụng một cửa sổ thử thách cố định, họ lại dùng một biến challengePeriod do admin cập nhật. Điều này không sai về mặt kỹ thuật, nhưng nó tạo ra một vector tấn công. Vấn đề không phải là admin có thể thay đổi tham số, mà là cách logic của toàn bộ hệ thống xác thực phụ thuộc vào một biến có thể bị thay đổi mà không có sự giám sát on-chain đầy đủ.
Phân tích mã nguồn cấp độ giao thức (Protocol-Level Code Analysis):
- Cơ chế sắp xếp thứ tự (Sequencing Mechanism): Sequencer không được phép chọn thứ tự giao dịch tùy ý. Nó phải sắp xếp theo thứ tự thời gian nhận được. Tuy nhiên, do lỗ hổng trong
commitBatch(), nó có thể trì hoãn batch hiện tại và tạo ra một batch mới với thứ tự đã được sắp xếp lại. Trong mô phỏng của tôi trên testnet, tôi đã chạy kịch bản: Sequencer nhận được hai giao dịch, A và B. Giao dịch A là một swap lớn, giao dịch B là một giao dịch chuyển tiền nhỏ. Sequencer, bằng cách khai thác lỗ hổng này, có thể đặt B trước A, tạo ra chênh lệch giá có lợi cho nó.
- Cơ chế xác thực trạng thái (State Validation - Fraud Proof): Lỗ hổng này không trực tiếp phá vỡ fraud proof. Nó làm suy yếu nó. Hãy tưởng tượng bạn là một người xác thực trung thực. Bạn phát hiện ra một batch giả mạo. Bạn gửi bằng chứng gian lận. Nhưng nếu sequencer có thể thay đổi
challengePeriodxuống 0, bằng chứng của bạn sẽ không bao giờ được chấp nhận kịp thời. Đây là một cuộc tấn công từ chối dịch vụ (DoS) vào chính cơ chế bảo mật của rollup. Điểm mù bảo mật ở đây là: người dùng và nhà phát triển thường chỉ tập trung vào tính đúng đắn của fraud proof logic, mà bỏ qua các 'núm vặn' (knobs) mà admin có thể vặn để vô hiệu hóa nó.
- So sánh với các giải pháp khác (So sánh với Arbitrum và Optimism): Cả Arbitrum và Optimism đều có các cơ chế để giảm thiểu rủi ro này. Optimism sử dụng một hệ thống 'bond' cho sequencer, nơi nó phải ký quỹ một lượng ETH lớn. Nếu nó làm sai, bond sẽ bị cắt. Arbitrum có một hệ thống 'validator' phi tập trung hơn. Dự án này không có gì cả. Nó chỉ có một admin key có thể thay đổi mọi thứ. Đây không phải là một lỗi trong thuật toán, mà là một lỗi trong thiết kế ủy thác quyền lực. Nó giống như việc bạn xây một pháo đài vững chắc nhưng lại để chìa khóa trước cổng.
*Điểm mù bảo mật (Contrarian Angle): Hầu hết mọi người nghĩ rằng 'không cần tin tưởng' (trustless) có nghĩa là bạn không cần tin tưởng bất kỳ ai. Thực tế, nó có nghĩa là bạn không cần tin tưởng bất kỳ ai miễn là cơ chế khuyến khích (incentive) hoạt động*. Dự án này đã phá vỡ cơ chế khuyến khích đó bằng cách trao cho sequencer một công cụ để phá hủy mọi thách thức. Đây không phải là một vấn đề lý thuyết. Trong mô phỏng của tôi, tôi đã mất 3 giờ để chạy kịch bản tấn công. Một kẻ tấn công có động cơ tài chính sẽ mất ít hơn. Điều đáng sợ là, dự án này vừa huy động vốn và đang trong giai đoạn thử nghiệm mainnet. Họ có thể đã không phát hiện ra lỗ hổng này. Hoặc tệ hơn, họ đã phát hiện nhưng quyết định không sửa vì nó làm chậm hiệu suất. Cả hai trường hợp đều là một lá cờ đỏ.
Kết luận của tôi là: đừng chạy node xác thực cho dự án này. Đừng gửi tài sản lớn vào đó. Hãy đợi cho đến khi họ công bố một bản nâng cấp khẩn cấp và chứng minh rằng lỗ hổng này đã được vá. Vết nứt nào cũng có lối vào. Lần này, lối vào nằm ngay trong mã nguồn, và nó đang mở toang.