Add PoC: FoxLpBondsPool stale _stakeAmount from manipulable AMM spot quote (~112,976 USDC, Aug 2026) - #1209
Merged
Conversation
…quote (~112,976 USDC, Aug 2026)
Owner
|
🤖 Automated PR review — changes requested Thanks for the contribution! This PR can't be merged yet:
Please fix the TOC link (add the missing |
Owner
|
🤖 Automated PR review — merged ✅
Thanks for the contribution and for the quick anchor fix! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stale _stakeAmount from manipulable AMM spot quote (~112,976.12 USDC, ~$118.7K reported, BSC)
Root cause, confirmed against the trace: FoxLpBondsPool.stake() calculates and fixes a _stakeAmount value from a manipulable Pancake AMM spot quote BEFORE executing its own large USDT->Fox swap. That swap materially skews the pair's reserves. The subsequent addLiquidity() call then supplies Fox and USDT according to the now-different reserve ratio, but _stakeAmount is never recomputed from the actual assets deposited or the fair value of the resulting LP tokens.
Treasury.lpBonds() trusts this stale, economically unsupported _stakeAmount, mints Fox based on it, and immediately transfers an inviter referral reward to an attacker-controlled address in the same flow. The attacker then sells the newly-minted Fox back into the pair in the same transaction. Flash-loan liquidity (multi-pool USDT aggregation, ~482M USDT, fully repaid at tx end) funds the scale of the swap the core vulnerability is the lack of manipulation-resistant pricing, no accounting-to-actual-backing validation, and immediate (non-delayed) reward settlement.
Every step (flash loan, USDT->Fox swap, stake(), addLiquidity(), referral reward transfer, sell-back) is a plain permissionless call chain from a plain EOA. No signer, admin role, or privileged path anywhere in the trace.
Attack sequence traced:
PoC forks at block-1 (attack contracts already deployed on-chain, no same-block setup needed), calls the already-deployed attack contract with exact on-chain calldata, pranks the caller as both msg.sender and tx.origin. Asserts net attacker USDT gain: 112,976.12 USDC on-chain, close to the reported ~$118.7K (remainder is WBNB/USDC dust from the two flash-loaned LP tokens).
Attacker: 0x3a82a2a77061017927e5331fffd07c0308a1d2da (controlled by 0x5670d36f00bc7f6860b6afddb288e3668efc0ef9)
Victim (USDT/Fox PancakePair): 0xaab18bcdee287aea288c0560612caadf7c328803
FoxLpBondsPool: 0x58e2a853bb14e46befd3148bd4280370fea4655a
Treasury: 0x87614d97808dcdecb069fe8489848fa1c001e04d
Tx: https://bscscan.com/tx/0x8e1775cbfd44db29744cc6687ff1822d2c47321de6e94062f789ad6181ad5514
Run: forge test --contracts src/test/2026-08/FoxLpBondsPool_exp.sol --evm-version cancun -vvv