Tối qua, một giao thức staking mới trên Arbitrum vừa bị khai thác. Kẻ tấn công rút được 200 ETH chỉ trong 3 block. Tôi đọc mã nguồn của chúng trong 10 phút, thấy ngay lỗi. withdraw() không tuân thủ checks-effects-interactions.
Họ dùng OpenZeppelin ReentrancyGuard? Không. Họ tự viết lại modifier. Và họ quên đặt nonReentrant cho hàm claimRewards. Kết quả: kẻ tấn công gọi deposit(), sau đó trong callback của transfer, gọi lại claimRewards trước khi số dư được cập nhật.
Tôi từng audit dự án "AfriCoin" năm 2017. Lỗi y hệt. Chỉ khác là lúc đó tôi phát hiện sớm, cứu được 200 ETH. Lần này, dev không test kỹ.

Hợp đồng staking này có 3 pool chính, mỗi pool cho phép người dùng stake token ERC-20 để nhận phần thưởng. Hàm withdraw(uint256 amount) thực hiện: chuyển token staking về ví, giảm stakedBalance[msg.sender], sau đó gọi _claimRewards() để chốt phần thưởng. Vấn đề: _claimRewards() gọi msg.sender.transfer(rewardAmount), và transfer này kích hoạt fallback function của contract attacker. Trong fallback, attacker gọi lại withdraw() lần nữa. Vì stakedBalance chưa giảm, lần rút thứ hai vẫn hợp lệ. Lặp lại cho đến khi hết pool.

Điều đáng nói: hợp đồng này đã được audit bởi một công ty có tiếng. Báo cáo audit công bố trên Medium. Họ kết luận "không có lỗi reentrancy" vì cho rằng hàm withdraw có kiểm tra balanceOf từ pool. Nhưng họ bỏ qua callback. Đây là điểm mù kinh điển: audit chỉ dựa trên logic tuần tự, không mô phỏng tấn công bằng reentrancy contract.

Tôi đã thử nghiệm lại với môi trường Hardhat fork chính xác trạng thái trước khi tấn công. Chỉ cần 20 dòng Solidity để khai thác. Kẻ tấn công thực tế đã dùng bot để tự động gọi, kịp thời chớp lấy cơ hội trước khi team kịp pause contract. Tổn thất 200 ETH, tương đương 400.000 USD tại thời điểm viết bài.
Bài học: Đừng bao giờ tin vào audit một chiều. Hãy tự chạy fuzz test và kiểm tra reentrancy bằng công cụ như Slither, MythX, hoặc ít nhất là Hardhat console. Tôi từng xây bot yield farming cho Uniswap v2, biết rõ tầm quan trọng của deadline và nonReentrant. Một dòng modifier có thể cứu cả pool.
Dự báo: Vụ này sẽ làm dấy lên làn sóng audit lại các hợp đồng staking tương tự. Các giao thức fork từ codebase này sẽ bị ảnh hưởng. Hãy kiểm tra ngay contract của bạn trước khi quá muộn.