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: 154/189
Findings: 1
Award: $0.15
🌟 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.1468 USDC - $0.15
You can see in the following code. addToDelegate will add the amount of totalWethDelegated[1]. But Withdraw will not sub the amount of totalWethDelegated. This will cause totalWethDelegated only increase, not decrease.
One directly impact of this bug will cause the sync function no longer usable. Because totalWethDelegated may increase to be higher than balance and cause a underflow in [2].Sync call is called in function such as addLiquidity. It will make the admin can't add liquidity.
The furture impact of this bug will cause lowerDepeg and provideFunding no long usable. Because attacker can control totalWethDelegated to increase to be equal to reserveAsset[reservesIndex["WETH"]]. Then after sync call, the reserveAsset[reservesIndex["WETH"]].tokenBalance will be 0. Finally, the sub operation in lowerDepeg[3] and provideFunding[4] will all cause underflow.
function addToDelegate( uint256 _amount, uint256 _fee ) external returns (uint256) { ... // add amount to total weth delegated totalWethDelegated += _amount; ------[1] ... return (delegates.length - 1); } function withdraw( uint256 delegateId ) external returns (uint256 amountWithdrawn) { _whenNotPaused(); _validate(delegateId < delegates.length, 14); Delegate storage delegate = delegates[delegateId]; _validate(delegate.owner == msg.sender, 9); amountWithdrawn = delegate.amount - delegate.activeCollateral; _validate(amountWithdrawn > 0, 15); delegate.amount = delegate.activeCollateral; IERC20WithBurn(weth).safeTransfer(msg.sender, amountWithdrawn); emit LogDelegateWithdraw(delegateId, amountWithdrawn); } function sync() external { for (uint256 i = 1; i < reserveAsset.length; i++) { uint256 balance = IERC20WithBurn(reserveAsset[i].tokenAddress).balanceOf( address(this) ); if (weth == reserveAsset[i].tokenAddress) { balance = balance - totalWethDelegated; ----------[2] } reserveAsset[i].tokenBalance = balance; } emit LogSync(); } function lowerDepeg( uint256 _rdpxAmount, uint256 _wethAmount, uint256 minamountOfWeth, uint256 minOut ) external onlyRole(DEFAULT_ADMIN_ROLE) returns (uint256 dpxEthReceived) { ... // update Reserves reserveAsset[reservesIndex["WETH"]].tokenBalance -= _wethAmount; -----[3] ... } function provideFunding() external onlyRole(DEFAULT_ADMIN_ROLE) returns (uint256 fundingAmount) { ... reserveAsset[reservesIndex["WETH"]].tokenBalance -= fundingAmount; -----[4] ... }
forge test --match-path tests/rdpxV2-core/BugTest.t.sol -vvvv And you will see a underflow happen in sync function.
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; import { Test } from "forge-std/Test.sol"; import "forge-std/console.sol"; import { Setup } from "./Setup.t.sol"; contract Mynit is Test,Setup { function testAaa() public{ uint256 receiptTokenAmount = rdpxV2Core.bond(1 * 1e18, 0, address(this)); uint256 previous_delegated = rdpxV2Core.totalWethDelegated(); uint256 delegateid = rdpxV2Core.addToDelegate(1 * 1e18, 10e8); rdpxV2Core.withdraw(delegateid); assertEq(previous_delegated,rdpxV2Core.totalWethDelegated()); rdpxV2Core.sync(); } }
vscode, foundry
It's easy to mitigate the bug. You just need to add
totalWethDelegated -= _amount;
in the function withdraw.
DoS
#0 - c4-pre-sort
2023-09-07T07:40:06Z
bytes032 marked the issue as duplicate of #2186
#1 - c4-judge
2023-10-20T17:53:25Z
GalloDaSballo marked the issue as satisfactory
#2 - c4-judge
2023-10-20T17:55:32Z
GalloDaSballo changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-10-21T07:38:54Z
GalloDaSballo changed the severity to 3 (High Risk)