成都鏈安:Balancer專案漏洞分析

買賣虛擬貨幣

一、事件背景

Balancer官網(https://balancer.finance/)上對於其具體功能的描述為『Easily swap ERC20 tokens. Exchange tokens without deposits, bids/asks, and order management. All on-chain.』。簡單來說Balancer就是提供在鏈上進行tokens交換的區塊鏈智慧合約應用。

2020年6月29日凌晨(北京時間),Balancer專案的兩個資金池遭受攻擊。攻擊者在此次事件中獲利約46萬美元,資金池市商損失約50萬美元。

根據此次安全事件的具體過程,可以將此次事件比喻為攻擊者『偷樑換柱』。

二、抽絲剝繭還原攻擊者『偷樑換柱』經過

2.1、安全事件概述

▷根據鏈上交易資料顯示:

1、攻擊者利用自建合約

(0x81d73c55458f024cdc82bbf27468a2deaa631407)對存在通縮貨幣STA的資產池

(0x0e511Aa1a137AaD267dfe3a6bFCa0b856C1a3682)進行了攻擊;

2、攻擊者利用自建合約

(0xab9a95521107bc40dfa2e795059319f8b1302866)對存在通縮貨幣STONK的資產池

(0xb9eaf49d9f913bC1314e37bb5482891840c8e3C1)進行了攻擊。

2.2、攻擊步驟簡介

攻擊者首先透過閃電貸借款大量WETH,而後使用借得的WETH將被攻擊資金池中的通縮貨幣(STA或STONK)兌換出來,僅留下1e-18個通縮貨幣。完成上述準備工作後,攻擊者開始發動攻擊,不斷使用1e-18個通縮貨幣(STA或STONK)兌換資金池內的其他代幣,以達到『偷樑換柱』的目的。直到池內資金基本被轉移完後,攻擊者將獲利存入如下地址:

0xBF675C80540111A310B06e1482f9127eF4E7469A

攻擊過程如下圖所示:

△圖1

此次事件發生後,Balancer團隊表示已對資產池進行審計,正在進行第三次審計,並將在UI介面啟用通縮貨幣黑名單,禁止使用者建立存在通縮貨幣的資產池。

2.3、漏洞原理詳細分析

在分析漏洞具體資訊之前我們需要知道以下兩點:

1、Balancer專案允許個人建立資金池。資金池本質上是一個智慧合約,使用者可以呼叫資金池的函式進行代幣兌換。資金池中可以存在多種貨幣,使用者可以使用資金池中存在的貨幣進行兌換,兌換的比例按照一種固定的演算法,如圖所示:

△圖2

我們以用STA兌換WETH為例:

▷TokenAmountOut表示可以兌換出的WETH的值

▷TokenBalanceOut表示當前池內的WETH的值

▷TokenBalanceIn 表示當前池子內的STA的值

▷TokenAmountIn 表示使用者輸入的STA的值

▷TokenweightIn 表示STA的權重,為一個固定值,只能由資金池的管理者更改

▷TokenweightOut表示WETH的權重,為一個固定值,只能由資金池的管理者更改

▷SwapFee表示手續費,為一個固定值,只能由資金池的管理者更改

綜上所述,當一種貨幣STA在一個資金池中的存量較少時,也就是bI較小時,就可以使用STA兌換更多的WETH。

2、STA代幣是一種通縮貨幣,當進行轉賬操作時,會自動銷燬一定量的STA。如下圖所示:

△圖3

tokensToBurn即為每次交易銷燬的值,其銷燬數額是轉賬數額的1/100(向上取整),如當數值為1e-18時,其銷燬值也是1e-18。銷燬值的計算原始碼如下圖所示:

△圖4

△圖5

接下來我們對本次攻擊事件進行分析,以存在STA 的被攻擊資金池為例。攻擊者向自建合約

(0x81d73c55458f024cdc82bbf27468a2deaa631407)發起了一筆交易

(0x013be97768b702fe8eccef1a40544d5ecb3c1961ad5f87fee4d16fdc08c78106)。在此筆交易中,攻擊者首先從閃電貸借出了104331個WETH,如下圖所示:

△圖6

而後使用借來的WETH兌換被攻擊資產池中的STA,因為STA是通縮貨幣,每次transfer都會使得STA銷燬轉賬金額的1/100(向上取整)。如下圖為一次兌換:

△圖7

這筆交易共進行了20餘次兌換,使得被攻擊資金池中的STA餘量為一個極小值(10-18)後開始使用STA兌換其他代幣(WETH),如下圖所示:

△圖8

我們可以發現,在此筆交易中,攻擊者轉給被攻擊合約的STA個數是『0』,但卻扣除了1e-18個STA,這不符合正常兌換情況。於是我們對此進行深入分析,透過事件日誌確定攻擊者傳送了1e-18個STA。如下圖所示:

△圖9

由此可得出結論,在傳送過程中,因為STA的通縮機制,傳送給資金池的STA會被銷燬,導致被攻擊資金池無法收到STA,但資金池合約仍然會認為收到了1e-18個STA,並更新STA的存量(inRecord.balance)。如下圖所示:

△圖10

如果STA的存量增加,就會使得STA能夠兌換其他代幣的比例下降,因此攻擊者又呼叫了gulp()方法來更新STA的餘額,使得資金池的STA餘額等於實際餘額。如圖所示:

△圖11

每進行一次兌換,攻擊者就會呼叫一次gulp()對STA的餘額進行更新,這樣使得STA的餘額始終為1e-18個,因此每次攻擊取出餘額的比例都是不變的(1/2),如下圖所示:

△圖12

攻擊者使用這種方式,將資金池中的所有代幣以每次1/2的比例進行兌換,最終幾乎將資金池中的所有代幣全部提出。

2.4、攻擊事件總結

根據我們日常智慧合約安全審計經驗來看,本次事件產生的原因,可能是資金池合約對流入資金的處理方式不夠完善,並沒有考慮到通縮性代幣的情況,在計算應當輸出的值tokenAmountOut和貨幣餘額inRecord.balance的增減時,都是使用由使用者控制的tokenAmountIn引數,而不是實際收到的代幣數,導致實際池中的流入資金與記錄資金不相符,如下圖所示:

△圖13

另外,用於更新代幣餘額的gulp()函式的許可權是external,這兩點組合起來,導致了本次事件漏洞的產生,如下圖所示:

△圖14

免責聲明:

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

推荐阅读

;