挑战概述
目标
用户初始拥有 10 WETH,需要通过攻击使余额超过 20 WETH。
解题条件 (IsSolved.sol)
if (WETH.balanceOf(user) > 20 ether) {
console.log("is-solved:true");
}
初始状态
| 组件 | 状态 |
|---|---|
| VUL-WETH 流动池 | 1000 VUL : 1000 WETH |
| Farming 合约 | whale 质押了 1000 WETH |
| Oracle 价格 | VUL:WETH = 0.98:1 |
| 用户 | 10 WETH |
合约分析
Farming.sol - 核心漏洞合约
contract Farming is ReentrancyGuard, IFarming {
IWETH public WETH;
IMintableERC20 public VUL;
IOracle public ORACLE;
IUniswapV2Router02 public UNISWAPV2_ROUTER02;
mapping(address => uint256) public shares;
uint256 public totalShares = 0;
uint256 lastAccumulateTimestamp;
uint256 yieldVulPerSecond; // 每秒产生 0.1 VUL
关键函数 1: deposit()
function deposit(uint256 amount) external nonReentrant {
require(amount > 0, "Farming: Amount must be greater than 0");
// 先累积收益
accumulateYield(getDefaultPath());
uint currentBalance = WETH.balanceOf(address(this));
uint currentShares = totalShares;
// 计算新 shares
uint newShares;
if (currentShares == 0) {
newShares = amount;
} else {
newShares = (amount * currentShares) / currentBalance;
}
shares[msg.sender] += newShares;
totalShares += newShares;
WETH.transferFrom(msg.sender, address(this), amount);
}
关键函数 2: accumulateYield() - 漏洞所在
// ⚠️ 漏洞:public 函数,任何人可调用,且可传入自定义 path
function accumulateYield(address[] memory path) public {
uint256 secs = block.timestamp - lastAccumulateTimestamp;
uint256 yield = secs * yieldVulPerSecond;
if (yield == 0) {
return;
}
lastAccumulateTimestamp = block.timestamp;
VUL.mint(address(this), yield); // mint VUL 作为收益
swapYieldToWeth(path); // 通过自定义路径 swap
}
关键函数 3: swapYieldToWeth()
function swapYieldToWeth(address[] memory path) private {
// 仅检查路径起点和终点
require(path.length > 1, "Farming: Path must have at least 2 elements");
require(path[0] == address(VUL), "Farming: Path must start with VUL");
require(path[path.length - 1] == address(WETH), "Farming: Path must end with WETH");
uint256 amount = VUL.balanceOf(address(this));
uint256 expectedWethAmount = (ORACLE.vulToWethPrice() * amount) / 1e18;
VUL.approve(address(UNISWAPV2_ROUTER02), amount);
// ⚠️ 使用用户提供的 path 进行 swap
UNISWAPV2_ROUTER02.swapExactTokensForTokensSupportingFeeOnTransferTokens(
amount,
expectedWethAmount,
path, // 自定义路径!
address(this),
block.timestamp
);
}
漏洞分析
漏洞 1: accumulateYield() 是 public 函数
任何人都可以调用此函数,触发收益累积和 swap。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
















暂无评论内容