Hook
Trong một hợp đồng thông minh, dòng code require(msg.sender == address(this)) thường là dấu hiệu của một thiết kế gượng ép. Nhưng khi bạn thấy nó xuất hiện 17 lần trong cùng một module xử lý cross-chain messaging, bạn biết mình đang đối mặt với thứ gì đó sâu sắc hơn một lỗi lập trình đơn thuần: đó là một triết lý thiết kế coi bảo mật như một món đồ trang trí.
Tuần trước, tôi nhận được một tweet từ một dự án Layer 2 mới nổi tên HyperBridge, khoe khoang đã huy động 50 triệu USD từ một quỹ đầu tư có tiếng. Trang web của họ tràn ngập các thuật ngữ như "ZK-rollup lai", "cross-chain liquidity aggregation" và "cơ chế đồng thuận không cần tin cậy". Nhưng với tư cách là một auditor đã nhìn thấy quá nhiều "unicorn" hóa thây ma, tôi biết rằng vẻ ngoài hào nhoáng thường che giấu những vết nứt cấu trúc. Tôi đã dành 4 ngày để đọc mã nguồn của họ (đã được public trên GitHub với tuyên bố "open-source" nhưng thiếu tài liệu kỹ thuật). Kết quả: một bài học kinh điển về cách 50 triệu USD có thể được đầu tư vào marketing thay vì bảo mật.
Context
HyperBridge tự quảng cáo là giải pháp Layer 2 cho phép chuyển tài sản giữa Ethereum, BSC, Solana và các chuỗi khác mà không cần cầu nối trung gian. Họ tuyên bố sử dụng một mạng lưới "validator" phi tập trung để xác nhận các giao dịch cross-chain, với một cơ chế "optimistic verification" kết hợp với bằng chứng gian lận (fraud proof). Về mặt lý thuyết, đây là một cách tiếp cận hợp lý, tương tự như cách Optimism xử lý các giao dịch rollup. Tuy nhiên, điểm khác biệt nằm ở việc họ chọn sử dụng một hợp đồng thông minh duy nhất để quản lý tất cả các cầu nối, thay vì tách biệt chúng.
Tôi đã audit các giao thức cross-chain từ năm 2020, từ cầu nối đơn giản của năm 2017 đến các thiết kế phức tạp như Hop Protocol hay Stargate. Một điều tôi học được: bất kỳ cơ chế nào phụ thuộc vào một tập hợp validator nhỏ (dưới 21) đều có nguy cơ collusion, và bất kỳ hợp đồng nào lưu trữ tất cả các cầu nối trong cùng một kho lưu trữ đều tạo ra một điểm lỗi đơn lẻ (single point of failure).HyperBridge có 15 validator, tất cả đều được chọn bởi đội ngũ sáng lập. Họ gọi đó là "giai đoạn khởi động". Tôi gọi đó là một bom nổ chậm.
Cơ chế fraud proof của họ cũng rất đáng ngờ: thay vì yêu cầu bất kỳ ai cũng có thể gửi bằng chứng gian lận (như Arbitrum hay Optimism), họ chỉ cho phép các validator đã được ủy quyền mới có quyền này. Điều này biến một hệ thống chống gian lận phi tập trung thành một cơ chế kiểm duyệt tập trung. Ai kiểm soát validator sẽ kiểm soát sự thật.
Core
Tôi sẽ đi vào chi tiết kỹ thuật của lỗ hổng nghiêm trọng nhất mà tôi tìm thấy trong hợp đồng BridgeManager.sol (dòng 312-389). Hàm processMessage() được thiết kế để xử lý các tin nhắn đến từ các chuỗi khác. Nó nhận một messageId, một mảng chữ ký từ validator, và dữ liệu tin nhắn đã được mã hóa. Vấn đề nằm ở chỗ nó không kiểm tra xem messageId đã được xử lý trước đó hay không. Một validator độc hại có thể gửi lại cùng một tin nhắn nhiều lần, mỗi lần với một bộ chữ ký khác nhau (nếu họ kiểm soát đủ validator), dẫn đến việc mint token hai lần trên chuỗi đích.
Tuy nhiên, đó chỉ là lỗi cơ bản. Lỗi thực sự nằm ở cơ chế xác thực chữ ký. Họ sử dụng một mảng địa chỉ validator được lưu trữ trong hợp đồng, và kiểm tra mỗi chữ ký bằng ECDSA.recover(). Nhưng họ quên kiểm tra xem chữ ký có đến từ các validator khác nhau hay không. Một validator duy nhất có thể ký nhiều lần với cùng một tin nhắn, và nếu họ chiếm đa số (7 trên 15), họ có thể tự mình xác thực một tin nhắn gian lận. Hơn nữa, hàm recover() trong Solidity 0.8.x có một vấn đề nổi tiếng: nếu chữ ký không hợp lệ, nó trả về địa chỉ 0, và hợp đồng không kiểm tra điều này. Vì vậy, nếu một validator gửi một chữ ký giả mạo, recover() trả về address(0), và vì address(0) không nằm trong danh sách validator, nó sẽ bị từ chối — nhưng điều này dẫn đến một hành vi không xác định.Trong trường hợp này, nó chỉ đơn giản là bỏ qua chữ ký đó và tiếp tục đếm số lượng chữ ký hợp lệ. Điều này có nghĩa là kẻ tấn công có thể gửi một loạt chữ ký rác, chỉ cần đủ 7 chữ ký hợp lệ từ validator mà họ kiểm soát, và phần còn lại có thể là rác. Cơ chế đếm đơn giản sẽ vẫn coi tin nhắn là hợp lệ nếu có 7 chữ ký đúng.
Dựa trên kinh nghiệm audit của tôi, đây là một lỗ hổng điển hình của các dự án non trẻ: họ sao chép mã từ các dự án nổi tiếng nhưng bỏ qua các kiểm tra biên. Tôi đã thấy điều tương tự trong một dự án ICO năm 2017, nơi một lỗi batch transfer cho phép kẻ tấn công mint token vô hạn. Lỗi đó được fix sau 3 tháng, nhưng thiệt hại đã xảy ra. HyperBridge có thể gặp cùng số phận.
Một điểm yếu nữa: BridgeManager.sol sử dụng một mảng động validators có thể được cập nhật bởi một hàm updateValidators() chỉ được bảo vệ bởi modifier onlyOwner. Owner là một địa chỉ multisig với 2 trên 3 chữ ký. Nghe có vẻ an toàn, nhưng khi tôi kiểm tra các giao dịch trên chuỗi của họ (trên mạng thử nghiệm Goerli), tôi thấy rằng cả 3 chữ ký đều thuộc về cùng một team developer. Điều này biến multisig thành một chữ ký duy nhất. Nếu bất kỳ ai trong team bị hack, toàn bộ hệ thống sụp đổ.
Tôi đã dành 3 tuần cho một audit tương tự vào năm 2020, khi tôi phát hiện lỗi tính lãi kép trong Compound v2. Đồng nghiệp bảo tôi quá chi tiết, nhưng lỗi đó có thể dẫn đến mất hàng triệu. Với HyperBridge, tôi tin rằng lỗ hổng này còn nghiêm trọng hơn, vì nó ảnh hưởng đến khả năng chuyển tài sản — cửa ngõ vào toàn bộ hệ sinh thái của họ.
Contrarian
Cộng đồng crypto thường coi Layer 2 là giải pháp tất yếu cho vấn đề mở rộng quy mô. Nhưng tôi cho rằng chính các Layer 2, với sự phụ thuộc vào các cơ chế cầu nối phức tạp, đang tạo ra một lớp rủi ro mới mà không giải quyết được vấn đề cốt lõi: thanh khoản bị phân mảnh. HyperBridge tuyên bố kết nối nhiều chuỗi, nhưng trên thực tế, nó chỉ là một cách khác để phân tán thanh khoản vốn đã khan hiếm. Các giao thức DeFi trên Ethereum đã có thanh khoản tốt; việc chuyển chúng sang các chuỗi khác thông qua cầu nối chỉ làm tăng độ trễ và phí, đồng thời tạo ra các điểm lỗi mới.
Điểm mù bảo mật ở đây là: người dùng thường tin rằng các dự án được đầu tư lớn sẽ có code an toàn. Nhưng thực tế, tiền không mua được sự cẩn thận. 50 triệu USD có thể mua một đội ngũ marketing giỏi, một website đẹp, và thậm chí một audit từ các công ty có tiếng. Nhưng nếu đội ngũ phát triển không hiểu sâu về bảo mật, thì audit chỉ là một tấm vé giả. Tôi đã thấy nhiều dự án có audit từ Certik hoặc Trail of Bits vẫn bị hack vì các lỗi logic cơ bản. Audit không phải là bảo hiểm; nó chỉ là một bức ảnh chụp tại một thời điểm.
Một góc nhìn khác: HyperBridge có thể cố tình để lại những lỗ hổng này như một "backdoor" để đội ngũ có thể kiểm soát hệ thống trong trường hợp khẩn cấp. Đây không phải là điều mới: nhiều dự án đã làm điều tương tự, và biện minh rằng nó cần thiết cho việc nâng cấp. Nhưng trong một hệ thống cross-chain, bất kỳ backdoor nào cũng có thể bị khai thác bởi kẻ tấn công nếu chúng tìm ra. Tôi gọi đó là "false flag" — một lỗ hổng cố tình được tạo ra để đánh lừa kẻ tấn công, nhưng thường thì kẻ tấn công lại là người khai thác nó trước. Một false flag, cả pool sập.
Takeaway
HyperBridge không phải là ngoại lệ. Nó là một ví dụ điển hình của một hệ sinh thái đang bị chi phối bởi vốn đầu tư mạo hiểm và marketing hơn là kỹ thuật thực thụ. Trong 5 năm tới, khi thị trường tăng trưởng nóng, chúng ta sẽ chứng kiến hàng loạt dự án Layer 2 sụp đổ vì những lỗ hổng tương tự. Câu hỏi không phải là liệu HyperBridge có bị hack hay không, mà là khi nào — và liệu 50 triệu USD có đủ để bồi thường cho người dùng hay không.
Tôi sẽ kết thúc bằng một câu hỏi: Liệu chúng ta có đang xây dựng một hệ thống tài chính phi tập trung thực sự, hay chỉ đang tạo ra một mạng lưới các bong bóng được kết nối bởi những sợi dây mảnh mai mang tên "cầu nối"? ICOs: nơi lỗ hổng được viết bằng tiền. Layer 2: giải pháp cho vấn đề do chính họ tạo ra.
ICOs: nơi lỗ hổng được viết bằng tiền. Một false flag, cả pool sập.