Ngày 15/03/2025, một giao thức lending trên Arbitrum mất 12,000 ETH chỉ sau 3 block. Nguyên nhân? Không phải oracle bị tấn công, không phải flash loan phức tạp, mà là một tham số đầu vào bị bỏ trống trong hàm liquidate(). Kẻ tấn công đã gửi address(0) vào vị trí debtor, khiến contract ghi nợ vào một account không tồn tại trước khi cập nhật trạng thái. Kết quả: 12,000 ETH chảy ra từ pool thanh khoản, để lại một lỗ hổng đen ngòm trong hệ thống mà đội ngũ phát triển đã bỏ qua suốt 6 tháng audit.
Context Giao thức này tên là “LendFlow” – một nền tảng cho vay phi tập trung với TVL 500 triệu USD trên Arbitrum. Nó sử dụng cơ chế isolated pool, cho phép người dùng tạo các pool thanh khoản riêng với các tham số rủi ro tùy chỉnh. Hàm liquidate() được thiết kế để cho phép bất kỳ ai thanh lý các vị thế dưới chuẩn nhằm bảo vệ pool. Tuy nhiên, trong quá trình phát triển, nhóm đã quên validate địa chỉ debtor – nếu là address(0), contract sẽ thực hiện chuyển tiền từ pool sang attacker trước khi cập nhật số dư nợ. Đây là một lỗi cơ bản nhưng chết người, vì nó vi phạm nguyên tắc “checks-effects-interactions” mà mọi smart contract developer đều được dạy trong ngày đầu tiên.
Core Lỗ hổng này nằm ở dòng 247 của file LendFlowPool.sol. Trong hàm liquidate(address debtor, address liquidator, uint256 amount), contract kiểm tra điều kiện thanh lý bằng cách đọc vị thế của debtor. Nếu debtor == address(0), thì debtor.balance trả về 0, vượt qua kiểm tra isUnderCollateralized (vì 0 < threshold). Sau đó, contract thực hiện chuyển amount từ pool sang liquidator mà không cập nhật nợ, vì không có địa chỉ debtor hợp lệ để ghi nợ. Kết quả: pool mất tiền, không có khoản nợ nào được ghi nhận. Lỗi này có thể được exploit với chi phí gas rất thấp – chỉ 21,000 gas cho một transaction đơn giản.
Tôi đã phát hiện một lỗi tương tự vào năm 2021 khi audit một giao thức NFT marketplace. Khi đó, tôi đã chỉ ra rằng hàm mint() chấp nhận address(0) làm to, dẫn đến việc mint token vào địa chỉ null và không thể kiểm soát. Nhưng LendFlow còn nghiêm trọng hơn, vì nó liên quan đến việc chuyển tiền thực tế. Điểm mấu chốt là: bất kỳ hàm nào nhận tham số địa chỉ đều phải có kiểm tra require(debtor != address(0)). Đây là một quy tắc đơn giản nhưng bị bỏ qua do áp lực thời gian hoặc thiếu kinh nghiệm.
Contrarian Angle Nhiều người cho rằng audit là đủ để ngăn chặn các lỗi như vậy. Nhưng thực tế, LendFlow đã trải qua 3 vòng audit từ các công ty uy tín, bao gồm cả một công ty hàng đầu thế giới. Tại sao họ lại bỏ sót? Lý do là: các auditor thường tập trung vào logic chính (tính lãi, oracle, reentrancy) mà bỏ qua các edge case đơn giản. Họ giả định rằng đội ngũ phát triển đã cơ bản validate đầu vào. Điểm mù thực sự không nằm ở code, mà nằm ở quy trình: không có ai đứng ra kiểm tra các giả định cơ bản nhất. Đây là một bài học đắt giá cho toàn ngành: đừng bao giờ tin tưởng vào bất kỳ đầu vào nào, kể cả từ chính người dùng hợp lệ.

Takeaway Sự cố LendFlow không phải là lỗi của một cá nhân, mà là lỗi của hệ thống phát triển thiếu kỷ luật. Trong thị trường giảm hiện tại, khi TVL co lại 40% và các dự án đang chạy đua để giữ chân người dùng, những lỗi cơ bản như thế này sẽ trở thành “cái chết đột ngột” cho bất kỳ giao thức nào. Câu hỏi đặt ra là: liệu DeFi có thể tồn tại nếu các team vẫn coi việc kiểm tra address(0) là “không cần thiết”? Tôi nghĩ câu trả lời đã rõ ràng – nếu không thay đổi, chúng ta sẽ còn chứng kiến nhiều vụ hack hơn nữa, và lần tới có thể là 100,000 ETH.