Terraini

Cánh cửa sập trong batch mint: Khi reentrancy guard không đủ

Lý Việt Web3

Trong 7 ngày qua, ba dự án NFT marketplace nhỏ đã báo cáo tổn thất tổng cộng 2.4 triệu USD do một lỗ hổng reentrancy trong hàm batch mint. Sự kiện này không gây chấn động lớn trên thị trường, nhưng với tư cách là một người từng kiểm toán hợp đồng thông minh, tôi nhận thấy đây là một lời nhắc nhở âm thầm: những cạm bẫy tưởng chừng đã được giải quyết vẫn còn tồn tại, ẩn mình trong những dòng code tưởng chừng vô hại.

Hàm batch mint là một tính năng phổ biến trong các hợp đồng NFT, cho phép người dùng mint nhiều token trong một giao dịch duy nhất nhằm tiết kiệm gas. Cấu trúc điển hình của nó là một vòng lặp for gọi hàm _safeMint hoặc _mint cho từng token ID. Trong các dự án bị tấn công, họ đã sử dụng thư viện OpenZeppelin và đã bật ReentrancyGuard ở cấp độ hàm chính. Tuy nhiên, lỗ hổng lại nằm ở cách họ xử lý callback từ hợp đồng người nhận. Cụ thể, khi _safeMint được gọi, nó kiểm tra xem địa chỉ nhận có phải là hợp đồng không, và nếu có, nó sẽ gọi hàm onERC721Received của hợp đồng đó. Chính tại điểm này, kẻ tấn công có thể thực hiện cuộc gọi lại (callback) vào chính hàm batch mint đang chạy, bỏ qua guard chỉ bảo vệ một lần vào hàm chính.

Để hiểu rõ, chúng ta hãy xem xét một đoạn code đơn giản hóa. Giả sử có hàm batchMint(address to, uint256[] memory tokenIds) external nonReentrant { for(uint i; i. Bộ biến nonReentrant ngăn việc gọi lại batchMint trực tiếp, nhưng nó không ngăn được các cuộc gọi đến các hàm khác trong cùng hợp đồng, chẳng hạn như một hàm emergencyWithdraw không được bảo vệ. Kẻ tấn công có thể tạo một hợp đồng độc hại, trong onERC721Received của nó, nó gọi lại emergencyWithdraw để rút toàn bộ số dư ETH hoặc token. Đây là một kiểu tấn công reentrancy cổ điển, nhưng vẫn xuất hiện. Trong mỗi lần gọi lại, có một cánh cửa sập vô hình mà team phát triển đã bỏ quên.

Điều đáng nói là cả ba dự án đều đã sử dụng công cụ phân tích tĩnh như Slither để quét hợp đồng. Slither có thể phát hiện reentrancy cơ bản, nhưng nó thường bỏ sót các trường hợp gián tiếp: khi _safeMint gọi callback, và callback gọi một hàm khác trong cùng hợp đồng (không phải chính batchMint). Một số lập trình viên cho rằng việc thêm nonReentrant vào tất cả các hàm có thể thay đổi state là đủ, nhưng điều này tạo ra một ảo giác an toàn. Trong mỗi lần gọi lại, có một cánh cửa sập vô hình – lần này là hàm withdraw không được bảo vệ, hoặc một hàm setApprovalForAll có thể được kẻ tấn công lợi dụng.

Theo kinh nghiệm kiểm toán của tôi, giải pháp triệt để không chỉ là thêm nonReentrant ở mọi nơi, mà còn phải tuân theo mô hình Checks-Effects-Interactions: cập nhật state trước khi thực hiện bất kỳ cuộc gọi ngoài nào. Trong hàm batch mint, điều này có nghĩa là phải đánh dấu token là đã mint (bằng cách tăng _totalSupply, hoặc thêm vào mapping) trước khi gọi _safeMint. Nếu thực hiện đúng, khi callback xảy ra, token đã được mint và state đã thay đổi, khiến cho các nỗ lực gọi lại không thể thay đổi được gì thêm. Nhưng hầu hết các lập trình viên đều viết code theo thứ tự tự nhiên: mint trước, cập nhật state sau, vì họ nghĩ rằng nonReentrant đã đủ.

Một góc nhìn phản trực giác khác: thị trường thường xem reentrangy là vấn đề đã được giải quyết từ năm 2016 (DAO hack), và các công cụ hiện đại được cho là đã bắt được hết. Nhưng thực tế, mỗi khi có một abstraction mới như _safeMint, bề mặt tấn công lại mở rộng. Các đội ngũ nhỏ, thiếu kinh nghiệm có xu hướng copy-paste code từ các dự án nổi tiếng mà không hiểu sâu các giả định bảo mật. Họ thấy OpenZeppelin cung cấp ReentrancyGuard và nghĩ rằng chỉ cần import là xong. Họ không lường trước rằng _safeMint chính là một điểm vào (entry point) cho callback, và callback có thể nhắm vào bất kỳ hàm nào khác.

Trong mỗi lần gọi lại, có một cánh cửa sập vô hình. Câu này không chỉ là một ẩn dụ; nó là lời cảnh báo về sự phức tạp ẩn dưới các lớp trừu tượng. Khi một giao thức mới ra mắt với tính năng batch mint, tôi thường kiểm tra xem họ có xử lý callback an toàn không, chứ không chỉ dựa vào guard. Những gì xảy ra với ba dự án vừa qua là một minh chứng rõ ràng: ngay cả khi đã có guard, vẫn có thể có cánh cửa sập nếu bạn không kiểm soát thứ tự thực thi.

Cuối cùng, câu hỏi dành cho các nhà phát triển và người dùng: Liệu bạn có đang tin tưởng vào một lớp bảo vệ mà không hiểu rõ giới hạn của nó không? Khi thị trường đi xuống và vốn trở nên khan hiếm, những lỗ hổng như thế này sẽ trở thành điểm chảy máu chết người. Hãy nhìn vào code, không chỉ vào audit report.

Giá thị trường

BTC Bitcoin
$63,012.1 +0.09%
ETH Ethereum
$1,856.65 -0.53%
SOL Solana
$72.67 -0.57%
BNB BNB Chain
$577.3 -1.97%
XRP XRP Ledger
$1.07 +0.23%
DOGE Dogecoin
$0.0696 -0.76%
ADA Cardano
$0.1767 +4.43%
AVAX Avalanche
$6.23 -2.81%
DOT Polkadot
$0.7887 +3.45%
LINK Chainlink
$8.16 -0.40%

Sợ & Tham

27

Sợ hãi

Tâm lý thị trường

Lịch sự kiện blockchain

{{年份}}
15
04
halving Bitcoin Halving

Phần thưởng khối giảm xuống 3,125 BTC

10
05
upgrade Nâng cấp Ethereum Pectra

Tăng giới hạn validator và trừu tượng hóa tài khoản

28
03
unlock Mở khóa token Arbitrum

Giải phóng 92 triệu ARB

08
04
upgrade Solana Firedancer

Trình xác thực độc lập ra mắt trên mainnet

30
04
upgrade Nâng cấp Celestia Mainnet

Cải thiện hiệu quả lấy mẫu tính khả dụng dữ liệu

22
03
unlock Mở khóa Optimism

Lượng cung lưu hành tăng khoảng 2%

12
05
halving BCH Halving

Sự kiện giảm một nửa phần thưởng khối

18
03
unlock Mở khóa token Sui

Phần đội ngũ và nhà đầu tư sớm được giải phóng

🧮 Công cụ

Tất cả →

Chỉ số mùa altcoin

44

Mùa Bitcoin

Sự thống trị BTC Mùa altcoin

Theo dõi phí Gas

Ethereum 28 Gwei
BNB Chain 3 Gwei
Polygon 42 Gwei
Arbitrum 0.5 Gwei
Optimism 0.3 Gwei

Vốn hóa thị trường

Tất cả →
# Tiền điện tử Giá
1
Bitcoin BTC
$63,012.1
1
Ethereum ETH
$1,856.65
1
Solana SOL
$72.67
1
BNB Chain BNB
$577.3
1
XRP Ledger XRP
$1.07
1
Dogecoin DOGE
$0.0696
1
Cardano ADA
$0.1767
1
Avalanche AVAX
$6.23
1
Polkadot DOT
$0.7887
1
Chainlink LINK
$8.16

🐋 Theo dõi cá voi

🟢
0xe5aa...14f0
2 phút trước
Chuyển vào
653,775 DOGE
🟢
0x25be...d4a9
2 phút trước
Chuyển vào
44,940 BNB
🟢
0xca6e...69f5
5 phút trước
Chuyển vào
4,038.57 BTC

💡 Smart Money

0xa92b...7a04
Nhà giao dịch on-chain dày dặn
+$2.9M
76%
0x077c...e368
Nhà tạo lập thị trường
+$3.2M
90%
0x507a...a9c5
Bot chênh lệch giá
+$2.7M
81%