動區動趨-最具影響力的區塊鏈新聞媒體
  • Home
    • Home Layout 1
    • Home Layout 2
    • Home Layout 3
  • Browse
    • News
    • Movie
    • Music
    • Technology
    • Howto & Style
    • Entertainment
    • Gaming
  • Features
    • Youtube Video
    • Vimeo Video
    • Dailymotion Video
    • Self-hosted Video
    • User Profile
    • Playlists
    • User-created Playlist
    • Favorite Playlist (Private)
    • Watch Later Playlist (Private)
    • All JNews Features
No Result
View All Result
  • Login
  • Register
UPLOAD
動區動趨-最具影響力的區塊鏈新聞媒體
No Result
View All Result
Currently Playing

ABS獨家專訪》Gitcoin共同創辦人Scott:台灣是現實與Web3治理的重要交匯點

ABS獨家專訪》Gitcoin共同創辦人Scott:台灣是現實與Web3治理的重要交匯點

ABS獨家專訪》Gitcoin共同創辦人Scott:台灣是現實與Web3治理的重要交匯點

搶先看
ABS獨家專訪》Gate.io CEO韓林:無懼銀行進軍加密服務,台北特別有人情味

ABS獨家專訪》Gate.io CEO韓林:無懼銀行進軍加密服務,台北特別有人情味

搶先看

6 Sci-fi Gadgets in Movie We Wish Actually Existed

Movie

The 10 best games to play on your new PlayStation 4

Gaming

Tesla’s Chinese factory just delivered its first cars

News

全解析 | 不可不知的 3種「Defi重入攻擊」: 基本概念、背後細節、手法 — Amber資安負責人

在區塊鏈短短的歷史上發生過跟智能合約相關的攻擊事件中,重入攻擊無疑是最廣為人知的一種類型,在以太坊新創時期,2016 年 7 月的 TheDAO 事件甚至直接造成了以太坊硬分叉為以太經典 (ETC) 及現在大部分人熟知的以太坊 (ETH)。這次將由 Amber Group 區塊鏈安全團隊負責人 Chiachih Wu 博士,步步深入三種重入攻擊的基本概念、背後細節與手法。
(前情提要:乾貨 | Amber 安全專家吳博士:剖析 BSC 的閃電貸攻擊手法,如何再引發 3 個分叉項目連環爆?)

 

本文為數位資產投資集團 Amber Group 的投稿,作者為該集團區塊鏈安全專家吳家志博士(Chiachih Wu),同時共同創辦了區塊鏈知名安全公司派頓(Peckshield)。


在 TheDAO 事件給以太坊重擊之後,開發者也多了一些方法來防範重入攻擊,例如,Checks-Effects-Interactions [1] 以及 Reentrancy Guard [2]。

然而,許多重入攻擊仍然持續發生,攻擊的形式也從通過 fallback 函數重入同函數轉變成通過不同的外部函數進入智能合約,造成合約狀態混亂以達成有效攻擊。

本文將介紹及重現發生於 2020 年 4 月 UniswapV1 的重入攻擊,2021 年 7 月發生在 BSC 上 DeFiPIE 項目的重入攻擊,以及近期發生於 C.R.E.A.M. 項目的 AMP 代幣重入攻擊 [8]。

延伸閱讀:DeFi | 拆解Cream.Finance「ERC-777重入攻擊」,駭客獲利1,880萬美元

延伸閱讀:勿輕信熱心網友!Board Ape投資者 78 萬鎂的NFT全被盜走:MetaMask也很有問題

經典案例分析

在進入案例分析之前,我們先介紹下重入攻擊的基本概念。

下面是 Solidity 網站上面介紹重入攻擊給的簡單案例 [3],事實上這個 Fund 合約,就是簡化過的 TheDAO 合約,在 withdraw () 函數裡我們可以看到 shares [msg.sender] 數量的 ETH 會透過 msg.sender.send() 發給 msg.sender,也就是 Fund.withdraw() 的 caller。

其中 shares[] 裡頭存的是每個 user 存入合約的 ETH 額度,因此在 user 成功取出 ETH 之後,shares[msg.sender] 會被清零,這個程式邏輯看起來沒有任何問題。
然而,上述的 caller (msg.sender) 可以是個惡意合約地址,如果惡意合約裡寫了 fallback function ,則 Fund.withdraw() 裡的 msg.sender.send() call 就可以被 hijack,在這個 fallback function 裡如果再次調用了 Fund.withdraw() ,則 shares[msg.sender] 數量的 ETH 就會在被清零之前被多次發送給 msg.sender,下面是一個示意圖:

攻擊者部署一個 Evil 合約,在 Evil.receive() (即 fallback function)檢查 Fund 的 ETH 足夠的情況下連續調用 Fund.withdraw() ,即可將 Fund 合約的 ETH 抽光,直到最後一次調用,shares[msg.sender] 才會被真正的清零。

這個簡單的重入攻擊案例有一個關鍵點:「清零」 發生在 「轉帳」 之後。

雖然這樣的寫法比較符合人類的邏輯,即『確認轉帳成功了,再把紀錄清掉』,但是在 EVM 的世界裡有點不同。其實先清零再轉帳也沒有什麼問題的,如果轉帳失敗了,清零的操作會自動回滾 (revert)。

而且把轉帳放到清零之後,反而可以避免重入攻擊,也就是 Checks-Effects-Interactions pattern。shares[msg.sender] 是 effects,msg.sender.send() 是 interactions,只要所有的 interactions 都在 effects 之後,即使重入了 Fund.withdraw() 也不會造成什麼影響。

延伸閱讀:被誤解的閃電貸:我只是一個工具….5月DeFi安全事件25起、BSC占15起

延伸閱讀:你的「紙錢包」可能不安全!私鑰盜竊問題叢生,資安新創 CYBAVO 詳列危險清單

Uniswap 遭重入攻擊

接下來,我們將介紹一個類似的案例,只是漏洞利用方式稍微複雜一點。2020 年 4 月 18 日下午,Twitter 上開始出現了關於 Uniswap imBTC pool 被攻擊的消息 [4]:

(編輯註:以下 Uniswap 攻擊模擬測試由 Amber Group「STAR-X實習計劃」 中來自卡內基梅隆大學的學生所做。目前新一期的 STAR-X 計畫已啟動。)

Uniswap 的創辦人 Hayden Adams 提到了 UniswapV1 不支持 ERC-777 並且附上了一個 ConsenSys Diligence blog 的連結[5]。事實上,這次攻擊符合 ConsenSys Diligence blog 裡的描述,而且這篇 blog 是差不多剛好一年之前寫的 (2019-4-20)。

關鍵點在 UniswapV1 的 tokenToEthInput() 函數與 ERC-777 token 的兼容性問題,從下面程式碼片段可以看到,tokenToEthInput() 函數基本上是符合 Checks-Effects-Interactions 的寫法,第 208 行合約給用戶發送 ETH,第 209 行用戶給合約發送 token,都在函數的最後面執行,如果從 UniswapV1 本身來看是沒有任何問題的。

然而,DeFi 世界就像是一個金融樂高遊樂場,209 行發送的 token 本身也是一個智能合約,在這個合約肚子裡存在一個 effects after interactions 的場景。
下面是某個 ERC-777 token contract 的 transferFrom() 函數底層實現,第 866 行有一個 callback interface 可以用來通知 holder ,只要 holder 是一個合約地址,並且按照 ERC-1820 註冊了 tokensToSend() 函數。

而第 868 行的 _move() 才是真正更新 token balances 的地方。因此,如果攻擊者在 _callTokensToSend() 時重入了 UniswapV1 的 tokenToEthInput(),可以造成 UniswapV1 pool 本身 token balance 增加之前,多次兌換成 ETH。即第 204 行的 token_reserve 永遠不變。
簡單的說,在重入攻擊發生的情況下,第 204 取出的 token_reserve 可能跟上一層調用是一樣的,在 Uniswap xy=k 的設定下,如果可以用同樣的 token_reserve 多次交易,等於是可以持續用較高的價格賣出 token 把流通性提供方 (LP) 的代幣消耗殆盡。

下面是我們利用 eth-brownie 回到案發之前的 2020-2-15 區塊高度 9488451 重現這次攻擊的程式碼:

首先是透過 ERC-1820 合約註冊 tokensToSend() callback function,註冊完成之後所有兼容 ERC-777 的 token transfer 發生時,如果目標地址是攻擊合約本身,則合約的 tokensToSend() external function 會被調用。接下來介紹攻擊發起函數 trigger():

上面這短短 10 行原始碼只做了四件事,第 38 行將 ETH 換成 token,第 39 行將上一步換出來的 token 又換回 ETH,第 40 行將一部分 ETH 換成 token,第 43-44 行將所有的 ETH 及 token 轉給 owner,也就是攻擊者錢包地址。

其中,第 39 行有一個比較特別的點,只有 1/32 的 token 被換回 ETH,按照這樣的寫法肯定是會虧錢的。其實另外的 31/32 置換是在上述的 callback function 裡頭完成,程式碼如下:
從上面的程式碼可以看到 entry 會計算現在是第幾次進入 tokensToSend() 然後在第 57 行完成另外 31 次兌換,每次也是 1/32 的 token balance。

透過這 31 次重入,攻擊者可以用較好的價錢賣出 token 並且破壞 UniswapV1 pool 裡的平衡狀態,即 xy=k 的 k 值改變,因此最終 pool 裡的 ETH 會變得很少 token 很多,ETH 相對於 token 的價值極高,所以上面 trigger() 函數的第 40 行,攻擊者可以用很少的 ETH 把大部分 pool 裡的 token 買回來。下面是攻擊原始碼執行的結果:

原本 pool 裡頭有 718 ETH + 19.59 imBTC,攻擊完成之後只剩下 0.013 ETH + 0.019 imBTC,幾乎是掏空了 pool。

DeFiPIE 重入攻擊

上述 UniswapV1 + ERC-777 的例子其實跟 TheDAO 的案例類似,都屬於同一個函數的重入,下面介紹一個多函數參與的案例,是近期發生在 BSC 上的 DeFiPIE 攻擊事件。

在第一眼看到 DeFiPIE 原始碼時,有一種熟悉感,與老牌 DeFi 項目 Compound 有 87% 的相似度,直覺聯想起了 2020-4-19 的 Lendf.Me $25M Better future 事件 [6]。仔細分析之後發現,問題的根源確實如出一轍,都是透過重入攻擊造成內部記帳錯誤,達成獲利。

從上面 DeFiPIE 的 PToken 合約程式碼片段中可以看到,borrowFresh() 函數會在把資產發給 borrower 之後才將因為這次借款造成的狀態改變寫入合約的 storage,所以又是一個 effects after interactions 的案例。

由於借款的上限取決於抵押資產的價值,正常情況下,某一次借款把額度用完之後,在歸還借款之前應該就借不出任何資產了。

但由於上述情況數據沒有及時更新,重入後的第二次借款仍然可以使用跟第一次借款發生前一樣的額度,因此理論上是可以無限嵌套,多次利用有限額度,最終攻擊者可透過清算自己以較低成本創造的負債獲利。

在 Lendf.Me 事件中,攻擊者是透過 imBTC 的 ERC-777 內建機制攔截 transferFrom() 完成重入攻擊。在 DeFiPIE ,對於 token 本身並沒有任何限制,可以隨意創建 token 合約納入借貸體系。

如上圖所示,任何人都可以創建一個惡意的 EvilToken 並且人工製造一個攔截 transfer() 的機制以達成重入攻擊,下面介紹我們如何 reproduce 針對 DeFiPIE 的攻擊,由於這個攻擊比較複雜,我們會依序從各個模組介紹,最後介紹如何組裝使用。

先從惡意 token contract 開始,現在要寫一個 ERC-20 合約基本上只要繼承 OpenZeppelin 的 template[7] 自行修改 token name 以及 symbol 就行。

在上面的 X token contract 裡可以看到,第 233 行的 transfer() 我們加入了一個開關 optIn,在開關打開的情況下 (optIn == true),Lib.shellcode() 會被調用執行重入攻擊任務,這就是上面說到的人工創建攔截 transfer() 的機制。其他如 mint(), setup(), start() 就是一些方便使用的外部函數。

第二個模組是 Lib.shellcode() 函數,也就是上述 transfer() 被攔截後發起重入攻擊的地方,在這次模擬中,我們嵌套了三層,依序調用了自行創建的 PToken (pX[1], pX[2]) 並且在第三層從 pBUSD 真正的借出了 21,000 BUSD,在這過程中實現了『三個罈子一個蓋』。

第三個模組是獲利的關鍵,清算者 (Liquidator)。在上面的 Liquidator.trigger() 函數可以看到,清算者使用 x 代幣調用 pX 合約的 liquidateBorrow() 獲取質押品 colleteral(即 pCAKE),隨後在第 66-67 行將 pCAKE 換成 CAKE 並轉給 owner(即 Lib 合約)。

mint() 函數的作用是提供足夠的 x 給 pX 合約,讓上述 Lib 合約能夠調用 pX.borrow() 借出資產。

接下來就是組裝上面三個模組搭配閃電貸取得獲利,首先是創建三個 X tokens 及 Lib 合約。Lib 合約的 constructor 創建了 Liquidator 合約。

第 272-278 行鑄造了X tokens給 Liquidator 及 Lib,第 280-284 行將 X tokens 與 Lib 互相關聯上。第 285 行觸發 Lib 合約啟動後續流程,最後在第 288 行將獲利的 WBNB 轉給 owner (即攻擊者錢包地址)。

Lib.trigger() 實際上就做了一個兩層的 PancakeSwap 閃電貸,第 116 行可以看到 154.5 WBNB 被借出,在回調函數 pancakeCall() 裡又借了 2,900 CAKE。主要的攻擊流程在 pancakeCall() 的後半段。

在進入第二層 pancakeCall() 時,就是真正攻擊流程的開始,首先是使用 x[0], x[1], x[2] 這三個 X tokens 創建三個 pToken (pX[0], pX[1], pX[2])。

要創建 pToken 需要預先在 Uniswap 創建交易對並且注入流通性(第 136-142行),pX[i] 創建完畢後,即可取出流通性(第 149 行)以方便重複使用前面借出的 WBNB,最後觸發 Liquidator 存入足夠的 x[i] 讓 pX[i] 能夠被 borrow() (第 152 行)。

第二步是觸發 pX.borrow() 前的準備工作,第 156-162 行調用了 Controller.enterMarkets() 將 pX[0], pX[1], pX[2], pCAKE 等 pToken 納入 DeFiPIE 體系,以便後續操作。第 166 行將前面閃電貸借出的 2,900 CAKE 全數注入 pCAKE 合約充當後續借貸的抵押品。

第三步打開 x[0], x[1], x[2] 的 transfer() 攔截機制(第 170-172 行),並且觸發 pX[0].borrow(),由於上述 Lib.shellcode() 的作用下,最終會拿到 21,000 BUSD,並且創造了不良資產。

第四步觸發 Liquidator 清算不良資產,獲得 CAKE。

清償完閃電貸後,在測試環境中最終獲利 66 WBNB。雖然數額不大,但這個案例涉及到代幣合約,清算合約等較複雜的漏洞利用過程,值得研究分享。

CREAM 遭同樣攻擊

2021 年 8 月 30 日下午,就在這篇文章完稿之際,C.R.E.A.M. 項目傳出了遭遇攻擊損失 $18M [9]。筆者短暫分析攻擊交易後發現這次攻擊與上述 DeFiPIE 遭遇的攻擊手法極其類似,決定復現此案例並加入本文。

漏洞的原理其實不需要贅述,跟 DeFiPIE 基本是一樣的,攻擊者通過 AMP 代幣自身的回調機制實現了『兩個罈子一個蓋』,用同一筆 ETH 質押品借出了 AMP 及 ETH,最終透過另一個合約清算自己的不量債務獲利。下面直接介紹攻擊合約的各個模組以及最後的組裝使用:

首先是註冊 callback function,跟前面 UniswapV1 的情況類似,攻擊者通過 ERC-1820 合約註冊一個 tokensReceived() 函數,當有人往攻擊合約發送 AMP tokens 時,callback function 會被觸發。

而 callback function 本身就是一個針對 crETH 合約的 borrow() 調用,攻擊者的預期是在 crAMP.borrow() 的調用過程中利用同樣的抵押品再借一筆 ETH。

第三個模組是 Liquidator 合約,與上述 DeFiPIE 的 Liquidator 類似,在上圖 Liquidator.trigger() 函數裡,攻擊者用 AMP 清算了自身創造的不良資產獲得 crETH 抵押品(第 60 行),隨後將 crETH 換成 ETH(第 61 行),並發回給 owner,即攻擊合約。

最後就是組裝執行攻擊了,上圖是 Exp.trigger() 函數,在第 94 行先是一個 UniswapV2 的閃電貸,借出了 500 WETH,後面的 uniswapV2Call() 函數才是真正的流程。

首先是一些準備工作,由於閃電貸借的是 WETH 而 crETH 需要使用 ETH 才能鑄造,因此在第 105 行,先將 WETH 換成 ETH,接下來將換出的 ETH 全數發給 crETH 合約鑄造出 crETH cTokens。與前面 DeFiPIE 攻擊一樣,需要調用一次 Comptroller.enterMarkets() 將 crETH 開啟以便後續的操作。

第二步就是利用上面存入的 500 ETH,借出 AMP tokens,在 crAMP.borrow() 的過程中 crAMP 合約把 AMP 轉給攻擊合約,由於前面 ERC-1820 的機制,這次轉帳會被攔截並另外借出 355 ETH。

第三步通過 Liquidator 合約清算債務,將部分質押品取回。從上圖可以看到攻擊者將前面借出的一半 AMP 發給 Liquidator,換回足夠支付閃電貸的 ETH,保留剩下的 AMP。

最終將 ETH 都換成 WETH 支付閃電貸後,帶走 41 WETH + 9.74M AMP。

若以『幣圈一天,人間一年』給區塊鏈世界計時,重入攻擊算是上古時期的物種了,開發者還需多從歷史上發生過的案例中吸取經驗,形成肌肉記憶,避免受到傷害。


.Reference.
[1] https://docs.soliditylang.org/en/v0.4.21/security-considerations.html#use-the-checks-effects-interactions-pattern
[2] https://docs.openzeppelin.com/contracts/4.x/api/security#ReentrancyGuard
[3] https://docs.soliditylang.org/en/v0.4.21/security-considerations.html#re-entrancy
[4] https://twitter.com/_prestwich/status/1251382098188877824
[5] https://medium.com/consensys-diligence/uniswap-audit-b90335ac007
[6] https://peckshield.medium.com/uniswap-lendf-me-hacks-root-cause-and-loss-analysis-50f3263dcc09
[7] https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
[8] https://twitter.com/CreamdotFinance/status/1432249771750686721
[9] https://twitter.com/ICO_Analytics/status/1432234014878879744

📍相關報導📍

乾貨 | Amber 安全專家吳博士:剖析 BSC 的閃電貸攻擊手法,如何再引發 3 個分叉項目連環爆?

台灣Defi | Cream Finance再遭閃電貸駭客攻擊,損失1,800萬美元的 ETH, AMP

BSC首現閃電貸攻擊/技術解析 Spartan Protocol 遭駭手法,造成 3 千萬美元損失


讓動區 Telegram 新聞頻道再次強大!!立即加入獲得第一手區塊鏈、加密貨幣新聞報導。

LINE 與 Messenger 不定期為大家服務

加入好友

加入好友

No Result
View All Result

近期文章

  • 精選文章搶先看!動區登入Access質押訂閱服務,解鎖寶貴資訊快人一步
  • ABS獨家專訪》Gitcoin共同創辦人Scott:台灣是現實與Web3治理的重要交匯點
  • ABS獨家專訪》Gate.io CEO韓林:無懼銀行進軍加密服務,台北特別有人情味
  • 快訊!BTC 現在已來到 58996.2
  • 快訊!BTC 現在已來到 58815.03
Next Post
海外也逃不掉!美財政部決心追稅,欲推「全球」加密貨幣稅務資訊共享規則

海外也逃不掉!美財政部決心追稅,欲推「全球」加密貨幣稅務資訊共享規則

Copyright (c) 2019 by Jegtheme.
  • About
  • Buy JNews
  • Request A Demo
  • Contact

Welcome Back!

Login to your account below

Forgotten Password? Sign Up

Create New Account!

Fill the forms below to register

All fields are required. Log In

Retrieve your password

Please enter your username or email address to reset your password.

Log In

Add New Playlist

- Select Visibility -

    No Result
    View All Result
    • Account
    • BlockTempo Beginner – 動區新手村
    • Change Password
    • Forgot Password?
    • Home 1
    • Home 2
    • Home 3
    • Jin-homepage
    • Latest
    • Login
    • Profile
    • Register
    • Reset Password
    • Trending
    • Users
    • Users List Item
    • 不只加密貨幣,談談那些你不知道的區塊鏈應用|動區新手村
    • 所有文章
    • 關於 BlockTempo

    © 2025 JNews - Premium WordPress news & magazine theme by Jegtheme.