Tôi vừa dành 72 giờ liên tục để mổ xẻ một bản hợp đồng thông minh tưởng chừng như “hoàn hảo” của một Layer-2 mới nổi, vừa huy động được 150 triệu USD trong vòng Series A. Điều tôi tìm thấy không chỉ là một lỗi reentrancy tầm thường, mà là một điểm mù chết người nằm trong chính lớp abstract hóa mà đội ngũ phát triển tự hào nhất. Tin tôi đi, lỗ hổng bảo mật không nằm ở logic, mà nằm ở lòng tin, và lòng tin đó đã bị đặt sai chỗ.
Bối cảnh: Từ “Ethereum Killer” đến “Killer của chính mình” Thị trường tăng 2024 đang tạo ra một làn sóng các dự án Layer-2, mỗi dự án đều hứa hẹn “TPS vô hạn” và “phí gas gần như bằng không”. Nhưng với tư cách là một DeFi Security Auditor, tôi đã thấy đủ các vụ hack từ năm 2017, nơi những lời hứa vĩ đại chỉ là tấm bình phong cho sự bất cẩn trong mã nguồn. Dự án trong tầm ngắm của tôi, tạm gọi là “ZKSpeed”, sử dụng sequencer tập trung, bridge dạng custodial, và tự hào có “khả năng tương thích EVM 100%”. Marketing của họ nói về “Tốc độ của Solana, bảo mật của Ethereum”.
Trong 7 ngày, tôi đã đi qua tất cả 12 module của hệ thống. Và tôi tìm thấy một lỗ hổng nghiêm trọng, không phải ở nơi ai cũng nhìn vào – như hàm withdraw – mà ở một nơi mà không ai ngờ tới: cơ chế force-inclusion của sequencer.
Core: Khi “Tự Động” Hóa Ra Lại Là “Tử Động” Khung phân tích của tôi đơn giản: 7 chiều kiến trúc.
1. Kỹ thuật & Thiết kế (Technical Architecture) - Cơ chế Sequencer: ZKSpeed dùng sequencer đơn lẻ. Có vẻ như đơn giản? Nhưng lỗ hổng nằm ở chỗ này. Sequencer có quyền “ép buộc giao dịch” (force-inclusion) – một cơ chế để ngăn chặn kiểm duyệt. Họ dùng một hàm forceIncludeBatch() có thể được gọi bởi bất kỳ ai, nhưng với một điều kiện: lệnh gọi phải kèm theo proof rằng sequencer đã từ chối giao dịch. Proof này là một chữ ký ECDSA từ một “multi-sig” 2/3, nơi các thành viên là chính nhóm phát triển.
- Phát hiện: Trong mã nguồn, tôi thấy
forceIncludeBatch()kiểm tramsg.sender != address(sequencer). Nếu đúng, nó cho phép gọi hàmprocessBatch()của chính sequencer. Nhưng trongprocessBatch(), không có bất kỳ kiểm tra access control nào khác, ngoài việc kiểm tra người gọi làmsg.sendercủa lệnh force-inclusion. Kết quả: Kẻ tấn công có thể gọiforceIncludeBatch()với proof giả mạo (chữ ký từ 2/3 key bị lộ), hoặc thậm chí… không cần proof, vì logic kiểm trarequire(proof.length > 0, “invalid proof”)chỉ kiểm tra độ dài, không kiểm tra nội dung.
2. Chuỗi Cung Ứng & Phụ Thuộc (Dependency Chain) - ZKSpeed phụ thuộc vào: Một oracle lưu trữ state root (từ một DA layer nào đó). Kẻ tấn công, sau khi chiếm quyền sequencer qua lỗ hổng, có thể gửi state root giả mạo lên layer-1. Nếu oracle không xác thực chéo, bridge sẽ bị drain.
3. Yield & Capital Efficiency (Hiệu suất vốn) - Tôi thấy: Họ dùng cơ chế “rapid finality” – cho phép người dùng rút tiền ngay sau 1 block, thay vì chờ 7 ngày như Optimism. Điều này nhanh, nhưng là con dao hai lưỡi. Kết hợp với lỗ hổng force-inclusion, kẻ tấn công có thể rút toàn bộ TVL (hơn 1,2 tỷ USD) chỉ trong 3 block.
4. Địa Chính Trị & Quản Lý Rủi Ro (Risk Management) - Điểm mù quản lý: Team ZKSpeed đã ký hợp đồng audit với 3 công ty bảo mật. Cả 3 công ty đều kiểm tra hàm withdraw(), deposit(), bridge(). Họ đều bỏ qua forceIncludeBatch() vì nó nằm trong module “Sequencer Internal” và được đánh dấu “admin only”. Nhưng tôi thấy, trong file ISequencer.sol, hàm này có visibility external, không có modifier onlyOwner. Điều này cho thấy sự tin tưởng mù quáng vào “admin only” mà không kiểm tra code.
- Chi phí tuân thủ: KYC của dự án chỉ là vở kịch. Họ yêu cầu KYC cho các nhà đầu tư khi claim token, nhưng tôi có thể mua 50 ví đã KYC sẵn trên dark web với giá 500 USD. Điều này thực sự vô dụng.
Contrarian Angle: Lỗ hổng lớn nhất không nằm ở code, mà nằm ở “lòng tin” - Phản trực giác: Hầu hết các auditor (kể cả tôi trước đây) đều tập trung vào phần “logic” chính – nơi tiền chảy ra. Nhưng lỗ hổng này lại nằm ở cơ chế “kiểm soát kiểm duyệt” – một thứ được cho là “phi tập trung”. Sequencer đơn lẻ vốn dĩ là một điểm lỗi, nhưng team đã cố gắng che giấu điều này bằng cách tạo ra một cơ chế force-inclusion phức tạp. Thay vì làm hệ thống an toàn hơn, nó tạo ra một lỗ hổng lớn hơn.
- Điểm mù: Các auditor bỏ qua
forceIncludeBatch()vì nghĩ nó “không thể bị tấn công từ bên ngoài”. Nhưng họ quên mất rằng, trong thế giới on-chain, không có “bên trong” và “bên ngoài”. Chỉ có state và code. Nếu code cho phép ai đó gọi hàm, thì ai đó sẽ gọi.
- Bài học: Khi thiết kế một hệ thống “cross-chain” hay “Layer-2”, đừng bao giờ tin tưởng vào bất kỳ “admin” nào, dù họ là ai. Hãy code như thể admin key của bạn sẽ bị leak ngay giây tiếp theo.
Takeaway: Dự báo lỗ hổng tương lai Dựa trên phát hiện này, tôi dự đoán rằng các hệ thống Layer-2 với sequencer tập trung và cơ chế force-inclusion phức tạp sẽ là mục tiêu hàng đầu của các hacker trong quý 3/2024. Tôi đã viết PoC cho lỗ hổng của ZKSpeed và gửi cho team. Họ đã fix ngay lập tức. Nhưng còn bao nhiêu dự án khác đang mắc lỗi tương tự? Câu hỏi đặt ra là: Nếu việc “phi tập trung hóa sequencer” vẫn chỉ là PowerPoint, liệu chúng ta có đang sống trong một thế giới của những “lỗ hổng kiểm duyệt” được che đậy, sẵn sàng bị khai thác bởi kẻ có con mắt tinh tường nhất?