首页
Search
1
yamux: how to work?
79 阅读
2
The Art of Memory Allocation: Malloc, Slab, C++ STL, and GoLang Memory Allocation
70 阅读
3
How to receive a network packet in Linux
63 阅读
4
Maps and Memory Leaks in Go
54 阅读
5
C++ redis connection pool
52 阅读
测试
Wireguard
K8s
Redis
C++
Golang
Libcurl
Tailscale
Nginx
Linux
web3
Uniswap V2
Uniswap V3
EVM
security
solidity
openzeppelin
登录
Search
标签搜索
web3
solidity
web3 security
c++
uniswapV3
redis
evm
uniswap
性能测试
k8s
wireguard
CNI
http
tailscale
nginx
linux
设计模式
Jericho
累计撰写
51
篇文章
累计收到
13
条评论
首页
栏目
测试
Wireguard
K8s
Redis
C++
Golang
Libcurl
Tailscale
Nginx
Linux
web3
Uniswap V2
Uniswap V3
EVM
security
solidity
openzeppelin
页面
搜索到
21
篇与
的结果
2025-08-26
Damn-vulnerable-defi-V4-solution(Unstoppable)
一、Unstoppable// UnstoppableMonitor.sol // SPDX-License-Identifier: MIT // Damn Vulnerable DeFi v4 (https://damnvulnerabledefi.xyz) pragma solidity =0.8.25; import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"; import {Owned} from "solmate/auth/Owned.sol"; import {UnstoppableVault, ERC20} from "../unstoppable/UnstoppableVault.sol"; /** * @notice Permissioned contract for on-chain monitoring of the vault's flashloan feature. */ contract UnstoppableMonitor is Owned, IERC3156FlashBorrower { UnstoppableVault private immutable vault; error UnexpectedFlashLoan(); event FlashLoanStatus(bool success); constructor(address _vault) Owned(msg.sender) { vault = UnstoppableVault(_vault); } function onFlashLoan(address initiator, address token, uint256 amount, uint256 fee, bytes calldata) external returns (bytes32) { if (initiator != address(this) || msg.sender != address(vault) || token != address(vault.asset()) || fee != 0) { revert UnexpectedFlashLoan(); } ERC20(token).approve(address(vault), amount); return keccak256("IERC3156FlashBorrower.onFlashLoan"); } function checkFlashLoan(uint256 amount) external onlyOwner { require(amount > 0); address asset = address(vault.asset()); try vault.flashLoan(this, asset, amount, bytes("")) { emit FlashLoanStatus(true); } catch { // Something bad happened emit FlashLoanStatus(false); // Pause the vault vault.setPause(true); // Transfer ownership to allow review & fixes vault.transferOwnership(owner); } } } // UnstoppableVault.sol // SPDX-License-Identifier: MIT // Damn Vulnerable DeFi v4 (https://damnvulnerabledefi.xyz) pragma solidity =0.8.25; import {ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {Owned} from "solmate/auth/Owned.sol"; import {SafeTransferLib, ERC4626, ERC20} from "solmate/tokens/ERC4626.sol"; import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol"; import {IERC3156FlashBorrower, IERC3156FlashLender} from "@openzeppelin/contracts/interfaces/IERC3156.sol"; /** * An ERC4626-compliant tokenized vault offering flashloans for a fee. * An owner can pause the contract and execute arbitrary changes. */ contract UnstoppableVault is IERC3156FlashLender, ReentrancyGuard, Owned, ERC4626, Pausable { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; uint256 public constant FEE_FACTOR = 0.05 ether; uint64 public constant GRACE_PERIOD = 30 days; uint64 public immutable end = uint64(block.timestamp) + GRACE_PERIOD; address public feeRecipient; error InvalidAmount(uint256 amount); error InvalidBalance(); error CallbackFailed(); error UnsupportedCurrency(); event FeeRecipientUpdated(address indexed newFeeRecipient); constructor(ERC20 _token, address _owner, address _feeRecipient) ERC4626(_token, "Too Damn Valuable Token", "tDVT") Owned(_owner) { feeRecipient = _feeRecipient; emit FeeRecipientUpdated(_feeRecipient); } /** * @inheritdoc IERC3156FlashLender */ function maxFlashLoan(address _token) public view nonReadReentrant returns (uint256) { if (address(asset) != _token) { return 0; } return totalAssets(); } /** * @inheritdoc IERC3156FlashLender */ function flashFee(address _token, uint256 _amount) public view returns (uint256 fee) { if (address(asset) != _token) { revert UnsupportedCurrency(); } if (block.timestamp < end && _amount < maxFlashLoan(_token)) { return 0; } else { return _amount.mulWadUp(FEE_FACTOR); } } /** * @inheritdoc ERC4626 */ function totalAssets() public view override nonReadReentrant returns (uint256) { return asset.balanceOf(address(this)); } /** * @inheritdoc IERC3156FlashLender */ function flashLoan(IERC3156FlashBorrower receiver, address _token, uint256 amount, bytes calldata data) external returns (bool) { if (amount == 0) revert InvalidAmount(0); // fail early if (address(asset) != _token) revert UnsupportedCurrency(); // enforce ERC3156 requirement uint256 balanceBefore = totalAssets(); if (convertToShares(totalSupply) != balanceBefore) revert InvalidBalance(); // enforce ERC4626 requirement // transfer tokens out + execute callback on receiver ERC20(_token).safeTransfer(address(receiver), amount); // callback must return magic value, otherwise assume it failed uint256 fee = flashFee(_token, amount); if ( receiver.onFlashLoan(msg.sender, address(asset), amount, fee, data) != keccak256("IERC3156FlashBorrower.onFlashLoan") ) { revert CallbackFailed(); } // pull amount + fee from receiver, then pay the fee to the recipient ERC20(_token).safeTransferFrom(address(receiver), address(this), amount + fee); ERC20(_token).safeTransfer(feeRecipient, fee); return true; } /** * @inheritdoc ERC4626 */ function beforeWithdraw(uint256 assets, uint256 shares) internal override nonReentrant {} /** * @inheritdoc ERC4626 */ function afterDeposit(uint256 assets, uint256 shares) internal override nonReentrant whenNotPaused {} function setFeeRecipient(address _feeRecipient) external onlyOwner { if (_feeRecipient != address(this)) { feeRecipient = _feeRecipient; emit FeeRecipientUpdated(_feeRecipient); } } // Allow owner to execute arbitrary changes when paused function execute(address target, bytes memory data) external onlyOwner whenPaused { (bool success,) = target.delegatecall(data); require(success); } // Allow owner pausing/unpausing this contract function setPause(bool flag) external onlyOwner { if (flag) _pause(); else _unpause(); } } 为了让 monitor 将 vault 的状态修改为 stop,需要在monitor 调用 flashLoan 的时候触发 revert,flashLoan 中要触发 revert,比较好触发的就是这行if (convertToShares(totalSupply) != balanceBefore) revert InvalidBalance();这块代码有一些问题,这个检查试图确保Vault的资产余额与份额总供应量转换后的值一致,但因为它比较的是两种不同的单位(份额和资产数量),而且当有人直接向Vault转账token时(而不是通过deposit函数),资产余额会增加但份额不变,导致检查失败,从而使flashLoan功能被禁用。修复之后/** * @inheritdoc IERC3156FlashLender */ function flashLoan(IERC3156FlashBorrower receiver, address _token, uint256 amount, bytes calldata data) external returns (bool) { if (amount == 0) revert InvalidAmount(0); // fail early if (address(asset) != _token) revert UnsupportedCurrency(); // enforce ERC3156 requirement uint256 balanceBefore = totalAssets(); // 注释了这里的检查 // if (convertToShares(totalSupply) != balanceBefore) revert InvalidBalance(); // enforce ERC4626 requirement // transfer tokens out + execute callback on receiver ERC20(_token).safeTransfer(address(receiver), amount); // callback must return magic value, otherwise assume it failed uint256 fee = flashFee(_token, amount); if ( receiver.onFlashLoan(msg.sender, address(asset), amount, fee, data) != keccak256("IERC3156FlashBorrower.onFlashLoan") ) { revert CallbackFailed(); } // pull amount + fee from receiver, then pay the fee to the recipient ERC20(_token).safeTransferFrom(address(receiver), address(this), amount + fee); ERC20(_token).safeTransfer(feeRecipient, fee); // 此处增加了借贷之前和之后的资产余额比较 确保Vault的资产余额没有减少 if (totalAssets() < balanceBefore) revert InvalidBalance(); return true; }
2025年08月26日
25 阅读
0 评论
1 点赞
1
...
4
5