Một dòng code bị bỏ quên trong contract finalizeWithdrawal() đã khiến 500 ETH bị rút sạch trong 3 block. Không phải lỗi oracle, không phải tấn công governance – chỉ là một hàm transfer() không được bảo vệ bởi modifier nonReentrant. Đây không phải lần đầu tiên cross-chain bridge dính lỗi reentrancy, nhưng SwanChain đã tự tin rằng kiến trúc của họ “khác biệt”. Họ sai.
SwanChain là một cầu nối optimistic dựa trên mô hình “cầu nối phi tập trung với xác thực đa chữ ký”. Ý tưởng: người dùng gửi ETH vào contract nguồn, sau đó các validator ký xác nhận để cho phép rút trên chuỗi đích. Contract đích có hàm finalizeWithdrawal() để người dùng claim token sau khi đủ chữ ký. Vấn đề: hàm này được viết theo pattern “gửi ETH → cập nhật trạng thái → emit event”. Nhưng ai cũng biết: trong Solidity, nếu bạn gửi ETH trước khi cập nhật trạng thái, kẻ tấn công có thể gọi lại hàm thông qua fallback.
Code mẫu đơn giản hoá: ``solidity function finalizeWithdrawal(bytes memory signature, address user, uint256 amount) external { require(validSignatures[msg.sender] == true, "Invalid signer"); // Sai: gửi ETH trước khi cập nhật (bool success, ) = user.call{value: amount}(""); require(success, "Transfer failed"); // Cập nhật trạng thái sau usedNonces[nonce] = true; emit WithdrawalFinalized(user, amount); } ` Kẻ tấn công deploy một contract với fallback nhỏ: gọi lại finalizeWithdrawal()` với cùng nonce (vì nonce chưa được đánh dấu là đã dùng). Mỗi lần gọi, contract đích gửi thêm ETH. Vòng lặp chỉ dừng khi gas hết hoặc balance cạn.
Điểm mù bảo mật: Đội ngũ SwanChain cho rằng việc kiểm tra chữ ký là đủ. Họ quên rằng reentrancy không đến từ việc giả mạo chữ ký, mà đến từ thứ tự thực thi. Đây là lỗi cổ điển từ 2016 (DAO attack), nhưng vẫn tái diễn vì các team non-technical thường tập trung vào crypto (chữ ký, hash) thay vì control flow. Góc nhìn phản trực giác: Nhiều người nghĩ cross-chain bridge an toàn hơn vì có nhiều lớp xác thực. Thực tế: càng nhiều lớp tương tác, attack surface càng lớn. Reentrancy trên cầu nối không chỉ đến từ contract đích, mà còn từ các oracle gửi message. Nếu một validator contract cũng có fallback, bạn có thể tấn công xuyên chuỗi. Trải nghiệm audit của tôi: Khi audit Uniswap v2 năm 2020, tôi thấy rõ pattern transfer() trước update state – nhưng Uniswap dùng transfer() từ token ERC20, không phải ETH, nên reentrancy ít nguy hiểm hơn. Trên SwanChain, họ dùng .call{value:}() – tương tự DAO attack. Tôi đã cảnh báo team, nhưng họ không fix kịp trước launch. Kết quả: 500 ETH mất sau 2 ngày mainnet. Takeaway: Lỗ hổng ở đây, không phải hype ở kia. Đọc contract đi, thay vì tin vào tweet. Trong 6 tháng tới, tôi dự đoán các lỗ hổng reentrancy trên cầu nối optimistic sẽ tăng 200% vì các team chạy đua TVL. Nhưng thực tế: chỉ cần thêm modifier nonReentrant và đảo thứ tự là đủ. Vậy mà họ không làm. Hợp đồng thông minh không bao giờ sai, chỉ là code chưa hoàn hảo.