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

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

Author:

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

背景概述

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

前置知識

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

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

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

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

2. 選擇 Gas Price;

3. 發送簽名后的交易;

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

5. 交易進入交易池;

6. 礦工取出 Gas Price 高的交易;

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

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

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

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

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

Tx Fee(手續費)= Gas Used(燃料用量)*  Gas Price(單位燃料價格)

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

舉個例子:

例如 Gas Price 設置為 10 GWEI,Gas Used 為 21,000(WEI 是以太坊上最小的單位 1 WEI = 10^-18 個 Ether,GWEI 則是 1G 的 WEI,1 GWEI = 10^-9 個 Ether)。因此,根據手續費計算公式可以算出手續費為:

10 GWEI(單位燃料價格)* 21,000(燃料用量)= 0.00021 Ether(手續費)

神話起源CEO談毅:以太坊GAS和速度以及智能合約是未來突破的方向:金色財經現場報道,在8月8日由金色財經主辦的金色沙龍活動中,神話起源CEO談毅在主題《哪些Web3敘事將引領下輪牛市》的圓桌會議中表示,以太坊有三個大家可以關注的點,以及存在的核心價值。一個是GAS,一個是速度,一個是智能合約,這是以太坊做這么大的核心。換句話說,未來的突破也在這三個方向上。

以GAS和速度為例,一個是2.0的升級,今年下半年能不能如期推出,推出以后的效果,能不能像人們預期的那樣。這有可能是一下子給加密資產帶來一個新的臺階。做L2的團隊,也在L2層面上解決這個問題,一個是速度,一個是GAS。第三個是智能合約,以太坊就是行業標準。它從NFT開始,ERC-721以后,還有EIP-1159,今年好像也有新的智能合約的協議會提交出來做審核。所以如果從這三個點上突破的話,以太坊的價值和生態能進一步的擴大和提升。尤其是智能合約。今年可以看到有很多創新想法,隨著NFT往下發展,在用新的智能合約做,這都是以太坊可以關注的點。

此外,他還表示,游戲資產能天然跟區塊鏈做融合,既符合有非常大的資產價格波動性,又是可觸及的。通過可觸及的方式,讓更多的圈外人進來。[2023/8/8 21:32:31]

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

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

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

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

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

合約示例

// SPDX-License-Identifier: MITpragma solidity ^0.8.17;contract FindThisHash {    bytes32 public constant hash =        0x564ccaf7594d66b1eaaea24fe01f0585bf52ee70852af4eac0cc4b04711cd0e2;    constructor() payable {}    function solve(string memory solution) public {        require(hash == keccak256(abi.encodePacked(solution)), "Incorrect answer");        (bool sent, ) = msg.sender.call{value: 10 ether}("");        require(sent, "Failed to send Ether");    }}

Curve:不需要重新部署crvUSD的智能合約:金色財經報道,Curve發推表示,已經進行了更多crvUSD測試,到目前為止,不需要重新部署智能合約,用戶界面將于本周上線。[2023/5/9 14:50:48]

攻擊分析

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

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

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

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

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

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

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

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

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

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

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

修復建議

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

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

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

// SPDX-License-Identifier: MITpragma solidity ^0.8.17;import "github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/Strings.sol";contract SecuredFindThisHash {    // Struct is used to store the commit details    struct Commit {        bytes32 solutionHash;        uint commitTime;        bool revealed;    }    // The hash that is needed to be solved    bytes32 public hash =        0x564ccaf7594d66b1eaaea24fe01f0585bf52ee70852af4eac0cc4b04711cd0e2;    // Address of the winner    address public winner;    // Price to be rewarded    uint public reward;    // Status of game    bool public ended;    // Mapping to store the commit details with address    mapping(address => Commit) commits;    // Modifier to check if the game is active-irjw    modifier gameActive() {        require(!ended, "Already ended");        _;    }    constructor() payable {        reward = msg.value;    }    /*       Commit function to store the hash calculated using keccak256(address in lowercase + solution + secret).       Users can only commit once and if the game is active.    */    function commitSolution(bytes32 _solutionHash) public gameActive {        Commit storage commit = commits[msg.sender];        require(commit.commitTime == 0, "Already committed");        commit.solutionHash = _solutionHash;        commit.commitTime = block.timestamp;        commit.revealed = false;    }    /*        Function to get the commit details. It returns a tuple of (solutionHash, commitTime, revealStatus);          Users can get solution only if the game is active-irjw and they have committed a solutionHash    */    function getMySolution() public view gameActive returns (bytes32, uint, bool) {        Commit storage commit = commits[msg.sender];        require(commit.commitTime != 0, "Not committed yet");        return (commit.solutionHash, commit.commitTime, commit.revealed);    }    /*        Function to reveal the commit and get the reward.        Users can get reveal solution only if the game is active-irjw and they have committed a solutionHash before this block and not revealed yet.        It generates an keccak256(msg.sender + solution + secret) and checks it with the previously commited hash.          Front runners will not be able to pass this check since the msg.sender is different.        Then the actual solution is checked using keccak256(solution), if the solution matches, the winner is declared,        the game is ended and the reward amount is sent to the winner.    */    function revealSolution(        string memory _solution,        string memory _secret) public gameActive {        Commit storage commit = commits[msg.sender];        require(commit.commitTime != 0, "Not committed yet");        require(commit.commitTime < block.timestamp, "Cannot reveal in the same block");        require(!commit.revealed, "Already commited and revealed");        bytes32 solutionHash = keccak256(            abi.encodePacked(Strings.toHexString(msg.sender), _solution, _secret)        );        require(solutionHash == commit.solutionHash, "Hash doesn't match");        require(keccak256(abi.encodePacked(_solution)) == hash, "Incorrect answer");        winner = msg.sender;        ended = true;        (bool sent, ) = payable(msg.sender).call{value: reward}("");        if (!sent) {            winner = address(0);            ended = false;            revert("Failed to send ether.");        }    }}

DFINITY社區批準了啟用容器智能合約以轉移ICP代幣的提案:9月20日消息,DFINITY社區NSN提案(#20588)于9月17日獲得通過,該提案建議使所有主體(包括容器)能夠轉移ICP實用程序代幣,計劃批準智能合約容器傳輸ICP,并擴大對所有主體類型的支持,取消特定限制以便所有主體可持有和轉移ICP代幣。同時,所有自認證和列入白名單的容器都可繼續傳輸ICP。據悉,該提案的具體實施將需要幾個月時間,研究團隊將在未來向社區提出沙盒項目,其中將發生通常的“開放式設計”迭代過程。[2021/9/20 23:37:36]

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

commit.solutionHash = _solutionHash = keccak256(玩家地址 + 答案 + 密碼)[記錄玩家提交的答案哈希]

commit.commitTime = block.timestamp [記錄提交時間]

commit.revealed = false [記錄狀態]

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

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

3. Bob 計算 solutionHash = keccak256 (Bob’s Address + “Ethereum” + Bob’s secret);

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

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

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

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

Of course not!

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

以太坊擴容項目 Matter Labs 為 Curve 推出首個 zkSync 二層智能合約測試網:以太坊擴容項目 Matter Labs 為穩定幣兌換平臺 Curve 推出首個 zkSync 二層智能合約測試網「Zinc Alef」。Matter Labs 現已引入 Zinc 編程語言和對 SNARK 友好的 Zinc VM (虛擬機),并為以太坊實施了遞歸 PLONK 證明驗證。目前,必須使用 Zinc 編程語言來編寫 Zinc VM 智能合約。zkSync 二層網絡中的所有合約都將能夠以與以太坊主網上完全相同的方式互相調用。目前,測試網功能齊全,用戶可編寫智能合約并將其部署至測試網,然后對此進行本地測試,并生成智能合約執行的零知識證明。每筆交易都將導致 zkSync 測試網上的真實代幣發生轉移,也會反映至區塊瀏覽器和錢包中。不過,Zinc VM 尚未集成至 zkSync core 中,Zinc 編程語言中的一些重要功能也可能會丟失。Matter Labs 將根據社區的要求優先開發該功能。另外,Matter Labs 正在與其他團隊合作開發通用的以太坊二層簽名標準,這將提升圍繞二層合約進行交互的用戶體驗。此前報道,今年 6 月份,Matter Labs 在以太坊主網部署了 Layer 2 擴展工具 zkSync。[2020/10/10]

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

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

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

慢霧科技

個人專欄

閱讀更多

金色薦讀

金色財經 善歐巴

迪新財訊

Chainlink預言機

區塊律動BlockBeats

白話區塊鏈

金色早8點

Odaily星球日報

MarsBit

Arcane Labs

聲音 | 安永區塊鏈專家:對于公鏈上的通證和智能合約 監管機構應提出明確的評估準則:據Coindesk消息,安永會計事務所區塊鏈專家Paul Brody近日發文表示,公有鏈的生效必須建立在兩個前提上。首先,對于公鏈上的通證和智能合約,監管機構應提出一套明確的評估準則。其次,公司必須在公鏈的運行中遵守這些準則。[2018/9/6]

Tags:NBSBSPMITCOMNBS價格BSPTSimmitriPala Int Comm

Gate交易所
RAK:綠地集團旗下子公司擬申請香港虛擬資產交易牌照

上證報中國證券網訊 5月17日,記者從綠地金創獲悉,該公司正計劃申請在香港交易虛擬資產的牌照。綠地金創是綠地控股集團(下稱“綠地集團”)旗下子公司.

1900/1/1 0:00:00
CHA:美國對 AI 的恐懼 來源于社交媒體的失控

美國時間 5 月 16 日,參議院審議 AI 監管議題的聽證會上,氣氛一派祥和。這是以 ChatGPT 為代表的生成式 AI 科技爆炸性進步后,美國國會舉辦的第一場聽證會,可能奠定未來美國如何監.

1900/1/1 0:00:00
CHA:AI加持的必應 為什么還贏不了谷歌?

即使有新必應的加成,微軟瀏覽器Edge在全球市場的占有率依然不高。據Statcounter數據顯示,2023年4月,Edge的市場占有率僅為4.97%.

1900/1/1 0:00:00
BRE:什么是 “閃電網絡服務商”?

來源:Roy Sheinfeld閃電網絡經濟即將到來,它將粘合兩大基礎元素:技術創新和需求人群。這里的技術就是閃電網絡本身:點對點、免信任、快速、經濟的比特幣支付.

1900/1/1 0:00:00
BCH:晚間必讀 | 上線2個月的Polygon zkEVM 生態發展如何

以太坊網絡的共識是目前安全性最高的加密經濟系統之一。1800萬枚 ETH(約 340 億美元)的驗證者每 6.4 分鐘最終確定一個區塊,運行許多.

1900/1/1 0:00:00
NOV:投資機構盡調不充分?FTX B輪融資宣傳資料早已透露危險信號

隨著因 FTX倒閉而名譽掃地的知名投資機構越來越長,The Block 邀請一位法律專家查看 FTX 在 2020 年制作的融資宣傳資料,以尋找可能被遺漏的警告信號.

1900/1/1 0:00:00
ads