NBA NFT合約漏洞深度分析:沒有白名單,科學家是怎麼輕鬆嚕光的?

ABMedia
分享
NBA NFT合約漏洞深度分析:沒有白名單,科學家是怎麼輕鬆嚕光的?

原標題:《深度解析:NBA的16進制合約漏洞是怎麼被科學家薅禿嚕皮的?》,發布於 4/21,本文經作者授權轉載。薅禿嚕皮,意指拔光至破皮損傷。

本文作者為陳劍 jason,來自微信公眾號「今天有更懂这个世界一点了吗」,大家可以關注公眾號查閱往期文章與作者進行深度交流,也可以關注作者的 Twitter jason_chen998

這是我目前寫過最精彩、最長、也是最辛苦的一篇文章,猶如破案一般抽絲剝繭為大家講清楚了科學家是怎麼薅到羊毛的,請一定完成全文閱讀,相信對你定會有很大收穫!

廣告 - 內文未完請往下捲動

今早一覺睡起來看群裡大家都在討論昨晚 NBA 帶給科學家的狂歡,據說有人直接 free mint 了 100 個,按照現在 0.4 ETH 的地板價也有一百萬人民幣了,原由是合約又出現了漏洞,所以來看看到底是怎麼回事。

NBA 於昨日發售了他們的 NFT 系列 The Association NFT,將 240 個球員各製作了 75 個 NFT,總共供應 18000 個,白名單持有者可以 free mint 1 個,這句話重點圈起來,之前的講過的 Gh0stlyGh0sts 的那篇文章也是 free mint,只需要繳納 gas 費就可以 mint 到坐等升值,在肉眼可見的獲利空間下如果能再發現點合約漏洞,比如繞過白名單或者繞過 mint 1 個的限制,那不得把羊毛薅禿嚕皮了,所以每當出現這種 NFT 項目都會有大量科學家盯著找漏洞,NBA 這次整了個王炸,白名單限制和 mint 1 個限制都被攻破了。

這次漏洞對於辛辛苦苦幹白名單的用戶也是很大的打擊,沒有白名單的能 mint 也就算了,還可以無限 mint,導致把有白名單的用戶名額都搶了,甚至很多用戶當初都是在場外花費幾千美金購買的白名單。並且在 mint 時湧進去一大堆科學家導致 gas 費飆升,白名單用戶也受了無妄之災被迫繳納更多 gas(畢竟白名單的目的之一就是避免 gas war),所以也有大量的用戶在維權哭訴,白名單用戶在 DC 哀嚎一片。

這的問題主要是兩個情況導致的:

  1. 用戶繞開官網,直接通過 matemask 用別人已經產生的 16 進制 Input Data 與合約直接交互。
  2. 合約對於 mint 的白名單校驗存在漏洞。

我們先來講第一個,先考大家一個知識,與智能合約進行交互 mint 的方式都有哪幾種?

首先大家肯定會說,不是在網站上點一個「mint」按鈕去調用智能合約來 mint 嗎?

這是最常規的也是所有項目方都希望我們進行的一種操作,大家應該都有蹲在屏幕前狂戳 mint 按鈕搶公售的經歷。

另外有經驗的同學會說,還可以直接打開區塊鏈瀏覽器如 etherscan,找到項目方的合約地址,在 read contract 和 write contract 裡對合約進行操作,很多同學都是通過這種方式搶公售的,因為你在官網裡點擊 mint,前端要再觸發請求去合約進行操作,合約再完成執行,而你直接在 etherscan 裡操作合約會跳過第一步,速度更快,對於這個方式不瞭解的可以看我之前的文章怎麼通過看懂 etherscan 瞭解 NFT 項目情況?

但是這種方式遇到兩種情況是行不通的,一種是項目方根本沒有開源它的合約,或者是項目方在合約接口裡必須要求你傳入一些如簽名等參數,而這種參數只能通過項目方的中心化服務器來生成,所以強制你必須要通過項目方的官網進行操作。

其實還有第 三 種,直接通過 matemask 錢包來和合約進行操作。

大家也許會對這一種很陌生,其實你已經在不知不覺中大量的使用了這種操作,轉帳的過程其實就是你用錢包直接交互合約的過程。

當我給某個地址轉錢時,首先點擊發送。

然後輸入收款地址,並輸入你要轉帳的金額。

這個過程大家已經非常熟悉了,這其實就是你與合約交互的過程,因為轉帳這種動作是我把錢轉給某個地址,該地址只需要收錢就好,不需要讓我輸入更多額外的信息,這時候你會問,mint 可不一樣呀,至少我需要輸入 mint 幾個,還可能包括白名單校驗等,這些參數怎麼輸入呢?

我們打開小狐狸錢包等設置,進入高級,然後下拉會看到有一個開關叫「顯示十六進制數據」,把它打開。

這個開關是幹嘛的呢?需要先為你講清楚什麼是十六進制數據。

你與任何一個合約交互都會為其輸入一定的數據,合約接收到這部分數據進行處理,這個數據定義了你要和合約的哪個接口函數交互,要給這個接口函數傳入什麼參數等等,這些數據都會以 16 進制的形式進行壓縮。

你打開 etherscan,隨意找到你曾經的一個交易記錄點擊進入詳情,然後一直下拉,你會在最下面看到  input data,右邊有一長串字符,這就是你當時與這個合約的函數交互時輸入的數據轉成 16 進制後的樣子。

關鍵是這一長串無規律的數字也壓根都不懂呀,先不急,這裡面是有規律的,雖然這次 NBA 的漏洞也不要求你讀懂 16 進制就可以直接用,但是本著求真的態度,我們還是要能理解這裡面的含義。

首先我們可以看到開頭的 0x 後面有幾位字符,再往後全是 0。

這幾位字符就代表著你調用的這個合約函數的編碼,每個函數會有一個自己的編碼。

我們打開一個合約的交易記錄,你會看到調用的方法中有的是如 mint、transfer 這種可以理解的文字,有的是 0x 開頭的編碼,因為 mint、transfer 這種操作函數是非常標準的,所以 etherscan 自動幫你把它們的編碼翻譯成了文字,但是有的函數是項目方自己開發的,所以只顯示其原始編碼。

我們在測試網試一下使用這種方式和合約直接進行交互的流程,我找到了之前部署的一套合約,然後點擊進入已經完成的 mint 交易詳情中。

然後拉到最下面複製當時的 input data。

將合約地址填寫進入後,並粘貼我剛才複製的 input data,點擊下一步。

然後到了繳納 gas 費的步驟。

我們可以點擊數據看一下,果然這裡功能類型是 mint,沒錯就是 mint 函數,說明我們成功的用之前的 16 進制 input data 調用到了合約。

然後點擊確認繳納 gas 費,到 etherscan上看一下,成功了!

好了到這裡我們已經很清楚的知道用已經存在的交易的 16 進制數據可以原模原樣的執行一次合約函數。

我們剛才講到這次問題出現的原因第二點是合約校驗白名單出現了漏洞。這個漏洞給了科學家用 16 進制手段薅到到機會。

接下來我們看合約 mint 到底有什麼問題,看過我之前的文章的讀者應該都清楚,在 mint 時一般都會經過幾層校驗,主要包括是否開啓 mint 校驗、數量校驗、白名單校驗等,如下圖 NBA 的 mint 合約所示,它有三層校驗:

batchNumber 是用來校驗第幾批 mint,這個不是我們今天要講的重點可以先略過。

重點是第二層的白名單校驗和第三層的 mint 數量校驗。

重點來了,第二層校驗用到了一個叫 verify 的函數,傳入了一個 info 參數,這是用來校驗當前用戶是否在白名單,問題就是出在了這裡。

在解釋白名單校驗的問題之前,我們有必要先瞭解一下常見的 2 種白名單校驗方式,在 NFT 行業初期,那時候很多項目的白名單都是一個個的錄入的,然後在 mint 時校驗一下當前用戶的地址是否能夠匹配到白名單,每錄入一個都要繳納一次 gas 費,成本投入極高,我周圍就有人僅錄入白名單就花費了數萬美金。

逐漸的有人意識到這種方法又貴又笨,於是採取了一種技術難度較高但更節省 gas 費的方式如梅克爾樹這種加密簽名驗證,它的原理就是將白名單不要存儲在鏈上合約中,而是放在鏈下由項目方自己保管,當用戶在官網 mint 時根據用戶的錢包地址用算法生成一個簽名,NBA 這次使用的就是加密簽名方式的校驗。

具體的加密原理和代碼實現方式講起來就太複雜了先略過,感興趣的可以自己去學習橢圓加密算法、梅克爾樹這些內容。我們先瞭解原理即可,我們現在只需要知道這個驗證的方法是需要輸入一個錢包地址,校驗該地址是否存在於白名單,如果存在則返回一個簽名。

這時候你可能會隱約覺得有問題,既然校驗的方式是輸入地址返回結果,那我如果用剛才說的 16 進制交互的方法,我把白名單用戶已經執行的交易 16 進制數據輸入進去,不就可以完成「白名單 mint」了嗎,這就相當於是指紋識別開門,你把有權限人的手指頭剁下來放在識別機上也一樣可以進門(有點殘忍)。

這裡有個問題是如果項目方又多加了一層校驗比如 mint 者地址和校驗白名單地址必須是一樣的,即你解鎖的手指頭和進門的人必須得是一體的,這個漏洞也就堵住了,可偏偏 NBA 還就沒有堵住。

我們再回顧一下代碼,先用 msg.sender 獲取了當前執行合約的用戶地址,在最終執行 mint 時傳入的是這個地址。

而在 mint 前校驗的是傳入一個叫 info 的參數,這個 info 的參數裡面包含的地址是官網獲取到操作官網用戶的地址後傳進來的。

明白問題所在了嗎?有白名單的張三在官網點了 mint,官網把張三的地址傳入了 info 後給到合約校驗通過,然後合約再執行 mint 時再獲取一遍當前正在執行的張三地址,把 NFT 轉給他,但是!但是沒有去判斷此張三是否為彼張三呀!他沒有去校驗這兩個地址是否為同一個。

所以我完全可以先找到張三的交易記錄,然後把他的 16 進制複製粘貼到錢包裡執行交易,這時候在校驗白名單時用的是張三的地址,所以會校驗通過,但是 mint 時用的是我的地址,因為是我正在和合約進行交互。

破案了!精不精彩!我把張三的手指頭砍下來進到了金庫!

我們看一下規規矩矩用白名單 mint 的用戶他們的 16 進制長什麼樣,可以看到這個地址他 mint 了 1 個 NFT 成功了。

然後我們看一下 16 進制,有兩個關鍵點,數字 1 代表著他 mint 了1個,下面的地址就是他自己的地址,對上了。

我們再看一個昨天傳遍科學家圈的一次擼了 69 個的巨能擼乾了點啥,以下為他的地址和 mint 的 100 個 NFT。

我們來看一下他的 16 進制都是什麼,先看到 45,這是什麼意思呢?這不是代表著數量嗎?可不是 1 也不是 64 呀,因為 16 進制的 45 轉為 10 進制就是 69,所以這位巨能擼把 1 改成了 45 從而擼擼 64個。

你會疑惑不是說有限制每人只能 1 個嗎?這裡就又是個漏洞,我們看到代碼確實有限制,mint 時你的持有量不能超過 1,但是我 mint 的時候是 0 呀,我 mint 一萬個這個限制也管不住我….不知道這個合約工程師在想啥。

接下來我們回到 16 進制數據,看那個合約地址,你會發現和巨能擼的對不上,說明巨能擼是把他的手指頭砍下來解鎖了,我們來看看是哪個倒霉蛋。

進入他的地址看到這小子也挺有錢帳上躺著 30 個以太,被擼了也不算虧。

然後看到他的交易記錄確實在昨晚 mint 了一個 NBA,看來巨能擼就是拿著他的 16 進制開鎖的。

這應該是我寫過最精彩的一篇文章,猶如破案一般層層抽絲剝繭為大家講清楚了科學家是怎麼把羊毛薅乾淨的,也是我寫過最累的一篇,耗時整整 5 個小時,寫作不易,如果對你有用麻煩大家多轉發擴散哈。

我之前的文章中多次強調,web3 的世界因為其開源性使得作惡空間很多,當然科學家的行為是否是作惡有待商榷,但是作為項目方一定是有責任保障自己用戶利益的,需要對技術有敬畏之心,代碼 review 一定要嚴謹。