Skip to main content

flash-loan

Flash-loan exploit patterns — callback reentrancy, oracle amplification, governance attacks, unauthenticated callback handlers.

Aller à l'installation

Informations de source

Dépôt
BitterSecurity/Decepticon
Dernière activité de la source
2 juin 2026 à 17:25
Langue détectée de SKILL.md
anglais
Étoiles
5 565
Forks
1 053

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
flash-loan
description
Flash-loan exploit patterns — callback reentrancy, oracle amplification, governance attacks, unauthenticated callback handlers.
metadata
{"subdomain":"smart-contracts","when_to_use":"flash loan aave dydx balancer composition","mitre_attack":["T1190","T1565.001"]}
# Flash Loan Attack Playbook Flash loans give the attacker uncollateralized capital for a single transaction. They're not vulnerabilities themselves — they're a force multiplier for existing bugs. Sources: Aave, Balancer, Uniswap V2/V3, Maker, dYdX (deprecated). ## Common attack patterns ### 1. Oracle amplification (most common) Use loan to push a price, then trigger price-dependent action. **Cross-reference**: see `oracle-manipulation/SKILL.md`. ### 2. Unauthenticated callback handler ```solidity function executeOperation( address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, address initiator, // ← UNVALIDATED in many contracts bytes calldata params ) external returns (bool) { // Anyone can call this with fake `initiator` // and have the contract do whatever the params say } ``` **Bug**: `initiator` and `msg.sender == pool` checks are missing. **Attack**: call `executeOperation` directly with no actual loan, malicious `params` → contract does the operation anyway. ### 3. Governance attack via flash loan ```solidity // Vulnerable governance: function propose() external { require(getVotes(msg.sender) > THRESHOLD); // ... } function getVotes(address user) public view returns (uint256) { return token.balanceOf(user); // ← reads SPOT balance, not snapshot } ``` **Attack**: flash-loan governance tokens, propose malicious change (e.g., upgrade contract to drain), vote with the loaned tokens, execute, repay loan. **MakerDAO** had this pattern; mitigated by checkpoint-based voting. ### 4. Liquidity manipulation Pool that uses `totalSupply()` or `balanceOf(pool)` for share math: ```solidity function deposit(uint256 amt) external { uint256 shares = (amt * totalSupply()) / underlying.balanceOf(address(this)); _mint(msg.sender, shares); underlying.transferFrom(msg.sender, address(this), amt); } ``` Attacker: 1. Flash-loan and deposit (becomes 99% of pool) 2. Donate underlying to inflate `underlying.balanceOf` artificially 3. Next depositor's shares = `amt * totalSupply / inflatedBalance` ≈ 0 4. Withdraw → drain This is the **ERC4626 inflation attack** (also covered in `reentrancy/` overlap). ### 5. Liquidation arbitrage gone wrong A liquidator that uses flash loans to repay debt before claiming collateral — usually benign. The bug: liquidation health check happens *before* the seizure, but the seizure happens via callback into a manipulable function. Reentrancy + oracle dependent. ## Audit steps ### 1. Locate flash-loan integrations ```bash grep -rn 'executeOperation\|flashLoan\|onFlashLoan\|flashCallback\|aaveFlashLoan' src/ ``` ### 2. For each handler Check: - `msg.sender == known_pool` (whitelist callback origin) - `initiator == address(this)` (only respond to your own loans) - ReentrancyGuard on the public function that *takes* the loan - The fee is correctly accounted (`amounts[i] + premiums[i]` is repaid) ### 3. For each price-reading function See `oracle-manipulation/SKILL.md`. ### 4. For each share-mint / share-burn function Check `ERC4626` virtual-shares mitigation: ```solidity // OpenZeppelin ERC4626 v4.7+ function _convertToShares(uint256 assets, MathUpgradeable.Rounding rounding) internal view virtual override returns (uint256) { return _initialConvertToShares(assets, rounding); // adds 10**18 to total supply for share calc } ``` If using legacy ERC4626 or custom vault math, write a Foundry test that: 1. Attacker deposits 1 wei → gets 1 share 2. Attacker direct-transfers 1e18 underlying to vault 3. Victim deposits 1e18 → gets 0 shares (rounding) 4. Attacker withdraws 1 share → gets 100% of pool ``` foundry_inflation_test(vault_address="...", underlying="...", target_path="src/Vault.sol") ``` ## Severity calibration | Pattern | Severity if confirmed | |---|---| | Unauth callback handler → arbitrary execution | Critical 10.0 | | Flash-loan oracle manipulation drains protocol | Critical 9-10 | | Governance attack possible | Critical 9-10 (if exec results in fund loss) | | ERC4626 inflation attack on live vault | Critical 9 | | Liquidation reentrancy via callback | High 8 | | Theoretical (low liquidity, manipulation cost > attack profit) | Medium-Low | ## PoC template (Foundry) ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "forge-std/Test.sol"; import "@aave-v3/interfaces/IPool.sol"; contract Test_FlashAttack is Test { IPool constant AAVE = IPool(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2); address constant TARGET = 0x...; address constant ATTACKER = address(0xBEEF); function setUp() public { vm.createSelectFork("https://eth-mainnet.../<block>"); } function test_drain() public { vm.startPrank(ATTACKER); uint256 beforeBal = WETH.balanceOf(ATTACKER); // Request 10000 WETH flash loan address[] memory assets = new address[](1); assets[0] = address(WETH); uint256[] memory amts = new uint256[](1); amts[0] = 10000 ether; uint256[] memory modes = new uint256[](1); // 0 = repay full AAVE.flashLoan(address(this), assets, amts, modes, address(this), "", 0); uint256 afterBal = WETH.balanceOf(ATTACKER); assertGt(afterBal, beforeBal + 100 ether, "should profit"); } function executeOperation( address[] calldata assets, uint256[] calldata amts, uint256[] calldata premiums, address initiator, bytes calldata ) external returns (bool) { // 1. Use loaned funds to manipulate TARGET // 2. Profit // 3. Repay IERC20(assets[0]).approve(address(AAVE), amts[0] + premiums[0]); return true; } } ``` ## OPSEC (operational, not blue team — this is on-chain) Flash-loan attacks leave a single transaction trail visible to: - Mempool watchers / Flashbots inspectors - MEV bots that may front-run if not using private mempool - Risk dashboards (DefiLlama, Forta, Cyfrin) For audit-PoC purposes (not live exploit) just use a forked anvil. ## Defender remediation 1. **Snapshot-based governance** — vote based on historical balance, not spot 2. **TWAP oracles only** — never spot price for trust-critical decisions 3. **Validate flash callback origin** — `require(msg.sender == knownPool && initiator == address(this))` 4. **ERC4626 OpenZeppelin v4.7+** — virtual shares mitigation 5. **Initial deposit by deployer** — seed the vault with 10**18 underlying before opening, makes inflation impractical ## Known exemplars - bZx 1 (Feb 2020): $350k flash-loan oracle attack - bZx 2 (Feb 2020): $645k Synthetix sUSD attack - Beanstalk (Apr 2022): $182M flash-loan governance attack - Euler (Mar 2023): $197M; donateToReserves bug + flash loan - Mango (Oct 2022): $114M oracle + flash loan - Cream (Oct 2021): $130M via yUSD oracle - KyberSwap (Nov 2023): $48.5M via concentrated-liquidity edge case + flash loan
Voir sur GitHub