前言
📌 在做 damn defi
的 backdoor
挑战时,关于如何才能让 proxy
合约给 hacker
执行 approve
授权操作,引发的深思。
我们知道,delegatecall
是很特殊的调用方式,委托调用,代码是在逻辑合约 Proxy
中执行。
如果我们通过这种方式执行 Caller --call--> Proxy --delegatecall--> Logic1 --delgatecall--> Logic2
对 Logic2
来说,如若其函数体呢有 msg.sender
和 address(this)
变量的话,
易知,此时的 msg.sender = address(Caller), address(this) = address(Proxy)
📌 但是,如果在 delegatecall
传递链上出现了call
那么就很离谱了。
1. 多重传递
按如下传递链进行函数的调用。
Caller --call--> Proxy --delegatecall--> Logic1 --delgatecall--> Logic2 --call--> Logic3
关键看 Logic3
的 msg.sender
和address(this)
的变化。
2. 代码演示
代码是不能正常运行的,不过不重要,我只要函数的调用过程即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| // SPDX-License-Identifier: MIT pragma solidity ^0.8.4;
import "hardhat/console.sol";
/** Caller --call--> Proxy --delegatecall--> Logic1 --delgatecall--> Logic2 --call--> Logic3
request: find Logic3's msg.sender is or not address(proxy) */
contract Caller{ address public proxy;
constructor(address proxy_){ proxy = proxy_; } function delegatecall_approve(address _logic2, address _logic3) external returns(bool , address) { console.log("Caller_address= ", address(this)); (bool success , bytes memory data) = proxy.call(abi.encodeWithSignature("funLogic1(address,address)", _logic2, _logic3)); return (success, abi.decode(data,(address))); }
}
contract Proxy {
address public implementation;
constructor(address implementation_){ implementation = implementation_; }
fallback() external payable { _delegate(); }
function _delegate() public { address _implementation; console.log("Proxy_address= ", address(this)); assembly { _implementation := sload(0) calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), _implementation, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } }
contract Logic1 {
address public implementation;
function funLogic1(address _logic2, address _logic3) external { console.log("Logic1_msg.sender= ", msg.sender); console.log("Logic1_address= ", address(this)); _logic2.delegatecall(abi.encodeWithSignature("funLogic2(address)",_logic3)); } }
contract Logic2 {
address public implementation;
function funLogic2(address _logic3) external { console.log("Logic2_msg.sender= ", msg.sender); console.log("Logic2_address= ", address(this)); _logic3.call(abi.encodeWithSignature("funLogic3()")); } }
contract Logic3 {
address public implementation;
function funLogic3() external { console.log("Logic3_msg.sender= ", msg.sender); console.log("Logic3_address= ", address(this)); } }
|
3. 运行结果

📌 由结果不难看出,对 Logic3
来说,ta 的调用者已成为了 Proxy
合约,且address(this)
也是ta本合约的地址了,我之前做题的时候想破脑袋都不知道怎么做到让 代理合约给 Hack 合约授权,因为我最开始认为,在 Logic2
中执行 call
,相对于Logic3
来说 调用者应该是 Logic2
才对,想了一天实在受不了了,只能实践出真知了。md。。。。结果真的是出乎我的认知。