Tôi nhận được một hợp đồng Uniswap V3 fork vào tuần trước. Dự án vừa huy động 50 triệu USD, TVL đạt 200 triệu trong 48 giờ. Mở file .sol ra, tôi thấy ngay một hàm withdraw() không có reentrancy guard.
Đây là một dấu hiệu quen thuộc. Năm 2017, khi còn làm việc tại một quỹ đầu tư nhỏ ở Austin, tôi đã phát hiện lỗ hổng tương tự trong hợp đồng ICO của "Aragon One". Khi đó, tôi submit issue lên GitHub và yêu cầu đội phát triển vá lỗi trước khi triển khai. Họ nghe theo, và quỹ đã tránh mất 200 ETH.
Lần này, dự án có vẻ vội vàng. Họ copy code từ Uniswap V3, thêm tính năng flash loan, nhưng quên mất một điều cơ bản: reentrancy vẫn là kẻ thù số một.
Bytecode không bao giờ nói dối. Tôi chạy thử fuzz testing với foundry, chỉ sau 3 phút, test đã fail. Hàm withdraw() gọi lại chính nó qua một hợp đồng độc hại, rút hết thanh khoản. Audit? Tôi thích fuzz testing hơn, vì nó cho thấy thực tế, không phải báo cáo dài dòng.
Hãy nhìn vào mã nguồn:
function withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
(bool success, ) = msg.sender.call{value: amount}("");
balances[msg.sender] -= amount;
}
Dòng call ở giữa - nếu msg.sender là một hợp đồng, nó có thể gọi lại withdraw() trước khi balances được cập nhật. Đây là lỗ hổng cổ điển từ thời DAO hack 2016. Thế mà năm 2024, vẫn có dự án triệu USD mắc phải.
Sự thật phản trực giác: Các dự án lớn thường có bảo mật tồi tệ hơn các dự án nhỏ, vì họ tin vào thương hiệu thay vì code. Họ thuê audit từ các công ty lớn, nhưng audit chỉ là một phần. Tôi đã từng đọc báo cáo audit dài 50 trang, nhưng không một dòng nào đề cập đến reentrancy ở hàm withdraw. Tại sao? Vì auditor tập trung vào logic kinh doanh, không phải pattern an toàn cơ bản.
Hệ quả: Nếu lỗ hổng này bị khai thác, dự án sẽ mất toàn bộ TVL trong một block. Flash loan kết hợp reentrancy là combo chết người. Thị trường tăng đang che giấu những rủi ro này. Khi mọi người FOMO vào các dự án mới, họ quên kiểm tra code.
Takeaway: Đừng tin vào white paper, hãy đọc code. Nếu bạn không thể đọc Solidity, ít nhất hãy kiểm tra xem dự án có dùng OpenZeppelin ReentrancyGuard không. Một dòng import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; có thể cứu bạn khỏi mất tiền.
Trong thị trường tăng này, mỗi ngày có hàng chục dự án ra mắt. Và tôi cá rằng, ít nhất một trong số chúng có lỗ hổng reentranny. Câu hỏi là: bạn sẽ là người phát hiện ra nó, hay là nạn nhân?