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

Trying to repro StaxExploit #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "tests/evm_manual/foundry1/lib/forge-std"]
path = tests/evm_manual/foundry1/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "tests/evm_manual/foundry1/lib/chimera"]
path = tests/evm_manual/foundry1/lib/chimera
url = https://github.com/Recon-Fuzz/chimera
8 changes: 7 additions & 1 deletion tests/evm_manual/foundry1/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ docs/

.tmp/
build-info/
results.json
results.json

# Crytic
crytic-compile/
crytic-export/
echidna/
medusa/
5 changes: 5 additions & 0 deletions tests/evm_manual/foundry1/echidna.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
testMode: optimization
testLimit: 1000000
corpusDir: echidna
rpcUrl: https://rpc.ankr.com/eth
rpcBlock: 15725066
3 changes: 3 additions & 0 deletions tests/evm_manual/foundry1/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ src = "src"
out = "out"
libs = ["lib"]

[rpc_endpoints]
mainnet = "${MAINNET_RPC_URL}"

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 1 addition & 0 deletions tests/evm_manual/foundry1/lib/chimera
Submodule chimera added at 1526cb
100 changes: 100 additions & 0 deletions tests/evm_manual/foundry1/src/StaxExploit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-License-Identifier: AGPLv3
// References: https://blog.trailofbits.com/2023/07/21/fuzzing-on-chain-contracts-with-echidna/
// References: https://gist.github.com/tuturu-tech/efbba5a57465c9e75f0ed0050bbc49ed

pragma solidity ^0.8.0;

import {vm} from "@chimera/src/Hevm.sol";

interface IStaxLP {
function balanceOf(address) external returns (uint256);
function transfer(address, uint256) external returns (bool);
function approve(address, uint256) external returns (bool);
}

interface IStaxLPStaking {
function stake(uint256) external;
function stakeAll() external;
function stakeFor(address, uint256) external;
function withdraw(uint256, bool) external;
function withdrawAll(bool) external;
function getRewards(address) external;
function getReward(address, address) external;
function notifyRewardAmount(address, uint256) external;
function migrateStake(address, uint256) external;
function migrateWithdraw(address, uint256) external;
}

struct Reward {
uint40 periodFinish;
uint216 rewardRate;
uint40 lastUpdateTime;
uint216 rewardPerTokenStored;
}

contract StaxExploit {
IStaxLP StaxLP = IStaxLP(0xBcB8b7FC9197fEDa75C101fA69d3211b5a30dCD9);
IStaxLPStaking StaxLPStaking = IStaxLPStaking(0xd2869042E12a3506100af1D192b5b04D65137941);

uint256 initialAmount;
address tokenHolder;

constructor() {
vm.warp(1665493703);
vm.roll(15725066);

tokenHolder = address(0xeCb456EA5365865EbAb8a2661B0c503410e9B347);
initialAmount = StaxLP.balanceOf(tokenHolder);
vm.prank(tokenHolder);
StaxLP.transfer(address(this), initialAmount);

require(StaxLP.balanceOf(address(this)) > 0, "Zero balance in the contract, perhaps transfer failed?");

initialAmount = StaxLP.balanceOf(address(this));
StaxLP.approve(address(StaxLPStaking), type(uint256).max);
}

function stake(uint256 _amount) public {
StaxLPStaking.stake(_amount);
}

function stakeAll() public {
StaxLPStaking.stakeAll();
}

function stakeFor(address _for, uint256 _amount) public {
StaxLPStaking.stakeFor(_for, _amount);
}

function withdraw(uint256 amount, bool claim) public {
StaxLPStaking.withdraw(amount, claim);
}

function withdrawAll(bool claim) public {
StaxLPStaking.withdrawAll(claim);
}

function getRewards(address staker) public {
StaxLPStaking.getRewards(staker);
}

function getReward(address staker, address rewardToken) public {
StaxLPStaking.getReward(staker, rewardToken);
}

function notifyRewardAmount(address _rewardsToken, uint256 _amount) public {
StaxLPStaking.notifyRewardAmount(_rewardsToken, _amount);
}

function migrateStake(address oldStaking, uint256 amount) public {
StaxLPStaking.migrateStake(oldStaking, amount);
}

function migrateWithdraw(address staker, uint256 amount) public {
StaxLPStaking.migrateWithdraw(staker, amount);
}

function echidna_optimize_extracted_profit() public returns (int256) {
return (int256(StaxLP.balanceOf(address(this))) - int256(initialAmount));
}
}
33 changes: 33 additions & 0 deletions tests/evm_manual/foundry1/test/StaxExploit.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test} from "forge-std/Test.sol";

interface IStaxLP {
function balanceOf(address) external returns (uint256);
function transfer(address, uint256) external returns (bool);
function approve(address, uint256) external returns (bool);
}

contract StaxExploitTest is Test {
uint256 private initialAmount;
IStaxLP private StaxLP = IStaxLP(0xBcB8b7FC9197fEDa75C101fA69d3211b5a30dCD9);
address private StaxLPStaking = 0xd2869042E12a3506100af1D192b5b04D65137941;
address private tokenHolder = address(0xeCb456EA5365865EbAb8a2661B0c503410e9B347);

function setUp() public {
vm.createSelectFork("mainnet", 15725066);

targetContract(address(StaxLP));
targetContract(address(StaxLPStaking));

initialAmount = StaxLP.balanceOf(tokenHolder);
vm.prank(tokenHolder);
StaxLP.transfer(address(this), initialAmount);
StaxLP.approve(address(StaxLPStaking), type(uint256).max);
}

function invariant_1() public {
assertEq(StaxLP.balanceOf(address(this)), initialAmount);
}
}