0VIX ovGHST Oracle Inflation
Exploit Transactions
0x10f2c28f5d6cd8d7b56210b4d5e0cece27e45a30808cd3d3443c05d4275bb008Victim Addresses
0x8849f1a0cb6b5d6076ab150546eddee193754f1cPolygon0xe053a4014b50666ed388ab8cbb18d5834de0ab12PolygonLoss Breakdown
Similar Incidents
Midas LP Oracle Read-Only Reentrancy via Curve stMATIC/WPOL
35%BonqDAO ALBT Oracle Manipulation via TellorFlex
33%Aave AMM LP Oracle Manipulation Through Delegated Recursive LP Looping
29%Helio Plugin Donation Inflation
27%LunaFi VLFI Reward Replay
27%Sentiment Balancer Oracle Overborrow
26%Root Cause Analysis
0VIX ovGHST Oracle Inflation
1. Incident Overview TL;DR
On Polygon block 42054769, transaction 0x10f2c28f5d6cd8d7b56210b4d5e0cece27e45a30808cd3d3443c05d4275bb008 let an unprivileged attacker inflate the value of ovGHST, the 0VIX market whose underlying asset is vGHST, and then extract value from 0VIX in the same transaction. The attacker did not need privileged keys or private orderflow: they used a fresh exploit contract, public flash-loan liquidity, and public 0VIX/Aavegotchi entrypoints.
The root cause was a bad composition between Aavegotchi's vGHST share accounting and 0VIX's custom ovGHST oracle. vGHST.convertVGHST(1 ether) was donation-sensitive because the vault counted all GHST sitting in the vault address, including unsolicited transfers, as backing for every share. 0VIX's custom oracle then treated that spot conversion as an authoritative collateral price, so a same-transaction GHST donation immediately raised ovGHST collateral value and unlocked excess borrowing power.
2. Key Background
vGHST at 0x51195e21BDaE8722B29919db56d95Ef51FaecA6C is a vault-style share token. Each share represents a claim on GHST and related staked positions, and the share-to-GHST conversion is exposed through convertVGHST(uint256).
ovGHST at 0xE053A4014b50666ED388ab8CbB18D5834de0aB12 is the 0VIX lending market for vGHST. 0VIX therefore depends on a correct vGHST price for collateral checks, borrowing limits, and liquidations.
At the exploit pre-state, 0VIX's oracle adapter OvixChainlinkOracleV2 at 0x1c312b14c129EabC4796b0165A2c470b659E5f01 resolved the ovGHST feed to 0x738FE8A918d5e43B705FC5127450e2300f7b08Ab. Exploit-block RPC reads confirm:
getFeed(ovGHST)returned0x738FE8A918d5e43B705FC5127450e2300f7b08Abat blocks42054768and42054769.getUnderlyingPrice(ovGHST)moved from1169366570000000000to2011981510000000000across the exploit block.
The attacker cluster identified from the transaction is:
- EOA
0x702ef63881b5241ffb412199547bcd0c6910a970, the sender and final profit recipient. - Contract
0x407feaec31c16b19f24a8a8846ab4939ed7d7d57, the exploit contract that received public flash-loan liquidity and executed the on-chain sequence.
3. Vulnerability Analysis & Root Cause Summary
This was an oracle-manipulation attack, not a governance or access-control failure. The vulnerable pricing path was fully on-chain and fully synchronous: 0VIX priced ovGHST by reading a custom feed, and that feed directly multiplied GHST/USD by vGHST.convertVGHST(1 ether). Because vGHST.convertVGHST was derived from the total GHST visible at the vault address, any direct GHST donation immediately raised the reported GHST-per-share ratio without minting corresponding liabilities. That meant flash-loaned GHST could be made to look like durable collateral backing during the same transaction. Once 0VIX accepted the pumped price, the attacker's account health improved instantly, allowing the transaction to borrow and liquidate against mispriced collateral before the flash-loan unwind. The exploit therefore violated the core lending invariant that collateral prices must not be same-transaction manipulable through unsolicited asset transfers.
From the verified vGHST contract:
function totalGHST(address _user) public view returns(uint256 _totalGHST){
uint256 totalGHSTHeld = IERC20Upgradeable(ghstAddress).balanceOf(_user);
...
_totalGHST = totalGHSTHeld + totalGHSTStaked + wapGHSTtoGHST;
}
function convertVGHST(uint256 _share) public view returns(uint256 _ghst){
uint256 totalShares = totalSupply();
uint256 totalTokenLocked = totalGHST(address(this));
_ghst = _share.mul(totalTokenLocked).div(totalShares);
}
From the verified VGHSTOracle contract configured for ovGHST at the exploit block:
function latestRoundData()
external
view
override
returns (uint80, int256, uint256, uint256, uint80)
{
(bool success, bytes memory data) = vGHST.staticcall(
abi.encodeWithSignature("convertVGHST(uint256)", 1 ether)
);
...
(, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)
= ghstFeed.latestRoundData();
int256 price = int((toUint256(data) * uint(answer)) / 10 ** vGHSTDecimals);
return (roundId, price, startedAt, updatedAt, answeredInRound);
}
4. Detailed Root Cause Analysis
The exploit pre-state was Polygon block 42054768, immediately before the attacker transaction entered block 42054769. Independent RPC validation at that pre-state gives:
vGHST.convertVGHST(1e18) = 1038332409239877123VGHSTOracle.latestRoundData().answer = 116936657OvixChainlinkOracleV2.getUnderlyingPrice(ovGHST) = 1169366570000000000
The direct accounting flaw is visible in the code above: totalGHST(address(this)) includes IERC20(ghst).balanceOf(address(this)). That means GHST that arrives by plain transfer is treated exactly like GHST that arrived through the intended enter() path. No checkpoint, time delay, or liability adjustment prevents that donation from affecting convertVGHST() immediately.
The exploit transaction first minted vGHST and built a helper borrow position, then donated a large amount of GHST into the vault address. The collector balance diff shows the effect on the vault directly:
{
"token": "GHST",
"holder": "0x51195e21bdae8722b29919db56d95ef51faeca6c",
"before": "27512443836174838889",
"after": "824457741083718931111488",
"delta": "824430228639882756272599"
}
That donation materially changed the share conversion and the oracle price inside the same transaction. Independent exploit-block RPC reads confirm:
vGHST.convertVGHST(1e18)rose to1786527556471688230VGHSTOracle.latestRoundData().answerrose to201198151OvixChainlinkOracleV2.getUnderlyingPrice(ovGHST)rose to2011981510000000000
The trace shows 0VIX consuming that manipulated state. The custom oracle adapter invoked getUnderlyingPrice(ovGHST), which called VGHSTOracle.latestRoundData(), which in turn called vGHST.convertVGHST(1e18) at the inflated vault state:
OvixChainlinkOracleV2::getUnderlyingPrice(0xE053A4014b50666ED388ab8CbB18D5834de0aB12)
VGHSTOracle::latestRoundData()
vGHST::convertVGHST(1000000000000000000)
...
← answer 201033025
The exact returned value in the trace differs slightly from the block-end read because the attack continued mutating state after this call, but both values confirm the same semantic fact: the ovGHST price was materially higher after the donation than before it.
Once that price moved, 0VIX's borrow and liquidation logic accepted the inflated collateral value. The trace then shows the attacker liquidating a helper borrow position and seizing oUSDC collateral:
0xE053A4014b50666ED388ab8CbB18D5834de0aB12::liquidateBorrow(
0x49c6dd832d76fB9dd0DFD3a889775FAa51AF095c,
467016954212907747535135,
0xEBb865Bf286e6eA8aBf5ac97e1b56A76530F3fBe
)
...
emit LiquidateBorrow(
liquidator: 0x407feAec31c16B19f24a8A8846AB4939ed7D7D57,
borrower: 0x49c6dd832d76fB9dd0DFD3a889775FAa51AF095c,
repayAmount: 467016954212907747535135,
oTokenCollateral: 0xEBb865Bf286e6eA8aBf5ac97e1b56A76530F3fBe,
seizeTokens: 5071115696329091
)
The same trace later shows the seized oUSDC being redeemed into liquid USDC:
OErc20::redeem(116681375699696032)
emit Redeem(
redeemer: 0x407feAec31c16B19f24a8A8846AB4939ed7D7D57,
redeemAmount: 23764949461402,
redeemTokens: 116681375699696032
)
...
OErc20::redeemUnderlying(15050538598)
This is the code-level breakpoint and exploitation path in one chain: donation-sensitive vGHST accounting raised convertVGHST, the custom oracle exposed that spot value to 0VIX, and 0VIX then allowed extraction of real market cash against artificially inflated ovGHST collateral.
5. Adversary Flow Analysis
The adversary flow was a single-transaction, multi-stage ACT sequence:
- The exploit contract sourced public flash-loan liquidity from Aave V3, Aave V2, and Balancer. The collected trace contains calls to the Aave V3 pool
0x794a61358d6845594f94dc1db02a252b5b4814ad, the Aave V2 pool0x8dff5e27ea6b7ac08ebfdf9eb090f32ee9a30fcf, and the Balancer vault0xba12222222228d8ba445958a75a0704d566bf2c8. - It called
vGHST::enter(294000 ether)to mintvGHSTshares and used a helper borrower contract to build a position that could later be liquidated profitably. - It transferred GHST directly into the
vGHSTvault address, increasing the vault's counted GHST backing without any corresponding share dilution. - With the ovGHST price now inflated, the attacker borrowed across multiple 0VIX markets. The trace shows borrow events from
oMATIC,oWBTC,oDAI,oWETH,oUSDC,oUSDT, andovGHST. - It repeatedly liquidated the helper borrower's
ovGHSTdebt intooUSDCcollateral, redeemed the seizedoUSDCinto underlying USDC, and finished the transaction with liquid profits before repaying the flash loans.
The borrowing phase is visible directly in the exploit trace:
OErc20::borrow(471478201676) // oUSDC
OErc20::borrow(452850498013984938642514) // another 0VIX market
0xE053A4014b50666ED388ab8CbB18D5834de0aB12::borrow(467016954212907747535135)
The attack remained permissionless throughout. The transaction sender was an EOA, the exploit contract was freshly deployed by that EOA, and every liquidity source and victim interaction used public protocol entrypoints.
6. Impact & Losses
The collector balance diff records the attacker's final asset gains as:
USDC:1453546066478base units (1,453,546.066478 USDC)USDT:584444536038base units (584,444.536038 USDT)GHST:9565867204501278658739base units (9,565.867204501278658739 GHST)
Using the same-block GHST/USD answer of 1.12619657, the reported post-transaction profit was 2048763.64935078482858608606232523 USD-equivalent before immaterial Polygon gas costs.
The loss was not confined to a single market. The transaction drained value from multiple 0VIX markets after the manipulated ovGHST collateral price increased the borrowable amount. One concrete depletion visible in the balance diff is the oUSDC market at 0xEBb865Bf286e6eA8aBf5ac97e1b56A76530F3fBe, whose USDC balance dropped from 471478201676 to 15050538598.
7. References
- Exploit transaction:
0x10f2c28f5d6cd8d7b56210b4d5e0cece27e45a30808cd3d3443c05d4275bb008 - 0VIX Unitroller:
0x8849f1a0cB6b5D6076aB150546EddEe193754F1C - ovGHST market:
0xE053A4014b50666ED388ab8CbB18D5834de0aB12 - vGHST vault:
0x51195e21BDaE8722B29919db56d95Ef51FaecA6C - Exploit-time custom ovGHST feed:
0x738FE8A918d5e43B705FC5127450e2300f7b08Ab - 0VIX oracle adapter:
0x1c312b14c129EabC4796b0165A2c470b659E5f01 - Collected exploit metadata, trace, and balance diff under
/workspace/session/artifacts/collector/seed/137/0x10f2c28f5d6cd8d7b56210b4d5e0cece27e45a30808cd3d3443c05d4275bb008/ - Verified source for
VGHSTOracleandOvixChainlinkOracleV2from Etherscan V2