白帽駭客 samczsun:針對 NFT 資產的攻擊會越來越頻繁

買賣虛擬貨幣

注:原文作者是擁有“審計上帝”之稱的白帽駭客samczsun,同時他也是Paradigm的研究合夥人,其最近出手拯救了BitDAO MISO荷蘭拍賣資金池中的3.5億美元資產(https://www.8btc.com/article/6675485),而在這篇文章中,他提醒了關於NFT代幣標準的潛在安全風險,他還預測稱,隨著ERC-721和ERC-1155代幣標準變得越來越流行,針對NFT的攻擊很可能會越來越頻繁。

如果你從事軟體工程方面的工作,很可能你聽說過至少一條軟體工程原則。雖然我不主張嚴格遵守每一條原則,但有一些確實是值得關注的。

我今天要講的就是最小驚訝原則,它有一個奇特的名字,但卻是一個非常簡單的想法。它所說的是,當呈現聲稱要做某件事的程式碼時,大多數使用者都會假設它是如何完成這件事的。因此,作為開發人員,你的工作是編寫符合這些假設的程式碼,這樣你的使用者就不會感到意外。

這是一個很好的原則,因為開發人員喜歡對事物進行假設。如果你匯出一個名為calculateScore(GameState)的函式,很多人就會假設該函式只會從遊戲狀態中讀取。如果你還改變了遊戲狀態,你會使得很多人面臨困惑的狀態,他們試圖弄清楚為什麼他們的遊戲狀態會隨機被破壞。即使你把它放在文件中,仍然不能保證人們會看到它,所以最好首先確保你的程式碼不會令人驚訝。

“6小時的除錯工作,可以為你們節省5分鐘的文件閱讀時間。”

1

越安全越好,對嗎?

早在 2018 年初,當ERC-721 標準被起草出來時,有人就提出了實施轉賬安全性‌的建議,以確保代幣不會被卡在不用於處理代幣的接受者合約中。為此,提案作者修改了transfer函式的行為,以檢查接收方是否能夠支援代幣轉賬。他們還引入了unsafeTransfer函式,如果傳送者願意,該函式將繞過這個檢查。

然而,由於擔心向後相容性,這個函式在隨後的提交中被重新命名了。這使得ERC-20 和 ERC-721 代幣的transfer函式表現相同。但是,現在需要將接收方檢查轉移到其他地方。因此,標準作者就引入了safe類函式:safeTransfer 以及 safeTransferFrom。

這是一個關於正當性問題的解決方案,因為有許多 ERC-20 代幣被意外轉移到從未期望收到代幣的合約的例子(一個特別常見的錯誤是將代幣轉移到代幣合約中,將其永久鎖定)。而在起草 ERC-1155 標準時,提案作者從ERC-721標準汲取了靈感,不僅在轉賬時,而且在鑄造(mint)也納入了接收方檢查,這一點也不足為奇。

在接下來的幾年裡,這些標準大多處於休眠狀態,而 ERC-20代幣標準保持了它的流行狀態,而最近gas成本的飆升,以及社羣對NFT興趣的增強,自然而然導致開發者越來越多地使用ERC-721和ERC-1155代幣標準。有了這些新的興趣,我們應該慶幸這些標準的設計考慮了安全性,對嗎?

2

越安全越好,真的嗎?

Ok,但對於轉帳和鑄造來說,安全意味著什麼呢?不同的當事人對安全有不同的解釋。對於開發人員來說,一個安全函式可能意味著它不包含任何bug或引入額外的安全問題。而對於使用者來說,這可能意味著它包含額外的護欄,以保護他們不被意外射中自己的腳。

事實證明,在這種情況下,這些函式更多的是後者,而較少會是前者。這是特別令人遺憾的,因為在transfer和safeTransfer函式之間進行選擇時,你為什麼不選擇安全的那個函式呢?名字都體現出來了!

好吧,其中的一個原因可能是我們的老朋友reentrancy(可重入性),或者我一直在努力將其重新命名為:不安全的外部呼叫。回想一下,如果接收方是攻擊者控制的,則任何外部呼叫都可能不安全,因為攻擊者可能會導致你的合約轉換為未定義狀態。根據設計,這些“安全”函式執行對代幣接收者的外部呼叫,通常在鑄造或轉移期間由傳送者控制。換句話說,這實際上是不安全外部呼叫的教科書示例。

但是,你可能會問自己,如果允許接收方合約拒絕他們無法處理的轉賬,那最壞的後果是什麼?好吧,讓我透過兩個案例研究來回答這個問題。

3

例子1: Hashmasks

Hashmasks是一個供應有限的 NFT頭像專案,使用者每次交易最多可以購買 20 個mask NFT(儘管它們已經售罄數月了)。下面是購買mask的函式:

你可能覺得這個函式看起來非常合理。然而,正如你可能已經預料到的,在_safeMint 呼叫中隱藏著一些險惡的東西。讓我們來看看。

為了安全性,這個函式對token的接受者執行了一次callback回撥,以檢查他們是否願意接受轉賬。然而,我們是token的接收者,這意味著我們剛剛得到了一次callback回撥,在這個點上我們可以做任何我們想做的事情,包括再次呼叫mintNFT函式。如果我們這樣做,我們將在僅鑄造了一個mask後重呼叫該函式,這意味著我們可以請求再鑄造另外19個mask。這導致最終鑄造出了39個 mask NFT,儘管規則允許鑄造的最大數量只有20個。

4

例子2: ENS域名封裝器

最近,來自ENS 的Nick Johnson聯絡了我,他想讓我看看他們正在進行的ENS域名封裝器工作。這個域名封裝器允許使用者用新的ERC-1155 token代幣化他們的ENS域名,這提供了對細粒度許可權以及更一致的 API 的支援。

概括地說,為了封裝任何ENS域名(更具體地說,除了 2LD.eth 之外所有的ENS域名),你必須首先批准域名封裝器以訪問你的ENS域名。然後,你呼叫wrap(bytes,address,uint96,address),它既為你鑄造一個ERC-1155 token,也負責管理底層的ENS域名。

下面就是這個wrap函式,它相當簡單。首先,我們呼叫_wrap,它執行一些邏輯並返回雜湊域名。然後,我們確保交易傳送方確實是ENS域名的所有者,然後再接管該域名。請注意,如果傳送方不擁有底層的ENS域名,則整個交易應還原,撤銷在_wrap 中所做的任何更改。

下面是_wrap函式本身,這裡沒有什麼特別的。

不幸的是,正是這個_mint 函式,它可能會給毫無戒心的開發者帶來可怕的驚喜。ERC-1155 規範規定,在鑄造token時,應諮詢接收者是否願意接受該token。在深入研究庫程式碼(該程式碼庫根據OpenZeppelin的基礎稍作了修改)後,我們發現情況確實如此。

但這到底對我們有什麼好處呢?好的,我們再一次看到了一個不安全的外部呼叫,我們可以用它來執行重入攻擊。具體地說,請注意,在callback回撥期間,我們擁有了代幣ENS域名的ERC-1155 token,但域名封裝器尚未驗證我們擁有基礎ENS域名本身。這使我們能夠在不實際擁有ENS域名的情況下對其進行操作。例如,我們可以要求域名封裝器解開我們的域名,燃燒掉我們剛剛鑄造的token並獲取底層的ENS域名。

現在我們擁有了底層的ENS域名,我們可以用它做任何我們想做的事情,比如註冊新的子域名或者設定解析器。完成後,我們只需退出callback回撥。域名封裝器將和底層ENS域名的當前所有者(即我們)互動,並完成交易。就像那樣,我們已經取得了域名封裝器被批准用於的任何ENS域名的臨時所有權,並對其進行了任意更改。

5

結論

令人驚訝的程式碼可能會以災難性的方式破壞事物。在本文的兩個案例下,開發人員合理地假設safe函式類可以安全地使用,卻無意中增加了他們的攻擊面。隨著ERC-721和ERC-1155代幣標準變得越來越流行及廣泛,這類攻擊情況很可能會越來越頻繁。開發人員需要考慮使用safe類函式的風險,並確定外部呼叫如何與他們編寫的程式碼進行互動。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读