
去中心化金融(DeFi) 作為區塊鏈生態當紅項目形態,其安全尤為重要。從去年至今,發生了幾十起安全事件。
往期回顧:
往期回顧:
(1)往期回顧:
(2)往期回顧:
(3)[BlockSec DeFi 攻擊分析系列之一] 我為自己代言:ChainSwap 攻擊事件分析
[BlockSec DeFi 攻擊分析系列之三] 偷天換日:深度剖析Akropolis 攻擊事件
0xffffffff. 前言
0xffffffff. 前言
北京時間2021 年07 月21 日03:40,我們的攻擊檢測系統檢測到某個交易異常。通過對該交易進行擴展分析,我們發現這是一起利用通縮代幣(deflation token) KEANU 的機制對Sanshu Inu 部署的Memestake 合約的獎勵計算機制的漏洞進行攻擊的事件,攻擊者最後獲利ETH 約56個。下面詳細分析如下:
閱讀建議:
如果您剛剛接觸DeFi (Ethereum),可以從頭看器,但是文章比較長,看不下去記得點個關注再走。
如果您對Akropolis 等DeFi 聚合器項目比較了解,可以直接從「0x2 攻擊分析」開始。
0x0. 背景介紹
今年以來爆火的狗狗幣(DOGE)、柴犬幣(SHIB)引發了廣泛的關注,同時帶火了其他相關的meme 幣,更引發了大量的項目方開發自己的meme 幣及圍繞meme 幣提供服務,其中Sanshu Inu 就是其中一員。 Sanshu Inu 不僅發行meme 幣SANSHU,還創建了合約Memestake 作為meme 幣的耕種池。用戶只要往Memestake 中質押meme 幣,就可以獲得代幣Mfund 作為獎勵。
另一方面,大量的meme 幣都是通縮代幣,即該種代幣的發行量會逐漸減少。其中部分meme 幣的通縮實現形式是在用戶每次進行交易轉賬(transfer)的時候扣取一定比例的幣用於銷毀和再分配,這將導致接收方實際收到的token 數量小於支出方實際支付的數量。本次涉及的通縮代幣KEANU 就是採用這種實現。
該攻擊的大致原理就是通過控制Memestake 進行KEANU 的多次轉入轉出減少其持有的KEANU 數量,從而利用其獎勵計算函數的漏洞致使Memestake 給攻擊者發送大量Mfund。
0x1. 代碼分析
為了便於理解,我們首先簡要地介紹一下和此次攻擊相關的兩個實體合約:KEANU 代幣的KeanuInu 合約和Memestake 合約。
KeanuInu 合約http://tx.blocksecteam.com:8080/正如前面所說,KeanuInu 在實現代幣KEANU 的轉賬時,會扣取一定比例的幣用於銷毀和再分配,其中用於銷毀的比例設置為定值——2%。如圖,在調用KeanuInu 的transfer() 及transferFrom() 函數的時候,函數調用中顯示的轉賬數量跟emit 的事件log 中記錄的數量並不一致。
其由於其實際代碼實現調用較為複雜,此處不再展示,感興趣的朋友可以根據後面附錄給出的合約地址在etherscan.io 自行查看合約實現。另外,上面兩張截圖來源於我們自行開發的交易解析工具,目前開放公測中。歡迎各位點擊
試用。我們的工具將函數調用與過程中產生事件log 結合展示的方式,對於分析通縮代幣等問題更有幫助。
Memestake 合約
下圖是MemeStake 的deposit 函數。函數首先調用updatePool 更新資金池狀態,然後將用戶的token 轉賬給自己。當傳入的_amount 大於0 時會在代碼的1295 行進行轉賬。
然而,由於KEANU token 的通縮特性,雖然調用safeTransferFrom 函數時傳入的金額是_amount,但是實際上轉入資金池的金額小於_amount。並且在代碼分析中我們注意到,transfer 的目的地是自己,也就是說對於MemeStake 來說,所有用戶的某個幣種(如KEANU token)的存款都屬於MemeStake。
在轉賬後的1296 行,MemeStake 會對用戶的存款進行登記,但這裡登記採用的仍然是_amount (而真實的轉賬量小於_amount),因此用戶真正的存款量比登記的user.amount 更小。
最後在1299 行,可以看出user.rewardDebt 參數也是根據(比真實值要大的) user.amount 來計算的。
下圖是MemeStake 的withdraw 函數。該函數首先會檢查user.amount 是否還有足夠的餘額,但由於user.amount 本身比真實值大,因此這裡的檢查是不准確的。接下來,同樣會調用updatePool 函數更新資金池狀態。
在1321 行,withdraw 函數會先扣除在user.amount 中登記的餘額,然後調用transfer 函數把token 轉回用戶。和deposit 函數一樣,這裡的邏輯同樣存在問題,由於每次轉賬都會造成通縮,因此轉給用戶的數量會小於實際的轉賬量。
pool.accMfundPerShare += mFundReward / token.balanceOf(MemeStake)
最後來看MemeStake 的updatePool 函數。首先從1255 行可以看出,每次調用會記錄上一次更新的blockNumber,如果此次調用的區塊和上次更新時相同,則會直接返回,也就是說updatePool 對每個區塊只會更新一次資金池狀態。
rewardMfund = user.amont * pool.accMfundPerShare / 1e18 - user.rewardDebt。
接下來在1259 行,會獲取MemeStake 自身在token 合約中的餘額(上文提到,每次用戶deposit 都會將token 轉給MemeStake)。最後在1275 行,會利用這個餘額作為分母,計算該資金池每一次deposit 和withdraw 的獎勵(也就是pool.accMfundPerShare 參數)。計算方式如下:
user.rewardDebt = user.amount * pool.accMfundPerShare / 1e18
回到withdraw,我們來看存取款獎勵代幣Mfund 是怎樣轉賬的。首先在上圖withdraw 函數的第1325 行,計算用戶是否有pending 的Mfund token 沒有發放,計算公式為:
而rewardDebt 是這樣計算的(圖中第1325 行):
因此,從代碼中我們不難構造出一種可能的攻擊:
首先,在一個交易內,通過反複調用deposit 和withdraw 函數,榨乾MemeStake 的資金池。這個操作利用了三個代碼問題:
首先,user.amount 的記賬比真實值多,因此每次withdraw 都可以成功。
第二,MemeStake 中所有用戶的資金都在一個池子中,因此每一筆轉賬實際上Burn 掉的是池子中其他用戶存入的KEANU token。
第三,由於updatePool 在同一個塊中不會進行狀態更新,因此不會影響pool.accMfundPerShare 參數,也不會產生Mfund token 的reward。
0x2. 攻擊分析
0x2. 攻擊分析
0x2. 攻擊分析
0x2. 攻擊分析
前面介紹了漏洞成因及漏洞的利用方式,我們接下來介紹攻擊者實際是如何進行攻擊的。如圖所示,攻擊可以分為4 步,其中關鍵攻擊步驟為第2 步,利用通縮代幣的特性操縱Memestake 的獎勵計算。第1 步(準備),首先攻擊者創建了兩個合約並進行初始化,其中KEANU合約一為表現正常的投資合約,攻擊者通過合約一往Memestake 存入約2,049B,為步驟3 獲利大量MFUND 獎勵做好鋪墊。
合約二0x00ed為操縱Memestake 的獎勵計算的合約,先進行了相關token 的approve 操作。
第2 步(操縱),攻擊者先從uniswapV2 中flash loan 大量的KEANU 代幣,然後通過合約二往Memestake 中多次deposit 和withdraw 大量KEANU,導致Memestake 被迫大量交易KEANU。由於KEANU 是一種通縮代幣,每次交易會燒掉2% 的交易額,導致用戶真正存入Memestake 的量比登記的user.amount 更小,取出時又是按照user.amount 轉給用戶(詳見代碼分析),導致Memestake 池子中KEANU 的代幣持有量不斷減少,最終為1e-07。如下圖所示,涉及交易為Mfund,交易截圖未完全,請自行點擊鏈接查看。0xa945第3 步(獲利),攻擊者首先通過合約二調用了Memestake.updatePool() 函數,修改了KEANU 所在池子的accMfundPerShare,由於該值依賴於池子所持有的KEANU 的代幣量,而這在第二步中被操縱了(具體公式見下方代碼分析)。這使得合約二在接下來withdraw 的時候可以獲得遠超正常值的
(約61M)這種token 作為獎勵。第3 步發生於交易Tornado.Cash中,同時攻擊者開始將部分獲得的MFund 換成WETH 等代幣。
第4 步(收尾),攻擊者將獲得的MFund、KEANU 等代幣換成ETH,並通過0x0333轉移走,至此攻擊結束,攻擊者從中獲利ETH 55.9484578158357 個(攻擊者的EOA 地址及部署的攻擊合約還殘留有部分SANSHU 和KEANU 代幣未計入),約10 萬美元。
下圖為攻擊地址
的交易截圖,交易截圖未完全,請點擊地址鏈接查看詳情。
攻擊相關0x00ed有趣的是,攻擊的第2、3 步都與flashbots 交易有關。
其中第2 步涉及的交易0xa945由於採用了UniswapV2 flashloan,且交易前後相當於用約38ETH 去購買了KEANU,由此產生了很大的套利空間。因此該筆交易受到另一名攻擊者的三明治攻擊(sandwich attack),即本事件的攻擊者也是另一個三明治事件的受害者。該三明治攻擊者獲利3.2769697165652474ETH,但是給了礦工2.405295771958891249ETH,淨獲利0.8716739446063562ETH。
而第3 步攻擊涉及的交易則由於在uniswap 池子中大量售出MFund,創造了套利空間,所以被back-running 而成為flashbots 交易。該searcher 獲利0.13858054192600666ETH,其中交給礦工0.099085087477094764ETH,淨獲利0.03949545444891189ETH。0x3. 總結及安全建議Towards A First Step to Understand Flash Loan and Its Applications in DeFi Ecosystem (SBC 2021).
0x3. 總結及安全建議
0x3. 總結及安全建議
0x3. 總結及安全建議
攻擊者利用通縮代幣的特性控制了平台持有代幣的數量,影響了獎勵代幣的計算發放,由此獲利ETH 55.9484578158357 個。而這原因在於,Sanshu Inu 平台在引入通縮代幣時缺乏一定的安全考量,導致攻擊者有空可趁。
項目上線前,需要找有資質的安全公司進行安全審計。我們可以看到,由於defi 的money lego 屬性,很多defi 項目之間可以隨意組合,從而產生了互相影響,而這正是defi 領域安全事件頻發的原因。因此,項目方所需關注的安全問題不僅僅局限於自己項目,也同樣需要考慮在與其他項目交互過程中存在的安全漏洞。
BlockSec 團隊以核心安全技術驅動,長期關注DeFi 安全、數字貨幣反洗錢和基於隱私計算的數字資產存管,為DApp 項目方提供合約安全和數字資產安全服務。團隊發表20 多篇頂級安全學術論文(CCS, USENIX Security, S&P),合夥人獲得AMiner 全球最具影響力的安全和隱私學者稱號(2011-2020 排名全球第六). 研究成果獲得中央電視台、新華社和海外媒體的報導。獨立發現數十個DeFi 安全漏洞和威脅,獲得2019 年美國美國國立衛生研究院隱私計算比賽(SGX 賽道) 全球第一名。團隊以技術驅動,秉持開放共贏理念,與社區夥伴攜手共建安全DeFi 生態。