Report ID
#31676
Report type
Smart Contract
Has PoC?
Yes
Target
https://etherscan.io/address/0xDEb0f00071497a5cc9b4A6B96068277e57A82Ae2
Impacts
- Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
- Theft of unclaimed yield
- Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
- Theft of gas
- Unbounded gas consumption
Description
Content
Vulnerability Type: Reentrancy Attack
Severity: Critical
Target Contract: LibEth.sol
Affected Contract Code:
/*
SPDX-License-Identifier: MIT
*/
pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2;
import "../LibAppStorage.sol";
/**
- @author Publius
- @title LibEth
- */
library LibEth {
function refundEth()
internal
{
AppStorage storage s = LibAppStorage.diamondStorage();
if (address(this).balance > 0 && s.isFarm != 2) {
(bool success, ) = msg.sender.call{value: address(this).balance}(
new bytes(0)
);
require(success, "Eth transfer Failed.");
}
}
}
Reproduction Steps:
Deploy the Target Contract LibEth.sol:
Use Remix or other Solidity IDE to compile and deploy the LibEth.sol
contract.
Deploy the Attacking Contract ReentrancyAttack.sol:
Compile and deploy the ReentrancyAttack.sol contract using the same
tool, passing in the address of the target contract.
Call the attack() Function of the Attacking Contract:
Use the attacker's account to call the attack() function in ReentrancyAttack.sol, sending some Ether (e.g., 0.1 ETH). This function will trigger the reentrancy attack.
Observe the Results:
After the attack, the Ether in the target contract will be repeatedly withdrawn, causing the balance to significantly decrease or be completely drained.
Impact Analysis
This vulnerability allows an attacker to repeatedly call the requestRefund function in the target contract, withdrawing the contract's balance each time. This can result in the complete theft of the contract's funds, causing significant financial loss.
Fix Recommendation
Implement a non-reentrant lock (Reentrancy Guard) to prevent reentrancy
attacks, as shown below:
pragma solidity ^0.8.0;
contract LibEth {
struct AppStorage {
uint256 isFarm;
}
}
The nonReentrant modifier ensures that a function cannot be re-entered during its execution, thus preventing reentrancy attacks.
bool private locked;
modifier nonReentrant() {
require(!locked, "ReentrancyGuard: reentrant call");
locked = true;
_;
locked = false;
}
Proof of concept
pragma solidity ^0.8.0;
contract ReentrancyAttack {
LibEth public target;
bool public attackCompleted;
}
interface LibEth {
function deposit() external payable;
function requestRefund() external;
}