Platform: Code4rena
Start Date: 02/08/2023
Pot Size: $42,000 USDC
Total HM: 13
Participants: 45
Period: 5 days
Judge: hickuphh3
Total Solo HM: 5
Id: 271
League: ETH
Rank: 5/45
Findings: 4
Award: $1,702.14
๐ Selected for report: 1
๐ Solo Findings: 0
๐ Selected for report: SanketKogekar
Also found by: MohammedRizwan, bin2chen, cartlex_, piyushshukla
231.3497 USDC - $231.35
In LiquidationRouter.sol, swapExactAmountOut() function has no deadline for the transaction when swapping.
File: src/LiquidationRouter.sol function swapExactAmountOut( LiquidationPair _liquidationPair, address _receiver, uint256 _amountOut, uint256 _amountInMax ) external onlyTrustedLiquidationPair(_liquidationPair) returns (uint256) { IERC20(_liquidationPair.tokenIn()).safeTransferFrom( msg.sender, _liquidationPair.target(), _liquidationPair.computeExactAmountIn(_amountOut) ); uint256 amountIn = _liquidationPair.swapExactAmountOut(_receiver, _amountOut, _amountInMax); emit SwappedExactAmountOut(_liquidationPair, _receiver, _amountOut, _amountInMax, amountIn); return amountIn; }
As seen above, there is no deadline checks while swapping. This can be further checked in LiquidationRouter.sol where deadline parameter is also missing. AMMs provide their users with an option to limit the execution of their pending actions, such as swaps or adding and removing liquidity. The most common solution is to include a deadline timestamp as a parameter (for example see Uniswap V2 and Uniswap V3). If such an option is not present, users can unknowingly perform bad trades:
Manual Review
Introduce a deadline parameter to all functions which potentially perform a swap.
Other
#0 - c4-pre-sort
2023-08-07T22:01:39Z
raymondfam marked the issue as low quality report
#1 - raymondfam
2023-08-07T22:02:50Z
A wrong scenario was given. Additionally, slippage has been implemented via _amountInMax.
#2 - c4-pre-sort
2023-08-08T02:37:34Z
raymondfam marked the issue as remove high or low quality report
#3 - c4-pre-sort
2023-08-08T02:37:44Z
raymondfam marked the issue as duplicate of #126
#4 - c4-judge
2023-08-12T09:25:52Z
HickupHH3 marked the issue as satisfactory
๐ Selected for report: 0xStalin
Also found by: MohammedRizwan
793.3802 USDC - $793.38
Malicious _liquidationPair
owner can deploy _liquidationPair
using malicious _source
(liquidation source that the pair will use) and other insufficient input validations that can put users' funds at risk.
File: src/LiquidationPairFactory.sol function createPair( ILiquidationSource _source, address _tokenIn, address _tokenOut, uint32 _periodLength, uint32 _periodOffset, uint32 _targetFirstSaleTime, SD59x18 _decayConstant, uint112 _initialAmountIn, uint112 _initialAmountOut, uint256 _minimumAuctionAmount ) external returns (LiquidationPair) { LiquidationPair _liquidationPair = new LiquidationPair( _source, _tokenIn, _tokenOut, _periodLength, _periodOffset, _targetFirstSaleTime, _decayConstant, _initialAmountIn, _initialAmountOut, _minimumAuctionAmount ); allPairs.push(_liquidationPair); deployedPairs[_liquidationPair] = true; // Some code }
In LiquidationPairFactory.sol, createPair() function is used to deploy a new LiquidationPair
and then push it to allPairs array.
LiquidationPair[] public allPairs;
It also have a mapping to verify that a _liquidationPair has been deployed via the LiquidationPairFactory.sol
mapping(LiquidationPair => bool) public deployedPairs;
This allows users to check the authenticity of a _liquidationPair
to ensure the that implementation of a _liquidationPair
is authentic but the issue arises when the addresses of the _source
and other parameters are passed as input in the function but they are not validated before the _liquidationPair
being deployed. In current implementation, users can see the authenticity of _liquidationPair
but they can not authenticate the _source
(liquidation source).
LiquidationPair.sol
constructor only checks that uint related parameters. It does not check or validate the _source, _tokenIn, _tokenOut addresses in constructor. It is recommended to validate before the deployment.
Manual Review
Ensure proper input validations since these are passed manually.
Invalid Validation
#0 - c4-pre-sort
2023-08-08T03:46:05Z
raymondfam marked the issue as duplicate of #52
#1 - c4-pre-sort
2023-08-10T00:00:50Z
raymondfam marked the issue as not a duplicate
#2 - c4-pre-sort
2023-08-10T00:01:10Z
raymondfam marked the issue as duplicate of #68
#3 - c4-judge
2023-08-14T06:41:40Z
HickupHH3 marked the issue as satisfactory
๐ Selected for report: MohammedRizwan
Also found by: MohammedRizwan, nadin, ptsanev
618.8366 USDC - $618.84
The createVaultBooster() function deploys a new VaultBooster contract using the create
, where the address derivation depends only on the VaultBoosterFactory nonce.
Re-orgs can happen in all EVM chains and as confirmed the contracts will be deployed on most EVM compatible L2s including Arbitrum, etc. It is also planned to be deployed on ZKSync in future. In ethereum, where this is deployed, Re-orgs has already been happened. For more info, check here
This issue will increase as some of the chains like Arbitrum and Polygon are suspicious of the reorg attacks.
Polygon re-org reference: click here This one happened this year in February, 2023.
Polygon blocks forked: check here
The issue would happen when users rely on the address derivation in advance or try to deploy the position clone with the same address on different EVM chains, any funds sent to the new
contract could potentially be withdrawn by anyone else. All in all, it could lead to the theft of user funds.
File: src/VaultBoosterFactory.sol function createVaultBooster(PrizePool _prizePool, address _vault, address _owner) external returns (VaultBooster) { >> VaultBooster booster = new VaultBooster(_prizePool, _vault, _owner); emit CreatedVaultBooster(booster, _prizePool, _vault, _owner); return booster; }
Optimistic rollups (Optimism/Arbitrum) are also suspect to reorgs since if someone finds a fraud the blocks will be reverted, even though the user receives a confirmation.
Attack Scenario Imagine that Alice deploys a new VaultBooster, and then sends funds to it. Bob sees that the network block reorg happens and calls createVaultBooster. Thus, it creates VaultBooster with an address to which Alice sends funds. Then Alicesโ transactions are executed and Alice transfers funds to Bobโs controlled VaultBooster.
This is a Medium severity issue that has been referenced from below Code4rena reports, https://code4rena.com/reports/2023-01-rabbithole/#m-01-questfactory-is-suspicious-of-the-reorg-attack https://code4rena.com/reports/2023-04-frankencoin#m-14-re-org-attack-in-factory
Manual Review
Deploy such contracts via create2
with salt
that includes msg.sender
Other
#0 - c4-pre-sort
2023-08-07T23:53:23Z
raymondfam marked the issue as duplicate of #169
#1 - c4-pre-sort
2023-08-08T05:24:31Z
raymondfam marked the issue as high quality report
#2 - c4-judge
2023-08-12T08:58:01Z
HickupHH3 marked the issue as satisfactory
#3 - c4-judge
2023-08-15T03:02:14Z
HickupHH3 changed the severity to QA (Quality Assurance)
#4 - c4-judge
2023-08-15T03:03:35Z
HickupHH3 marked the issue as grade-c
#5 - 0xRizwan
2023-08-15T12:10:49Z
Hi @HickupHH3
I believe this is valid Medium and this report has more burden of proof than #169
While going through the discussion at #169 to know the reason of this issue being made invalid, i found your comment,
I shouldn't be doing the work to reason it out though, burden of proof lies with the wardens. None have sufficient justification to warrant the medium severity.
I think this reports has enough proofs for making the issue as valid.
In PoolTogether V5 audit which happended before this audit. This reorg issue has been made valid by Judge Picode and its very similar issue. Though the report is not public yet, I am putting below link from backstage for your reference,
https://github.com/code-423n4/2023-07-pooltogether-findings/issues/416
PoolTogether V5 audit has made this issue to final report. I believe this report justifies the issue with references which can be seen in report.
Thank you!
#6 - c4-judge
2023-08-17T14:50:10Z
This previously downgraded issue has been upgraded by HickupHH3
#7 - c4-judge
2023-08-17T14:50:20Z
HickupHH3 marked the issue as selected for report
#8 - HickupHH3
2023-08-17T15:01:44Z
Accepting because of this:
Imagine that Alice deploys a new VaultBooster, and then sends funds to it. Bob sees that the network block reorg happens and calls createVaultBooster. Thus, it creates VaultBooster with an address to which Alice sends funds. Then Alicesโ transactions are executed and Alice transfers funds to Bobโs controlled VaultBooster.
Again, the scenario should have been more explicit: stating how the vault could be different, how funds are transferred & possibly exploited.
#9 - thebrittfactor
2023-08-17T16:42:12Z
For transparency, the judge requested to remove the unsatisfactory label for this submission and any duplicates.
๐ Selected for report: MohammedRizwan
Also found by: MohammedRizwan, nadin, ptsanev
618.8366 USDC - $618.84
_liquidationPair are created from the factory via CREATE1, an attacker can frontrun createPair() to deploy at the same address but with different config. If the deployed chain reorg, a different vault might also be deployed at the same address.
File: src/LiquidationPairFactory.sol function createPair( ILiquidationSource _source, address _tokenIn, address _tokenOut, uint32 _periodLength, uint32 _periodOffset, uint32 _targetFirstSaleTime, SD59x18 _decayConstant, uint112 _initialAmountIn, uint112 _initialAmountOut, uint256 _minimumAuctionAmount ) external returns (LiquidationPair) { >> LiquidationPair _liquidationPair = new LiquidationPair( _source, _tokenIn, _tokenOut, _periodLength, _periodOffset, _targetFirstSaleTime, _decayConstant, _initialAmountIn, _initialAmountOut, _minimumAuctionAmount ); // Some code }
Manual Review
Use CREATE2 for such contracts with msg.sender containing salt.
Other
#0 - c4-pre-sort
2023-08-08T02:49:23Z
raymondfam marked the issue as duplicate of #52
#1 - c4-pre-sort
2023-08-10T00:09:01Z
raymondfam marked the issue as not a duplicate
#2 - c4-pre-sort
2023-08-10T00:09:15Z
raymondfam marked the issue as duplicate of #169
#3 - c4-judge
2023-08-12T08:44:21Z
HickupHH3 marked the issue as satisfactory
#4 - c4-judge
2023-08-15T03:02:14Z
HickupHH3 changed the severity to QA (Quality Assurance)
#5 - c4-judge
2023-08-15T03:03:53Z
HickupHH3 marked the issue as grade-c
#6 - c4-judge
2023-08-17T14:50:07Z
This previously downgraded issue has been upgraded by HickupHH3
#7 - thebrittfactor
2023-08-17T16:39:41Z
For transparency, the judge requested to remove the unsatisfactory label.
๐ Selected for report: 0xmystery
Also found by: 0xbepresent, MohammedRizwan, Rolezn, bin2chen, rvierdiiev
58.5695 USDC - $58.57
Number | Issue | Instances | |
---|---|---|---|
[Lโ01] | setOriginChainOwner() can be called more than once which is against Natspec | 1 | |
[Lโ02] | Prevent createVaultBooster() from setting incorrect inputs | 1 | |
[Lโ03] | update openzeppelin version to 4.9.3 | 1 | |
[Lโ04] | unsafe downcast may lead to accounting errors | 1 |
In RemoteOwner.sol, Per the Natspec of setOriginChainOwner() it can be called only once,
* @dev Can only be called once.
However, with current impelementation it can be called more than once. To prevent this happening, a validation checks must be added to setOriginChainOwner() function.
There is 1 instance of this issue:
File: src/RemoteOwner.sol function setOriginChainOwner(address _newOriginChainOwner) external { _checkSender(); _setOriginChainOwner(_newOriginChainOwner); }
File: src/RemoteOwner.sol + bool initialize; function setOriginChainOwner(address _newOriginChainOwner) external { + require(!initialize, "can be called only once"); _checkSender(); _setOriginChainOwner(_newOriginChainOwner); + initialize = true; }
In VaultBoosterFactory.sol, createVaultBooster() is used to create a new vault booster contract. As confirmed by sponsor, this can be called by anyone. However it has some missing sanity validations which should be added.
There is 1 instance of this issue:
function createVaultBooster(PrizePool _prizePool, address _vault, address _owner) external returns (VaultBooster) { + require(address(_prizePool) != address(0), "invalid address"); + require(_vault != address(0), "invalid address"); + require(_owner != address(0), "invalid address"); VaultBooster booster = new VaultBooster(_prizePool, _vault, _owner); emit CreatedVaultBooster(booster, _prizePool, _vault, _owner); return booster; }
The contracts has used openzeppelin version 4.9.2 which is checked from package.json. External libraries which are being used in contracts must be upto date and should not have any known vulnerabilities. Lates OZ features can be checked here
Update openzeppelin version to 4.9.3
In contracts, while down casting the data type of data value, It is directly processed without any safety. As seen in contract, such unsafe downcasting can lead to accounting errors which can seriously hamper the overall system.
There is 3 instances of this issue:
Use openzeppelin safeCast.sol for safe casting.
#0 - HickupHH3
2023-08-14T10:34:11Z
#30: R #31 / #71 - R #76 - R L-01: Upgraded L-02: NA (automated finding) L-03: R L-04: NA (automated finding)
4R
#1 - c4-judge
2023-08-14T10:34:23Z
HickupHH3 marked the issue as grade-c
#2 - c4-judge
2023-08-15T03:34:09Z
HickupHH3 marked the issue as grade-b