在私有以太坊上實現針對ERC20數字貨幣ProxyOverflow漏洞的攻擊

ERC20的ProxyOverflow漏洞造成影響廣泛,本文將對其攻擊方法進行分析,以便于智能合約發布者提高自身代碼安全性以及其他研究人員進行測試。本文選擇傳播廣泛、影響惡劣的SMT漏洞(CVE-2018–10376)作為樣本進行分析,文中所涉及的代碼截圖均來自于SMT代碼。由于目前各大交易平臺已經將ERC20協議的數字貨幣交易叫停,本文的發布不會對這些貨幣帶來直接影響。

1 ERC20貨幣及transferProxy函數

1.1 ERC20貨幣簡介

基于ERC20協議的數字貨幣(以下簡稱為ERC20貨幣)實際上是以太坊上運行的智能合約,合約中對于每個賬戶擁有的貨幣數目是通過?賬戶地址→貨幣數?的映射關系進行的記錄:

mapping (address => uint256) balances

ERC20貨幣的擁有者要想進行貨幣交易、余額查詢等操作時,需要向智能合約對應的地址發送消息,聲明調用的函數和相應參數。這一消息將會礦機接收,并執行智能合約中相應的函數代碼。在這一過程中,消息發送者需要向挖礦成功的礦機支付相應的報酬。這筆報酬在以太坊中被稱作gas,其支付貨幣為以太幣。也就是說,ERC20的貨幣擁有者要想發送貨幣交易消息,就需要擁有一定數量的以太幣。

然而,ERC20貨幣擁有者并不一定擁有以太幣。為了滿足他們發起貨幣交易的需求,ERC20 協議提供了transferProxy函數。利用該函數,ERC20貨幣擁有者可以簽署一個交易消息,并交由擁有以太幣的第三方節點將其發送到以太坊上。消息的發送者會從擁有者那里獲取一定數量的ERC20貨幣作為其發送消息的代理費用。

1.2 transferProxy函數代碼分析

SMT的transferProxy函數代碼如下圖所示:

該函數的各個參數解釋如下,該函數代碼邏輯較為簡單,此處不做贅述。

  • address _from:ERC20 貨幣的擁有者和交易的發起者;
  • address _to:貨幣交易中的接收者;
  • uint256 _value:貨幣交易的數額;
  • uint256 _feeSmt:交易信息發送者(即函數中msg.sender)收取的代理費用;
  • uint _v,bytes32 _r,bytes32 _s:交易發起者(即_from)生成的簽名數據。

需注意的是,代碼215行中的transferAllowed(_from)是transferProxy()運行前必會運行的驗證函數。該函數代碼如下:

代碼117行中的exclude為映射結構,僅合約的創建者將為設置為True,其他地址默認均為False。

代碼118行判定transferEnabled標志符是否為true,該標志只能通過enableTransfer函數設定,且該函數只能被合約創建者調用,該函數的作用是使得ERC20合約的交易過程可控,這也是SMT等貨幣出現問題時能夠在后續中止交易的原因:

代碼119-121行對于交易發送者(即_from)帳號是否被鎖定進行了檢查,lockFlag和locked都只能被合約創建者所控制:

?

綜上所述,只有整個合約在允許交易且攻擊者帳號未被鎖定的情況下,攻擊者才能真正調用transferProxy函數。在參數處理過程中發生漏洞的原因可參見我們之前的分析文章:https://weibo.com/ttarticle/p/show?id=2309404232782242012923

2 攻擊重現

為了重現攻擊,我們選擇了基于go語言編寫的以太坊客戶端geth進行以太坊私有網絡的部署。為了便于實現可編程的自動化交互,我們選擇了Web3.py作為與以太坊節點交互的中間件。

2.1 漏洞驗證環境的搭建

S1.?? 從鏈接頁面下載SMT智能合約源碼;

S2.?? 創建兩臺Linux虛擬機;

S3.?? 準備Python運行環境,在兩臺虛擬機上安裝python3,并利用pip安裝web3、py-solc、hexbytes、attrdict;

S4.?? 準備合約編譯環境,在兩臺虛擬機上安裝智能合約代碼編譯器solc,參考鏈接

S5.?? 在兩臺虛擬機上搭建以太坊私有網絡,可參考鏈接,其中:

— 1)??? 節點1用于發布SMT合約代碼,為其創建以太坊賬戶并分配一定數量以太幣,啟動挖礦;

— 2)??? 節點2用于部署攻擊代碼,創建兩個以太坊賬戶,分別作為transferProxy中的from賬戶(轉賬消息簽署者,記為Signer)和transferProxy調用者(即轉賬消息的發送者,記為Sender),為Sender分配一定數量以太幣,并啟動挖礦。

2.2 SMT智能合約發布

在節點1上,利用deploy_SMT.py腳本中的代碼實現SMT智能合約的一鍵部署。

關于執行前的配置的介紹:

1)? sol_path,代表合約代碼路徑;

2)? account,代表用于發布合約的賬戶,如1.2所示,只有該賬戶才能調用部署好的智能合約函數,進行控制交易開啟和關閉,維護被鎖賬戶列表等操作;

3)? pin,用于解鎖account的密碼。

關于執行過程與結果的分析:

1)??? tx_receipt,該變量用于獲取部署智能合約(23行)和發送啟動交易消息(35行)的結果,當這兩行代碼被調用后,以太坊網絡中會發布相應的消息,只有在下一個區塊被挖掘出來后,tx_receipt才能獲取非空的結果;

2)??? contract_address,代表該合約被順利部署到以太坊網絡后的合約地址,其他節點要想調用合約代碼,需要獲知該地址以便發送函數調用消息。

合約代碼部署結果的截圖如下:?

2.3 ProxyOverflow漏洞攻擊

在節點2上,利用test_SMT.py腳本中的代碼可實現針對SMT合約的一鍵攻擊。

關于執行前的配置的介紹:

1)??? contract_address,來自2.2中SMT部署完成后的輸出值;

2)??? sol_path,代表合約代碼路徑;

3)??? signer,交易信息的簽署者,也將作為調用transferProxy時的_from和_to的實參;

4)??? sender,交易信息的發送者,需要擁有一定數量以太坊以支付gas費用;

5)??? signer_pin,signer的密鑰解鎖口令,以便對交易信息進行簽名;

6)??? sender_pin,sender的密鑰解鎖口令,以便解鎖sender賬戶,支付gas費用;

7)??? value,代表發生交易的金額;

8)??? fee,代表支付給sender的代理費用;

9)??? signer_key_path,代表signer的密鑰文件路徑。

關于執行過程與結果的分析:

1)??? 30-35行,基于目標智能合約地址和代碼,創建智能合約對象;

2)??? 37-38行,獲取并打印sender和signer在攻擊前的SMT幣數目;

3)??? 40-43行,獲取signer現有的nonce的值,并將其擴充為64字符的字符串;

4)??? 46-62行,構建要進行簽名的數據的Hash值,獲取signer的私有密鑰,并對Hash值進行簽名,獲得簽名數據s,r,v;

5)??? 63-77行,構造transferProxy函數調用參數,進行函數調用,并獲取交易回執;

6)??? 79-80行,獲取并打印sender和signer在攻擊后的SMT幣數目。

攻擊結果的截圖如下:

 

作者:騰訊湛盧實驗室