Transfers may partially succeed/fail in unexpected ways, and balance updates may not reflect the intended transfer outcomes in ERC4626 Vault Implementation. #113
Description:Description
The InvestToken vault implementation contains a vulnerability in its share price calculation mechanism. The interaction between YieldOracle price updates and vault operations allows manipulation of the share-to-asset ratio, potentially leading to significant value extraction.
The issue manifests in the _update function implementation across the token contracts.
function _update(
address from,
address to,
uint256 amount
) internal override {
// @Issue - Non-atomic validation check allows race conditions between transfers
require(validator.isValid(from, to), "account blocked");
super._update(from, to, amount);
}
The validator checks are not atomic - between two transfers in the same block, the validator status could change, causing the second transfer to fail while the first succeeds. This breaks the atomicity assumption. Validators could be manipulated by MEV bots to extract value
function isValid(address from, address to) external view returns (bool valid) {
// @Issue - Status checks lack atomicity guarantees across multiple transfers
return accountStatus[from] == Status.BLACKLISTED ? to == address(0x0) : accountStatus[to] != Status.BLACKLISTED;
}
function isValidStrict(address from, address to) external view returns (bool valid) {
// @Issue - Whitelist status can change between transfers in same block
return to == address(0x0)
|| (
accountStatus[to] == Status.WHITELISTED
&& (from == address(0x0) || accountStatus[from] == Status.WHITELISTED)
);
}
The protocol's transfer validation mechanism lacks atomic guarantees when multiple transfers occur within the same block. This creates a race condition where validator status changes can occur between transfers, leading to inconsistent validation results.
This vulnerability directly affects the core transfer functionality of both USDE and InvestToken, impacting all protocol users and integrated systems.
Attack Scenario
Explanation of the two attack scenarios demonstrated:
Transfer Atomicity Attack:
Exploits a race condition in the validation system
Step 1: Alice transfers 500 USDE to attacker
Step 2: Alice's account gets voided mid-block
Step 3: Bob transfers 500 USDE to attacker
Result: Attacker gains 1000 USDE by bypassing intended transfer restrictions
Price Manipulation Attack:
Exploits the oracle update mechanism
Step 1: Alice deposits 100 USDE for shares at 1e18 price
Step 2: Oracle price is doubled to 2e18
Step 3: Share value changes affect asset conversion rates
Result: Demonstrates how price manipulation can impact share/asset ratios
Vulnerabilities:
Non-atomic validation checks in transfers
Oracle price updates without sufficient safeguards
Time-based manipulation opportunities
Attachments
Proof of Concept (PoC) File
This file demonstrates the transfer atomicity and Price Manipulation vulnerability.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import {Test} from "forge-std/Test.sol";
import {console2} from "forge-std/console2.sol";
import {USDE} from "../src/USDE.sol";
import {IUSDE} from "../src/interfaces/IUSDE.sol";
import {InvestToken} from "../src/InvestToken.sol";
import {Validator} from "../src/Validator.sol";
import {YieldOracle} from "../src/YieldOracle.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract TransferAtomicityTest is Test {
USDE public usdeImpl;
USDE public usde;
InvestToken public investTokenImpl;
InvestToken public investToken;
Validator public validator;
YieldOracle public oracle;
Github username: @0xbrett8571 Twitter username: 0xbrett8571 Submission hash (on-chain): 0x23fa0b8694b08ab97bcec4e87c488557deb55b5e170f8032c5e53171d81aa2ff Severity: high
Description: Description The InvestToken vault implementation contains a vulnerability in its share price calculation mechanism. The interaction between YieldOracle price updates and vault operations allows manipulation of the share-to-asset ratio, potentially leading to significant value extraction.
The issue manifests in the
_update
function implementation across the token contracts.USDE.sol#L81-L91
InvestToken.sol#L104-L114
The validator checks are not atomic - between two transfers in the same block, the validator status could change, causing the second transfer to fail while the first succeeds. This breaks the atomicity assumption. Validators could be manipulated by MEV bots to extract value
Validator.sol#L129-L144
The protocol's transfer validation mechanism lacks atomic guarantees when multiple transfers occur within the same block. This creates a race condition where validator status changes can occur between transfers, leading to inconsistent validation results.
This vulnerability directly affects the core transfer functionality of both USDE and InvestToken, impacting all protocol users and integrated systems.
Attack Scenario Explanation of the two attack scenarios demonstrated:
Transfer Atomicity Attack:
Price Manipulation Attack:
Vulnerabilities:
Attachments
import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; import {USDE} from "../src/USDE.sol"; import {IUSDE} from "../src/interfaces/IUSDE.sol"; import {InvestToken} from "../src/InvestToken.sol"; import {Validator} from "../src/Validator.sol"; import {YieldOracle} from "../src/YieldOracle.sol"; import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract TransferAtomicityTest is Test { USDE public usdeImpl; USDE public usde; InvestToken public investTokenImpl; InvestToken public investToken; Validator public validator; YieldOracle public oracle;
}
I. Transfer Atomicity.
II. Price Manipulation.
maxPriceIncrease
limit