Hook
Vào ngày 11 tháng 5 năm 2026, một giao dịch bất thường xuất hiện trên block explorer của ZKSwap – một ZK-Rollup AMM đang được kỳ vọng. Số dư của pool chính giảm 18.000 ETH trong vòng 3 block. Tôi đã fork repo của họ ngay lập tức và phát hiện một lỗ hổng trong logic xác minh proof. Đây không phải là một reentrancy điển hình, mà là một vấn đề ở cấp độ giao thức: verifier contract không kiểm tra tính toàn vẹn của public input khi proof được submit qua sequencer. Hãy cùng trace execution path.

Context
ZKSwap là một trong những DEX đầu tiên sử dụng công nghệ ZK-Rollup để giao dịch với phí thấp. Họ triển khai proof system dựa trên PLONK, với mục tiêu đạt throughput 2.000 TPS. Giao thức hoạt động với sequencer tập trung để sắp xếp giao dịch, sau đó gửi batch proof lên Ethereum mainnet. Từ tháng 3 năm 2026, TVL của họ đã đạt 1.2 tỷ USD, chủ yếu từ các pool thanh khoản ETH/USDC và WBTC/ETH. Sự kiện này xảy ra ngay sau khi họ nâng cấp contract verifier lên phiên bản 2.1.0, với mục đích tối ưu gas cost.

Core
Nếu bạn đọc kỹ whitepaper của ZKSwap, họ tuyên bố rằng sequencer không thể gian lận vì proof được xác minh trên mainnet. Nhưng thực tế, lỗ hổng nằm ở chỗ sequencer có thể chọn public input tùy ý mà không bị ràng buộc bởi trạng thái on-chain. Trong phiên bản cũ, contract verifier có một mảng publicInputs được hash từ các tham số giao dịch. Phiên bản mới đã loại bỏ bước hash này để tiết kiệm gas, nhưng lại quên kiểm tra rằng publicInputs phải tương ứng với dữ liệu giao dịch thực tế. Kết quả: sequencer có thể tạo proof cho một batch chứa giao dịch giả mạo, chuyển token từ pool sang ví của chính nó, và submit proof hợp lệ lên mainnet. Insight ở cấp độ giao thức mà hầu hết mọi người bỏ lỡ là: việc tối ưu gas bằng cách giảm số lượng public input đã phá vỡ giả định bảo mật cốt lõi của ZK-Rollup: tính toàn vẹn của trạng thái được xác minh phải include tất cả dữ liệu giao dịch.
Tôi đã phân tích mã nguồn contract verifier trên Etherscan (phiên bản 0x8f…). Trong hàm verifyBatchProof, có một tham số _publicInputs kiểu uint256[]. Code mới chỉ gọi plonkVerifier.verifyProof(proof, _publicInputs) mà không kiểm tra xem _publicInputs có khớp với batchHash của block đã commit trước đó hay không. Trong phiên bản cũ, họ có một modifier onlyValidInputs nhưng đã bị xóa trong bản nâng cấp. Lịch sử commit kể một câu chuyện khác: commit message ghi “Optimize gas by removing redundant checks” – một quyết định tưởng chừng vô hại nhưng đã mở ra cánh cửa cho kẻ tấn công.
Kẻ tấn công (có thể là sequencer hoặc ai đó chiếm quyền kiểm soát sequencer) đã tạo một batch chứa 18.000 ETH chuyển từ pool vào ví riêng. Họ tạo proof cho batch này với public input là một giá trị bất kỳ (ví dụ: 0x1). Contract verifier xác nhận proof hợp lệ, và số tiền được giải phóng. Trong 7 ngày qua, giao thức đã mất 40% LP, nhưng khối lượng giao dịch vẫn cao – một dấu hiệu cho thấy kẻ tấn công đã chuẩn bị kỹ lưỡng.

Contrarian
Điểm mù của cộng đồng là họ cho rằng ZK-Rollup an toàn tuyệt đối vì proof được xác minh on-chain. Nhưng thực tế, bảo mật của ZK-Rollup phụ thuộc vào việc tất cả dữ liệu cần thiết để xác minh phải được đưa vào public input một cách đầy đủ. Nếu bất kỳ dữ liệu nào bị bỏ qua, sequencer có thể nói dối. Nhiều dự án ZK-Rollup đang chạy đua tối ưu gas đã cắt giảm các bước kiểm tra, tạo ra lỗ hổng tương tự. Đây không phải lỗi của ZK, mà là lỗi của implementation. Nếu bạn nhìn vào merkle tree của trạng thái, bạn sẽ thấy rằng root hash của batch không được so sánh với root hash do sequencer gửi – một thiếu sót nghiêm trọng.
Takeaway
Sự kiện này đặt ra câu hỏi: Liệu thị trường có đang định giá sai rủi ro của ZK-Rollup? Tôi tin rằng trong vòng 6 tháng tới, sẽ có ít nhất 3 dự án ZK-Rollup khác bị tấn công theo kiểu tương tự. Các nhà đầu tư nên kiểm tra mã nguồn của verifier contract trước khi deposit thanh khoản. Và cộng đồng cần một tiêu chuẩn bảo mật cho ZK-Rollup, tương tự như OpenZeppelin cho smart contract. Nếu không, ‘ZK’ sẽ trở thành từ ngữ bị thị trường thổi phồng, che giấu những điểm yếu cơ bản.
Phân tích theo các khía cạnh (tương tự bài gốc)
1. Bảo mật kỹ thuật
| Subcategory | Analysis | Confidence | |------------|----------|------------| | Lỗ hổng hợp đồng | Thiếu kiểm tra public input so với batch hash | High | | Tấn công proof | Sequencer có thể tạo proof với public input tùy ý | High | | Tác động | Mất 18.000 ETH, tương đương ~50 triệu USD tại thời điểm | High |
2. Tác động thị trường
| Subcategory | Analysis | Confidence | |------------|----------|------------| | Giá token ZKS | Giảm 70% trong 24h | Medium | | TVL | Từ 1.2B xuống 200M | High | | Tâm lý nhà đầu tư | Hoảng loạn, rút thanh khoản hàng loạt | Medium |
3. Phản ứng đội ngũ
| Subcategory | Analysis | Confidence | |------------|----------|------------| | Thông báo | Xác nhận hack, tạm dừng giao thức | High | | Biện pháp | Đang phối hợp với bảo mật để truy vết | Medium | | Kế hoạch | Sẽ nâng cấp contract và audit lại | Low |
4. Rủi ro hệ thống
| Subcategory | Analysis | Confidence | |------------|----------|------------| | Ảnh hưởng đến Layer2 | Niềm tin vào ZK-Rollup giảm | Medium | | So sánh với Optimistic | Có thể thúc đẩy sự chú ý vào Optimistic Rollup | Medium |
Kết luận: Sự cố này là một lời nhắc nhở rằng công nghệ ZK không tự động đảm bảo an toàn. Việc audit và kiểm tra mã nguồn là quan trọng hơn bao giờ hết. Dựa trên kinh nghiệm audit của tôi, tôi khuyên các dự án nên có một lớp kiểm tra bổ sung cho public input ngay cả khi nó làm tăng gas.
Tags: ["ZK-Rollup", "Security", "DeFi", "Audit", "Layer2", "Hack"]