以太幣交易所 以太幣交易所
Ctrl+D 以太幣交易所
ads
首頁 > SHIB > Info

智能合約安全審計入門篇 —— 搶跑_MIT

Author:

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

背景概述

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

前置知識

提到搶跑,大家第一時間想到的一定是田徑比賽,在田徑運動中各個選手的體能素質幾乎相同,起步越早的人得到第一名的概率越大。那么在以太坊中是如何搶跑的呢?

想了解搶跑攻擊必須先了解以太坊的交易流程,我們通過下面這個發送交易的流程圖來了解以太坊上一筆交易發出后經歷的流程:

可以看到圖中一筆交易從簽名到被打包一共會經歷7個階段:

1.使用私鑰對交易內容簽名;

2.選擇GasPrice;

3.發送簽名后的交易;

4.交易在各個節點之間廣播;

5.交易進入交易池;

6.礦工取出GasPrice高的交易;

7.礦工打包交易并出塊。

交易送出之后會被丟進交易池里,等待被礦工打包。礦工從交易池中取出交易進行打包與出塊。根據Eherscan?的數據,目前區塊的Gas限制在3000萬左右這是一個動態調整的值。若以一筆基礎交易21,000Gas來計算,則目前一個以太坊區塊可以容納約1428筆交易。因此當交易池里的交易量大時,會有許多交易沒辦法即時被打包而滯留在池子中等待。這里就衍生出了一個問題,交易池中有那么多筆交易,礦工先打包誰的交易呢?

礦工節點可以自行設置參數,不過大多數礦工都是按照手續費的多少排序。手續費高的會被優先打包出塊,手續費低的則需要等前面手續費高的交易全部被打包完才能被打包。當然進入交易池中的交易是源源不斷的,不管交易進入交易池時間的先后,手續費高的永遠會被優先打包,手續費過低的可能永遠都不會被打包。

那么手續費是怎么來的呢?

我們先看以太坊手續費計算公式:

Flashbots核心貢獻者等人發表新論文,研究DeFi智能合約經濟安全的形式化驗證框架CFF:9月18日消息,Flashbots核心貢獻者PhilipDaian、康奈爾大學計算機安全博士生KushalBabel和MahimnaKelkar以及康奈爾理工學院教授AriJuels共同發布關于ClockworkFinanceFramework(CFF)的新論文,這是一種DeFi智能合約經濟安全的形式化驗證框架。論文中表示,CFF具有三個關鍵特性,合約完備、以漸近最優的模型大小實現、在構造上窮舉攻擊。CFF可以支持多個目標,開發者對合約的經濟安全性分析、用戶對DeFi交易風險的分析、機器人或礦工對套利機會的優化。此外,CFF框架引入了一個新的DeFi合約經濟安全概念,可提取價值(EV)。[2021/9/18 23:35:18]

TxFee=GasUsed*?GasPrice

其中GasUsed是由系統計算得出的,GasPrice是可以自定義的,所以最終手續費的多少取決于GasPrice設置的多少。

舉個例子:

例如GasPrice設置為10GWEI,GasUsed?為21,000。因此,根據手續費計算公式可以算出手續費為:

10GWEI*21,000=0.00021Ether

在合約中我們常見到Call函數會設置GasLimit,下面我們來看看它是什么東西:

GasLimit可以從字面意思理解,就是Gas限制的意思,設置它是為了表示你愿意花多少數量的Gas在這筆交易上。當交易涉及復雜的合約交互時,不太確定實際的GasUsed,可以設置GasLimit,被打包時只會收取實際GasUsed作為手續費,多給的Gas會退返回來,當然如果實際操作中GasUsed>GasLimit就會發生Outofgas,造成交易回滾。

當然,在實際交易中選擇一個合適的GasPrice也是有講究的,我們可以在ETHGASSTATION上看到實時的GasPrice對應的打包速度:

TRON網絡智能合約觸發器數已達926324931:孫宇晨在推特表示,TRON網絡智能合約觸發器數(smart contract triggers)已達926324931。[2020/7/31]

由上圖可見,當前最快的打包速度對應的GasPrice為2,我們只需要在發送交易時將GasPrice設置為>=2的值就可以被盡快打包。

好了,到這里相信大家已經可以大致猜出搶跑的攻擊方式了,就是在發送交易時將GasPrice調高從而被礦工優先打包。下面我們還是通過一個合約代碼來帶大家了解搶跑是如何完成攻擊的。

合約示例

//?SPDX-License-Identifier:?MITpragmasolidity^0.8.17;contractFindThisHash{??bytes32publicconstanthash=????0x564ccaf7594d66b1eaaea24fe01f0585bf52ee70852af4eac0cc4b04711cd0e2;??constructor()payable{}??functionsolve(stringmemorysolution)public{????require(hash==keccak256(abi.encodePacked(solution)),"Incorrectanswer");????(boolsent,)=msg.sender.call{value:10ether}("");????require(sent,"FailedtosendEther");??}}

攻擊分析

通過合約代碼可以看到?FindThisHash?合約的部署者給出了一個哈希值,任何人都可以通過solve()?提交答案,只要solution的哈希值與部署者的哈希值相同就可以得到10個以太的獎勵。我們這里排除部署者自己拿取獎勵的可能。

我們還是請出老朋友Eve看看他是如何使用搶跑攻擊拿走本該屬于Bob的獎勵的:

1.Alice使用10Ether部署FindThisHash合約;

現場 | 以太坊行業峰會第一場圓桌 如何應對智能合約安全問題:金色財經9月8日現場報道,Loi Luu (KyberNetwork 創始人)、Shawn Douglass

(Amberdata 聯合創始人兼CEO)、Jay Zhou (Loopring Protocol 聯合創始人)、Alan Li (FBG X 研發部負責人)、Antoine Cote (Enuma Technologies 聯合創始人兼CEO) 參與了討論,嘉賓們一致認為,由于智能合約涉及資產交易且不可篡改,一旦發生安全問題將引起資產嚴重受損,關注智能合約安全是必要的。Loi Luu指出,區塊鏈行業項目方,需要為開發者設立預警機制。開源代碼要引入第三方檢測,避免犯一些顯而易見的錯誤。

Jay zhou認為,需要更有經驗的開發者去開發用戶易用、以人為本的合約,同時也有必要引入交叉審計。

Shawn Douglass說:“因為智能合約的不可篡改性,在程序員開發前,就必須告知開發者未來可能造成的后果,特別是在涉及資產的領域必須確保事前萬無一失甚至考慮保險。”

Antoine Cote表示,可以在早期時候引入懸賞機制讓黑客或者社區成員共同發現漏洞。大家討論后得出共識,每一個開發者都必須在開發速度和安全性之間做一個平衡。在區塊鏈領域,注重安全和責任心是必須的。[2018/9/8]

2.Bob找到哈希值為目標哈希值的正確字符串;

3.Bob調用solve("Ethereum")并將Gas價格設置為15Gwei;

4.Eve正在監控交易池,等待有人提交正確的答案;

5.Eve看到Bob發送的交易,設置比Bob更高的GasPrice,調用solve("Ethereum");

6.Eve的交易先于Bob的交易被礦工打包;

7.Eve贏得了10個以太幣的獎勵。

這里Eve的一系列操作就是標準的搶跑攻擊,我們這里就可以給以太坊中的搶跑下一個定義:搶跑就是通過設置更高的GasPrice來影響交易被打包的順序,從而完成攻擊。

MIT正在測試一個智能合同驅動的比特幣閃電網絡:上周,麻省理工學院(MIT)透露,其已經演示了比特幣閃電網絡的實驗性用例,展示了如何將它與智能合約結合起來,使比特幣網絡不僅能夠處理數百萬筆交易,而且還能夠以更高程度的復雜度來進行處理。MIT創造性地使用所謂的“預言機”(oracle,一種可信的實體,旨在向智能合約廣播數據)使得演示的成功成為可能的。這是第一次有運行代碼的原型實現。[2018/5/23]

那么這類攻擊該如何避免呢?

修復建議

在編寫合約時可以使用Commit-Reveal方案:

https://medium.com/swlh/exploring-commit-reveal-schemes-on-ethereum-c4ff5a777db8

SoliditybyExample中提供了下面這段修復代碼,我們來看看它是否可以完美地防御搶跑攻擊。

//SPDX-License-Identifier:MITpragmasolidity^0.8.17;import"github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/Strings.sol";contractSecuredFindThisHash{??//Structisusedtostorethecommitdetails??structCommit{????bytes32solutionHash;????uintcommitTime;????boolrevealed;??}??//Thehashthatisneededtobesolved??bytes32publichash=????0x564ccaf7594d66b1eaaea24fe01f0585bf52ee70852af4eac0cc4b04711cd0e2;??//Addressofthewinner??addresspublicwinner;??//Pricetoberewarded??uintpublicreward;??//Statusofgame??boolpublicended;??//Mappingtostorethecommitdetailswithaddress??mapping(address=>Commit)commits;??//Modifiertocheckifthegameisactive??modifiergameActive(){????require(!ended,"Alreadyended");????_;??}??constructor()payable{????reward=msg.value;??}??/*???Commitfunctiontostorethehashcalculatedusingkeccak256(addressinlowercase+solution+secret).???Userscanonlycommitonceandifthegameisactive.??*/??functioncommitSolution(bytes32_solutionHash)publicgameActive{????Commitstoragecommit=commits;????require(commit.commitTime==0,"Alreadycommitted");????commit.solutionHash=_solutionHash;????commit.commitTime=block.timestamp;????commit.revealed=false;??}??/*????Functiontogetthecommitdetails.Itreturnsatupleof(solutionHash,commitTime,revealStatus);?????UserscangetsolutiononlyifthegameisactiveandtheyhavecommittedasolutionHash??*/??functiongetMySolution()publicviewgameActivereturns(bytes32,uint,bool){????Commitstoragecommit=commits;????require(commit.commitTime!=0,"Notcommittedyet");????return(commit.solutionHash,commit.commitTime,commit.revealed);??}??/*????Functiontorevealthecommitandgetthereward.????UserscangetrevealsolutiononlyifthegameisactiveandtheyhavecommittedasolutionHashbeforethisblockandnotrevealedyet.????Itgeneratesankeccak256(msg.sender+solution+secret)andchecksitwiththepreviouslycommitedhash.?????Frontrunnerswillnotbeabletopassthischecksincethemsg.senderisdifferent.????Thentheactualsolutionischeckedusingkeccak256(solution),ifthesolutionmatches,thewinnerisdeclared,????thegameisendedandtherewardamountissenttothewinner.??*/??functionrevealSolution(????stringmemory_solution,????stringmemory_secret)publicgameActive{????Commitstoragecommit=commits;????require(commit.commitTime!=0,"Notcommittedyet");????require(commit.commitTime<block.timestamp,"Cannotrevealinthesameblock");????require(!commit.revealed,"Alreadycommitedandrevealed");????bytes32solutionHash=keccak256(??????abi.encodePacked(Strings.toHexString(msg.sender),_solution,_secret)????);????require(solutionHash==commit.solutionHash,"Hashdoesn'tmatch");????require(keccak256(abi.encodePacked(_solution))==hash,"Incorrectanswer");????winner=msg.sender;????ended=true;????(boolsent,)=payable(msg.sender).call{value:reward}("");????if(!sent){??????winner=address(0);??????ended=false;??????revert("Failedtosendether.");????}??}}

SmartMesh發布關于“以太坊智能合約溢出漏洞事件”的公告: SMT官方微博發布公告,公布最新進展:

1.本次漏洞產生的Token增減、交易數據如下。

①.由ETH智能合約漏洞生成的“假幣”總數:65,133,050,195,990,400,000,000,000,000,000,000,000,000,000,000,000,000,000,000.891004451135422463

②.由黑客地址轉至交易所的“假幣”總數:65,300,289

③.在交易所交易的“假幣”總數:16,638,887(待最終確認)

黑客地址轉入交易所的假幣中,未被交易的假幣已全被交易所凍結。

2.生成并已經流通于交易所的“假幣”,SmartMesh基金會稱將拿出對等的SMT數量凍結及銷毀,以彌補所造成的損失,保持SMT的總數在3,141,592,653。

3.SmartMesh團隊已與Huobi、Gate、OKEx、CEX等多家交易所溝通、協調重新開通SMT交易對交易的具體時間以及其他相關措施,并在努力確認相關事項處理完畢后重啟SMT在各交易所充提幣。[2018/4/25]

首先可以看到修復代碼中使用了結構體Commit記錄玩家提交的信息,其中:

commit.solutionHash=_solutionHash=keccak256

commit.commitTime=block.timestamp

commit.revealed=false

下面我們看這個合約是如何運作的:

1.Alice使用十個以太部署SecuredFindThisHash合約;

2.Bob找到哈希值為目標哈希值的正確字符串;

3.Bob計算solutionHash=keccak256(Bob’sAddress+“Ethereum”+Bob’ssecret);

4.Bob調用commitSolution(_solutionHash),提交剛剛算出的solutionHash;

5.Bob在下個區塊調用revealSolution("Ethereum",Bob'ssecret)函數,傳入答案和自己設置的密碼,領取獎勵。

這里我們看下這個合約是如何避免搶跑的,首先在第四步的時候,Bob提交的是這三個值的哈希,所以沒有人知道Bob提交的內容到底是什么。這一步還記錄了提交的區塊時間并且在第五步的revealSolution()?中就先檢查了區塊時間,這是為了防止在同一個區塊開獎被搶跑,因為調用revealSolution()?時需要傳入明文答案。最后使用Bob輸入的答案和密碼驗證與之前提交的solutionHash哈希是否匹配,這一步是為了防止有人不走commitSolution()?直接去調用revealSolution()。驗證成功后,檢查答案是否正確,最后發放獎勵。

所以這個合約真的完美地防止了Eve抄答案嗎?

Ofcoursenot!

咋回事呢?我們看到在revealSolution()?中僅限制了commit.commitTime<block.timestamp?,所以假設Bob在第一個區塊提交了答案,在第二個區塊立馬調用revealSolution("Ethereum",Bob'ssecret)?并設置GasPrice=15Gwei?Eve,通過監控交易池拿到答案,拿到答案后他立即設置GasPrice=100Gwei,在第二個區塊中調用commitSolution()?,提交答案并構造多筆高GasPrice的交易,將第二個區塊填滿,從而將Bob提交的交易擠到第三個區塊中。在第三個區塊中以100Gwei的GasPrice調用revealSolution("Ethereum",Eve'ssecret)?,得到獎勵。

那么問題來了,如何才能有效地防止此類攻擊呢?

很簡單,只需要設置uint256revealSpan?值并在commitSolution()中檢查?require(commit.commitTime+revealSpan>=block.timestamp,"Cannotcommitinthisblock");,這樣就可以防止Eve抄答案的情況。但是在開獎的時候,還是無法防止提交過答案的人搶先領獎。

另外還有一點,本著代碼嚴謹性,修復代碼中的revealSolution()?函數執行完后并沒有將commit.revealed?設為True,雖然這并不會影響什么,但是在編寫代碼的時候還是建議養成良好的編碼習慣,執行完函數邏輯后將開關設置成正確的狀態。

Tags:MITCOMMMITOMMIunlimitedipCOMOS價格MMIT幣OMMI價格

SHIB
韓國法院計劃將《遺產與贈與稅法》應用于虛擬資產評估_虛擬資產

據首爾經濟日報消息,韓國法院計劃根據《遺產與贈與稅法》評估和處理破產個人或公司擁有的虛擬資產。據悉,韓國首爾破產法院虛擬資產研究小組計劃于今年上半年發布《破產案件虛擬資產處理指南》.

1900/1/1 0:00:00
《紐約時報》記者:本周早些時候,我被企鵝“襲擊”了_NFT

原文標題:《IJoinedaPenguinNFTClubBecauseApparentlyThat’sWhatWeDoNow》作者:KevinRoose翻譯:Odaily|?不一樣的NFT 《紐.

1900/1/1 0:00:00
BSC鏈上DeFi協議AutoShark Finance再遭攻擊,攻擊者獲利200萬美元_BSC

PeckShield在推特上表示,BSC鏈上DeFi協議AutoSharkFinance再次遭遇黑客攻擊,攻擊者獲利200萬美元,AutoShark協議遭遇的損失則可能高于200萬美元.

1900/1/1 0:00:00
誰分走了 MEV 中最大的一塊蛋糕_MEV

原文:EIGENPHI?編譯:GWEIResearch 深入研究以太坊經濟的復雜性,最大可提取價值供應鏈的好處呈現出令人著迷的研究.

1900/1/1 0:00:00
StarkNet在測試網上線Alpha 0.8.0版本,首次展示費用機制_STA

巴比特訊,3月14日,以太坊Layer2開發商StarkWare宣布StarkNet在測試網上線Alpha0.8.0版本,推出根據計算復雜度定價的費用機制,但當前版本不會強制支付費用.

1900/1/1 0:00:00
三方聚首,波卡平行鏈插槽拍賣有何吸引之處?_DOT

本篇文章為Polkadot社區志愿者組織提供的第二篇文章。眾所周知波卡的平行鏈插槽數量是有限的,因而項目方想要成為波卡的平行鏈,必須要抵押DOT,和其他項目方一起競爭插槽的使用權,我們把平行鏈插.

1900/1/1 0:00:00
ads