我最近在重新學solidity,鞏固一下細節,也寫一個“WTF Solidity極簡入門”,供小白們使用(編程大佬可以另找教程),每週更新1-3講。
這一講,我們將介紹智能合約的壞隨機數(Bad Randomness)漏洞和預防方法,這個漏洞經常在NFT 和GameFi 中出現,包括Meebits,Loots,Wolf Game等。
偽隨機數
很多以太坊上的應用都需要用到隨機數,例如NFT隨機抽取tokenId、抽盲盒、gamefi戰鬥中隨機分勝負等等。但是由於以太坊上所有數據都是公開透明(public)且確定性(deterministic)的,它沒有其他編程語言一樣給開發者提供生成隨機數的方法,例如random()。很多項目方不得不使用鏈上的偽隨機數生成方法,例如blockhash() 和keccak256() 方法。
壞隨機數漏洞:攻擊者可以事先計算這些偽隨機數的結果,從而達到他們想要的目的,例如鑄造任何他們想要的稀有NFT而非隨機抽取。更多的內容可以閱讀WTF Solidity極簡教程第39講:偽隨機數。
壞隨機數案例
下面我們學習一個有壞隨機數漏洞的NFT 合約: BadRandomness.sol。
contract BadRandomness is ERC721 { uint256 totalSupply; // 構造函數,初始化NFT合集的名稱、代號 constructor() ERC721(“”, “”){} // 鑄造函數:當輸入的 luckyNumber 等於隨機數時才能mint function luckyMint(uint256 luckyNumber) external { uint256 randomNumber = uint256(keccak256(abi.encodePacked(blockhash(block.number – 1), block.timestamp))) % 100; // get bad random number require(randomNumber == luckyNumber, “Better luck next time!”); _mint(msg.sender, totalSupply); // mint totalSupply++; }}
它有一個主要的鑄造函數luckyMint(),用戶調用時輸入一個0-99 的數字,如果和鏈上生成的偽隨機數randomNumber 相等,即可鑄造幸運NFT。偽隨機數使用blockhash 和block.timestamp 聲稱。這個漏洞在於用戶可以完美預測生成的隨機數並鑄造NFT。
下面我們寫個攻擊合約Attack.sol。
contract Attack { function attackMint(BadRandomness nftAddr) external { // 提前計算隨機數 uint256 luckyNumber = uint256( keccak256(abi.encodePacked(blockhash(block.number – 1), block.timestamp)) ) % 100; // 利用 luckyNumber 攻擊 nftAddr.luckyMint(luckyNumber); }}
攻擊函數attackMint()中的參數為BadRandomness合約地址。在其中,我們計算了隨機數luckyNumber,然後將它作為參數輸入到luckyMint() 函數完成攻擊。由於attackMint()和luckyMint()將在同一個區塊中調用,blockhash和block.timestamp是相同的,利用他們生成的隨機數也相同。
Remix 復現
由於Remix 自帶的Remix VM不支持blockhash函數,因此你需要將合約部署到以太坊測試鏈上進行複現。
-
部署BadRandomness 合約。
-
部署Attack 合約。
-
將BadRandomness 合約地址作為參數傳入到Attack 合約的attackMint() 函數並調用,完成攻擊。
-
調用BadRandomness 合約的balanceOf 查看Attack 合約NFT餘額,確認攻擊成功。
預防方法
我們通常使用預言機項目提供的鏈下隨機數來預防這類漏洞,例如Chainlink VRF。這類隨機數從鏈下生成,然後上傳到鏈上,從而保證隨機數不可預測。更多介紹可以閱讀WTF Solidity極簡教程第39講:偽隨機數。
總結
這一講我們介紹了壞隨機數漏洞,並介紹了一個簡單的預防方法:使用預言機項目提供的鏈下隨機數。 NFT 和GameFi 項目方應避免使用鏈上偽隨機數進行抽獎,以防被黑客利用。
推特:@0xAA_Science|@WTFAcademy_
社區:Discord|微信群|官網wtf.academy
所有代碼和教程開源在github: github.com/AmazingAng/WTFSolidity
來源:bress