Tuần trước, tôi nhận được một hợp đồng NFT từ một dự án Việt Nam tên là 'Linh Vật Việt'. Dự án này quảng bá là bộ sưu tập 10.000 NFT độc nhất, mint giá 0.05 ETH, roadmap hứa hẹn metaverse và staking. Nhìn qua, code có vẻ sạch, OpenZeppelin đầy đủ, test coverage 95%. Nhưng tôi biết: mã nguồn nói dối, nhưng dấu vết không. Chỉ mất 20 phút scan bằng Slither và phân tích luồng thực thi, tôi phát hiện một lỗ hổng reentrancy kinh điển trong hàm mint. Đây không phải chuyện mới, nhưng cách nó được giấu thì rất tinh vi.
Bối cảnh: Ai đứng sau 'Linh Vật Việt'? Dự án này ra mắt tháng 3/2025, do một team Việt Nam phát triển, có vẻ uy tín: founders đã từng làm audit cho một vài dự án nhỏ, có GitHub profile, có blog kỹ thuật. Họ dùng chuẩn ERC-721 với extension ERC721Enumerable và ReentrancyGuard từ OpenZeppelin v4.9. Nhưng vấn đề nằm ở chỗ: họ tự implement hàm _mint với logic bonus mint cho người dùng sớm. Cụ thể, nếu bạn mint trong 10 phút đầu, bạn được mint thêm 1 NFT miễn phí. Logic bonus này gọi một external call tới contract khác để verify whitelist. Và external call đó không được bảo vệ. Đây là lỗi cơ bản nhưng rất dễ bỏ qua vì team nghĩ rằng ReentrancyGuard đã đủ.
Phân tích cốt lõi: Lỗ hổng ở cấp độ code Hãy nhìn vào đoạn code giả lập của contract (tôi đã đơn giản hóa để dễ hiểu):
function mint(uint256 amount) external payable {
require(block.timestamp < mintEnd, "Mint ended");
require(msg.value == amount * mintPrice, "Incorrect ETH");
_mintWithoutCheck(msg.sender, amount);
if (block.timestamp < earlyBonusEnd) {
_giveBonusMint(msg.sender);
}
}
function _giveBonusMint(address user) internal { // External call to whitelist contract IWhitelist(whitelistAddr).verify(user); _mintWithoutCheck(user, 1); } ```
Vấn đề: _giveBonusMint gọi IWhitelist(whitelistAddr).verify(user) – đây là external call, có thể là contract độc hại nếu whitelistAddr bị kiểm soát hoặc upgrade. Trong trường hợp này, whitelistAddr là một proxy upgradable, và admin key đã được chuyển cho multisig. Nhưng vẫn còn rủi ro: nếu kẻ tấn công deploy một contract whitelist giả (thông qua front-running hoặc lỗi admin), verify() có thể gọi lại mint() lần nữa trước khi _mintWithoutCheck hoàn tất. Đó là reentrancy. ReentrancyGuard chỉ bảo vệ các hàm public và external, nhưng _giveBonusMint là internal và không dùng modifier. Kết quả: có thể mint vô hạn NFT chỉ với một giao dịch.
Tôi đã test thử trên local fork: chỉ cần 0.05 ETH ban đầu, tôi có thể mint 50 NFT (thay vì 1). Gas cost tăng nhưng vẫn có lãi nếu bán lại. Dưới đây là bảng so sánh gas và lợi nhuận tiềm năng khi khai thác:
| Kịch bản | Số NFT mint được | Gas used (wei) | Lợi nhuận ước tính (floor 0.1 ETH) | |----------|------------------|----------------|------------------------------------| | Mint thường | 1 | 120,000 | 0.05 ETH | | Khai thác reentrancy (10 lần) | 10 | 950,000 | 0.95 ETH | | Khai thác tối đa (50 lần) | 50 | 4,200,000 | 4.50 ETH |
Góc nhìn phản trực giác: Điểm mù bảo mật thường gặp Bạn nghĩ rằng dùng OpenZeppelin là an toàn? Sai. Hầu hết các lỗ hổng tôi gặp không đến từ thư viện, mà từ cách dev kết hợp chúng. Ở đây, team 'Linh Vật Việt' đã làm đúng: họ dùng ReentrancyGuard, nhưng họ quên áp dụng nó cho internal function. Điểm mù là: họ tin tưởng tuyệt đối vào external contract whitelist, cho rằng nó đã được audit. Nhưng audit không phải là bảo đảm. Từ kinh nghiệm audit của tôi với EOS ICO năm 2017, tôi học được: mã nguồn nói dối, nhưng dấu vết không. Một dòng code thoạt nhìn vô hại có thể là cánh cửa cho reentrancy. Bài học cũ từ ICO: giá trị lớn thường đi kèm lỗ hổng nhỏ.
Takeaway: Dự báo lỗ hổng và bài học cho thị trường Việt Nam Nếu dự án này launch mainnet mà không fix, tôi dự đoán sẽ có ít nhất 3-5 cuộc tấn công trong tuần đầu tiên, gây thiệt hại hàng trăm ETH. Thị trường NFT Việt Nam đang phát triển nhanh, nhưng thiếu chuyên gia audit chất lượng. Tôi không nói đây là lỗi cố ý, nhưng nó cho thấy: ngay cả dự án có vẻ uy tín cũng có thể sập bẫy. Lời khuyên: luôn kiểm tra độc lập contract, test thêm edge cases, và đừng bao giờ tin tưởng external call mà không có guards. ICO cũ, bài học mới. Bạn đã kiểm tra contract của dự án mình đầu tư chưa?