Platform: Code4rena
Start Date: 21/08/2023
Pot Size: $125,000 USDC
Total HM: 26
Participants: 189
Period: 16 days
Judge: GalloDaSballo
Total Solo HM: 3
Id: 278
League: ETH
Rank: 183/189
Findings: 1
Award: $0.01
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: klau5
Also found by: 0x3b, 0xCiphky, 0xDING99YA, 0xWaitress, 0xbranded, 0xc0ffEE, 0xklh, 0xsurena, 0xvj, ABA, AkshaySrivastav, Anirruth, Aymen0909, Baki, Blockian, BugzyVonBuggernaut, DanielArmstrong, Evo, GangsOfBrahmin, HChang26, Inspex, Jiamin, Juntao, Kow, Krace, KrisApostolov, LFGSecurity, LokiThe5th, Mike_Bello90, Norah, Nyx, QiuhaoLi, RED-LOTUS-REACH, SBSecurity, Snow24, SpicyMeatball, T1MOH, Tendency, Toshii, Udsen, Yanchuan, __141345__, ak1, asui, auditsea, ayden, bart1e, bin2chen, blutorque, carrotsmuggler, chaduke, chainsnake, circlelooper, clash, codegpt, crunch, degensec, dirk_y, ge6a, gjaldon, grearlake, jasonxiale, juancito, ke1caM, kodyvim, kutugu, ladboy233, lanrebayode77, mahdikarimi, max10afternoon, mert_eren, nirlin, nobody2018, oakcobalt, parsely, peakbolt, pks_, pontifex, ravikiranweb3, rokinot, rvierdiiev, said, savi0ur, sces60107, sh1v, sl1, spidy730, tapir, tnquanghuy0512, ubermensch, visualbits, volodya, wintermute
0.0098 USDC - $0.01
https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVaultLP.sol#L201 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/perp-vault/PerpetualAtlanticVault.sol#L359 https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/core/RdpxV2Core.sol#L774
rpdxV2core.sol
buys the put option for rdpx token (underlying) in Weth from the PerprtualAtlanticVault.sol
, if the option turns out ITM (In the Money), the DEFAULT_ADMIN_ROLE calls settle()
function of 1rpdxV2core.sol` contract.
Which in turn calls PerprtualAtlanticVault.sol
contracts settle()
function where in,
subtractLoss()
function of PerprtualAtlanticVaultLP.sol.
-In subtractLoss
function , a check is performed to ensure that the collateral (Weth) owned by PerprtualAtlanticVaultLP.sol matches the internal accounting of collateral minus the transferred amount (loss).function subtractLoss(uint256 loss) public onlyPerpVault { require( collateral.balanceOf(address(this)) == _totalCollateral - loss, "Not enough collateral was sent out" ); _totalCollateral -= loss; }
The issue arises due to the specific check mentioned above, where the protocol checks if the collateral (ETH) owned by PerprtualAtlanticVaultLP.sol
is equal to the internal accounting of collateral minus the transferred amount. This check utilizes the collateral.balanceOf(address(this))
function to determine the current ownership of the collateral (ETH). This can be manipulated through external transfers, potentially causing a discrepancy between the actual balance and the internal accounting.
An attacker could transfer a tiny amount of collateral (e.g., 1 wei worth of ETH) to PerprtualAtlanticVaultLP.sol
contract from an external source, which would make collateral.balanceOf(address(this)
) always 1 wei higher than the internal accounting. This manipulation would cause the mentioned check to fail, leading to a transaction revert and effectively performing a Denial of Service (DOS) attack on the settle() functionality.
Also, since there is no way to remove or sweep unaccounted collateral or forcefully sync internal accounting to the same, protocol will be permanently in DOS, will have to redeployed the vault for normal functioning.
The vulnerability results in a sustained denial of service attack targeting the settle() functionality. This not only disrupts the protocol's core operation but also necessitates the redeployment of the entire protocol to restore normal functioning.
Manual Review
To mitigate this vulnerability :
collateral.balanceOf(address(this))
in PerprtualAtlanticVaultLP.sol
contracts subtractLoss()
function.function subtractLoss(uint256 loss) public onlyPerpVault { require( collateral.balanceOf(address(this)) == _totalCollateral - loss, "Not enough collateral was sent out" ); _totalCollateral -= loss; }
Implement a new check directly within the PerprtualAtlanticVault.sol
contract's settle() function.
function settle( uint256[] memory optionIds ) external nonReentrant onlyRole(RDPXV2CORE_ROLE) returns (uint256 ethAmount, uint256 rdpxAmount) { . . . . uint256 balanceBefore = collateralToken.balanceOf(Addresses.perpetualAtlanticVaultLP); collateralToken.safeTransferFrom( addresses.perpetualAtlanticVaultLP, addresses.rdpxV2Core, ethAmount ); uint256 balanceAfter = collateralToken.balanceOf(ddresses.perpetualAtlanticVaultLP); require(ethAmount == balanceAfter - balanceBefore, "Incorrect transfer"); IPerpetualAtlanticVaultLP(addresses.perpetualAtlanticVaultLP).subtractLoss( ethAmount );
DoS
#0 - c4-pre-sort
2023-09-09T10:00:47Z
bytes032 marked the issue as duplicate of #619
#1 - c4-pre-sort
2023-09-11T16:15:11Z
bytes032 marked the issue as sufficient quality report
#2 - c4-judge
2023-10-20T20:02:08Z
GalloDaSballo marked the issue as satisfactory
#3 - c4-judge
2023-10-21T07:26:28Z
GalloDaSballo changed the severity to 3 (High Risk)
#4 - c4-judge
2023-10-21T07:26:28Z
GalloDaSballo changed the severity to 3 (High Risk)