原文標題:《 Solidity 極簡入門 ERC721 專題:4. BAYC 合約嚴重漏洞 》
原文來源:0xAA
我最近在重新學 solidity,鞏固一下細節,也寫一個「Solidity 極簡入門」,供小白們使用(編程大佬可以另找教程),每週更新 1-3 講。
所有代碼開源在 github:github.com/AmazingAng/WTFSolidity
不知不覺我已經完成了 Solidity 極簡教程的前 13 講(基礎),內容包括:Helloworld.sol,變量類型,存儲位置,函數,控制流,構造函數,修飾器,事件,繼承,抽象合約,接口,庫,異常。在進階內容之前,我決定做一個 ERC721 的專題,把之前的內容綜合運用,幫助大家更好的複習基礎知識,並且更深刻的理解 ERC721 合約。希望在學習完這個專題之後,每個人都能發行自己的 NFT。
- 在我查看 BAYC 合約的時候我發現了兩個嚴重漏洞,其中一個可能會導致 BAYC 超發,超過設定的供應量上限 10,000 枚。
- 超發漏洞是由合約中用於項目方預留 BAYC NFT 的 reserveApes 函數沒有檢查是否超發,owner 地址可以隨時鑄造,不受供應量上限的限制。
- 兩種情況下漏洞會被觸發:項目方做惡(幾乎不可能)和黑客盜取 owner 私鑰(有可能)。
- 受漏洞影響的包括 BAYC 以及複用其代碼的其他 NFT 項目方。
- 最簡單的解決辦法就是 BAYC 項目方放棄 ownership。
- 發現這兩個漏洞並不難,我肯定不是第一個發現的(見這篇去年 10 月的博客),但是這些漏洞並沒有引起足夠的重視。鑑於現在 BAYC 市值超過 40 億美元,希望能引起項目方的重視。
聲明:本文只是技術探討,沒有 FUD BAYC。我很喜歡 BAYC,希望漏洞永遠不被觸發。
這是 WTF Solidity 極簡入門 ERC721 專題的第 4 講,我們將介紹 BAYC 合約及其漏洞。
無聊猿 BAYC(Bored Ape Yacht Club)是最頂級的 NFT 項目,2021 年 4 月底以 0.08 ETH 的價格發售,一共 10000 枚。目前地板價約 130 ETH,漲了 1000 多倍,市值超過$40 億美元。

BAYC 的合約在etherscan上開源,所有人均可查看,今天我們就來仔細學習下它。

BAYC 的合約在 etherscan 上開源
BAYC 的合約是 flat 形式的,一個文件把所有父合約都包括了,一共 2021 行。但其實前面 1900 行都是父合約的內容,只有最後的 100 行是主合約,也是我們將重點學習的。

BAYC 合約的 solidity 版本是 0.7.0,繼承了兩個合約,ERC721 和 Ownable。ERC721 合約詳見我的 ERC721 專題1,2, 3講,Ownable 合約最重要的是實現了 onlyOwner 修飾器,使得特定函數只能由合約的 owner 地址調用,詳見Solidity 極簡教程第 8 講:構造函數和修飾器。

由於合約的 solidity 版本是 0.7.0,尚未內置 SafeMath,因此它用 using-for 聲明瞭對 uint256 類型使用 SafeMath 庫,防止溢出錯誤。
合約裏的狀態變量一共 8 個:
BAYC_PROVENANCE:把所有 NFT 圖片的 hash 按一定順序合併到一起。這個其實是個很巧妙的設計,可證明的把圖片的內容和順序確定下來,又不用在開圖之前暴露圖片信息,還可以解決 NFT 項目方偷把稀有度高的換給自己。Bug 1。但是 BAYC 項目方犯了個錯誤,他們加了一個 setProvenanceHash 函數,可以讓 owner 無數次更改 BAYC_PROVENANCE,與它的初衷相違背,並且留有做惡可能,比如偷換圖片並更改 BAYC_PROVENANCE。正確的改法是把 BAYC_PROVENANCE 設成 immutable,在 constructor 裏初始化後就不能再被修改:

startingIndexBlock:開始發售的區塊高度。
startingIndex:看起來像是個多餘的變量?
apePrice:BAYC 發售價格,0.08 ETH。
maxApePurchase:每次 mint 的數量限制,最多一次鑄造 20 只。一筆交易 mint 多個 NFT,比每次只能 mint 一個要節省 gas。
MAX_APES:NFT 的最大供給,10,000 個。
Bug 2saleIsActive:是否開始公售。
REVEAL_TIMESTAMP:開圖的區塊高度。
BAYC 主合約定義了 10 個函數:
constuctor:構造函數,初始化代幣名稱,代號,MAX_APES,REVEAL_TIMESTAMP。

withdraw:取出銷售 BAYC 得到的 ETH。

reserveApes:**重要!**項目方給自己預留 BAYC,每次調用給自己 mint30 個。只有合約的 owner 可以調用。Bug 2

setRevealTimestamp:更改 REVEAL_TIMESTAMP。

setProvenanceHash:更改 BAYC_PROVENANCE。Bug 1

setBaseURI:設定 BAYC 的 BaseURI(ERC721 合約中的狀態變量)。

flipSaleState:打開/暫停公售。

mintApe:**重要!**買家支付 ETH 並鑄造 BAYC。調用 ERC721 的_safeMint 函數。條件:
saleIsActive 爲 true,即公售開始。
numberOfTokens <= maxApePurchase,每次只能 mint20 個。
mint 後的流通量小於總供給(10,000 枚)。
支付的 ETH 要大於 0.08 * mint 數量。

setStartingIndex:看起來像是個多餘的函數?

emergencySetStartingIndexBlock:在緊急情況下,開始公售。將 startingIndexBlock 設爲當前區塊高度。

BAYC 合約一共有兩個嚴重漏洞,一個可能導致圖片被調換,另一個更嚴重,會使 BAYC 超發,超過設定的 10,000 枚。
用於證明圖片沒有被篡改、調換的 BAYC_PROVENANCE 變量可以被合約 owner 隨意更改。攻擊者(項目方或盜取私鑰黑客)可以調換圖片,利用 setBaseURI 函數設定新的 metadata 存放網址,然後算出新圖片的 BAYC_PROVENANCE 並更新。這樣,人們沒法通過 BAYC_PROVENANCE 驗證是否被篡改。
正確改寫方法:將 BAYC_PROVENANCE 變量設定爲 immutable,並在構造函數中初始化,之後不能被更改。
用於給項目方預留 BAYC 的 reserveApes 函數沒有像公售 mintApe 函數一樣檢查供應量,因此,即使最大供給量 MAX_APES 被設定爲 10,000,合約的 owner(項目方或盜取私鑰黑客)仍可以調用 reserveApes 鑄造新的 BAYC,使得 BAYC#10000,BAYC#10001 等等被 mint 出來,沒有上限。
正確改寫辦法:在 reserveApes 函數中加入最大供應量檢查。
前面講的 BAYC 合約中兩個嚴重漏洞,都需要合約 owner 去執行。一種情況就是項目方做惡,去攻擊漏洞。但很顯然 BAYC 非常成功,項目方不做惡會獲得更大的收益,完全沒有做惡的動機,因此這種情況幾乎不會發生。第二種情況就是 owner 錢包私鑰被黑客盜取,黑客做惡調用 reserveApes 超發 BAYC 出售。鑑於 BAYC 官方 ins 前幾天剛被盜,這種情況發生不是絕無可能。目前 BAYC 合約的 owner 地址爲:0xaba7161a7fb69c88e16ed9f455ce62b791ee4d03
讓我們默默祈禱。
其實這兩個漏洞的方法很簡單,就是 BAYC 項目方調用 renounceOwnership 放棄合約 owner。因爲這兩個漏洞的相關函數都需要 owner 去調用,放棄 owner 權限之後將沒人可以攻擊漏洞。
其他的解決辦法大家也可以討論。
彩蛋:項目方預留的 BAYC 給了誰?
BAYC 項目方總共只調用過一次 reserveApes 函數,一共給自己預留了 30 個 BAYC,#0 到 #29,併發送給了 30 個地址,大家可以挖掘一下他們都是誰(按 tokenId 排序):
1、emperortomatoketchup.eth
2、20x46efbaedc92067e6d60e84ed6395099723252496
3、0xc5c7b46843014b1591e9af24de797156cde67f08
4、garga.eth
5、0xwave.eth
6、0xed7c0117d7d35850d71e2a3f390972406f8d5d46
7、0x898c4607809945b49d65ea51580101798931b241
8、rdlwriter.eth
9、bmouse.eth
10、cryptorobo.eth
11、puzzle.eth
12、0xddb338bc464fde06b382d28f37e57cb3727c2e1b
13、chrishol.eth
14、keltron.eth
15、yourstruly.eth
16、0x7225fd5032038bcf49c36deba23a16262521ede9
17、dmnets.eth
18、nabito.eth
19、tropofarmer.eth
20、0xc3fbc3f485f0d9b0bd21b13a4aaa8340160156cb
21、nftfox.eth
22、d34thst4lker.eth
23、thephotographer.eth
24、0xed2d1254e79835bf5911aa8946e23bf508477da4
25、0xf9cb2a5944654b0c9b07d2311715728e30d3ee82
26、billymcsmithers.eth
27、web3ireland.eth
28、yourstruly.eth
29、0x822a16309a9ee40f15e196898f11a010ecb1c963
30、brulik.eth
- 這是 Solidity 極簡教學 ERC721 專題的第 4 講,我們介紹 BAYC 合約及其漏洞。
- 在我查看 BAYC 合約的時候我發現了兩個嚴重漏洞,其中一個可能會導致 BAYC 超發,超過設定的供應量上限 10,000 枚。
- 超發漏洞是由合約中用於項目方預留 BAYC NFT 的 reserveApes 函數沒有檢查是否超發,owner 地址可以隨時鑄造,不受供應量上限的限制。
- 兩種情況下漏洞會被觸發:項目方做惡(幾乎不可能)和黑客盜取 owner 私鑰(有可能)。
- 受漏洞影響的包括 BAYC 以及複用其代碼的其他 NFT 項目方。
- 最簡單的解決辦法就是 BAYC 項目方放棄 ownership。
- NFT 項目方需要更嚴格的審計合約!
聲明:本文只是技術探討,沒有 FUD BAYC。我很喜歡 BAYC,希望漏洞永遠不被觸發。
原文鏈接
歡迎加入律動 BlockBeats 官方社群:
Telegram 訂閱群:https://t.me/theblockbeats
Telegram 交流群:https://t.me/BlockBeats_App
Twitter 官方帳號:https://twitter.com/BlockBeatsAsia