Platform: Code4rena
Start Date: 23/05/2022
Pot Size: $50,000 USDC
Total HM: 44
Participants: 99
Period: 5 days
Judge: hickuphh3
Total Solo HM: 11
Id: 129
League: ETH
Rank: 19/99
Findings: 7
Award: $709.30
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: dipp
Also found by: 0x1f8b, csanuragjain, pedroais
https://github.com/code-423n4/2022-05-rubicon/blob/main/contracts/RubiconRouter.sol#L475
Using a malicious targetPool, attacker can drain the contract of its funds
Attacker calls withdrawForETH with malicious targetPool
The malicious targetPool returns weth address on calling underlyingToken() which passes the require condition
IERC20 target = IBathToken(targetPool).underlyingToken(); require(target == ERC20(wethAddress), "target pool not weth pool");
IBathToken(targetPool).transferFrom(msg.sender, address(this), shares);
withdrawnWETH = IBathToken(targetPool).withdraw(shares);
WETH9(wethAddress).withdraw(withdrawnWETH); //Send back withdrawn native eth to sender msg.sender.transfer(withdrawnWETH);
Apply whitelisting logic on targetPool
#0 - bghughes
2022-06-04T22:42:21Z
Duplicate of #356
🌟 Selected for report: cccz
Also found by: AlleyCat, GimelSec, IllIllI, Ruhum, berndartmueller, csanuragjain, dipp, fatherOfBlocks, gzeon, horsefacts, pedroais, shenwilly
https://github.com/code-423n4/2022-05-rubicon/blob/main/contracts/RubiconRouter.sol#L462
User can loss funds. If user accidentally sends extra ETH via msg.value while using depositWithETH or offerWithETH function then the extra amount of ETH is not refunded back to user
User calls depositWithETH function with amount as 5 and targetPool as X
User accidentally passed msg.value as 50 instead of 5
Deposit of amount 5 is success but user is not refunded back the excess amount which is 50-5=45
Need to be fixed for offerWithETH function as well
Send the extra ETH back to the user
if(msg.value>amount){ msg.sender.transfer(msg.value-amount); }
#0 - KenzoAgada
2022-06-04T15:35:46Z
Duplicate of #15.
#1 - bghughes
2022-06-04T22:38:55Z
Duplicate of #15
🌟 Selected for report: Ruhum
Also found by: 0x1f8b, 0x4non, 0xDjango, Dravee, GimelSec, StErMi, berndartmueller, blackscale, catchup, cccz, csanuragjain, defsec, eccentricexit, ellahi, horsefacts, hubble, joestakey, kenzo, pedroais, peritoflores, reassor, rotcivegaf, sashik_eth, shenwilly, sseefried, throttle, xiaoming90
1.8534 USDC - $1.85
Judge has assessed an item in Issue #85 as Medium risk. The relevant finding follows:
#0 - HickupHH3
2022-06-25T03:46:55Z
https://github.com/code-423n4/2022-05-rubicon/blob/main/contracts/rubiconPools/BathToken.sol#L260 Function setFeeBPS: _feeBPS should be in between 0 and 100. If it exceeds that then all withdraw will fail
require(_feeBPS>=0 && _feeBPS<=100, "Incorrect fees"); dup of #21
🌟 Selected for report: csanuragjain
Also found by: 0x1f8b
401.6035 USDC - $401.60
https://github.com/code-423n4/2022-05-rubicon/blob/main/contracts/RubiconRouter.sol#L519
User will loose funds if user accidentally pass route with only 1 value which is route[0]=X WETH while calling swapForETH or swapWithETH/swapEntireBalance/swap function
pay_amt=500 buy_amt_min=0 route[0]=WETH expectedMarketFeeBPS=1
require( ERC20(route[0]).transferFrom( msg.sender, address(this), pay_amt.add(pay_amt.mul(expectedMarketFeeBPS).div(10000)) ), "initial ERC20 transfer failed" );
for (uint256 i = 0; i < route.length - 1; i++) // here since route.length - 1 is 1-1=0 so loop will not run as i=0 and 0<0 is false
Since currentAmount will be 0 and buy_amt_min is also 0 so require(currentAmount >= buy_amt_min, "didnt clear buy_amt_min"); will pass and 0 will be returned back
Swap is complete and user will not receive anything
The same need to be fixed for swapWithETH,swapEntireBalance,swap function as well
Add below check
require(route.length>1, "Invalid route param");
#0 - bghughes
2022-06-04T22:33:36Z
Medium risk because it's user error
#1 - pauliax
2022-06-07T09:21:27Z
I think the problem is created by the warden by specifying buy_amt_min=0
. If a slippage of 0 is specified then you basically anticipate that you may receive nothing. While enforcing min route length is a good suggestion, I do not think it should be that severe.
#2 - HickupHH3
2022-06-17T08:58:11Z
2 user errors have to be made for this to happen:
buy_amt_min = 0
Hence, because of these prerequisites, I will downgrade the issue to medium severity.
🌟 Selected for report: WatchPug
Also found by: Chom, Dravee, Hawkeye, MaratCerby, Ruhum, csanuragjain, fatherOfBlocks, minhquanym
42.6857 USDC - $42.69
Judge has assessed an item in Issue #84 as Medium risk. The relevant finding follows:
#0 - HickupHH3
2022-06-28T15:08:39Z
https://github.com/code-423n4/2022-05-rubicon/blob/main/contracts/RubiconMarket.sol#L471 Function isClosed: If this is meant to be hardcoded as false then better use a variable
dup of #148
🌟 Selected for report: IllIllI
Also found by: 0x1337, 0x1f8b, 0x4non, 0xDjango, 0xKitsune, 0xNazgul, 0xf15ers, ACai, AlleyCat, Bahurum, BouSalman, CertoraInc, Chom, Dravee, ElKu, FSchmoede, Funen, GimelSec, Hawkeye, JC, JMukesh, Kaiziron, MaratCerby, Metatron, PP1004, Picodes, Ruhum, SmartSek, StErMi, TerrierLover, UVvirus, UnusualTurtle, WatchPug, Waze, _Adam, asutorufos, berndartmueller, blackscale, blockdev, broccolirob, c3phas, catchup, cryptphi, csanuragjain, defsec, delfin454000, dipp, eccentricexit, ellahi, fatherOfBlocks, gzeon, hansfriese, horsefacts, hubble, ilan, joestakey, kebabsec, minhquanym, oyc_109, parashar, pauliax, rotcivegaf, sach1r0, sashik_eth, shenwilly, simon135, sorrynotsorry, sseefried, throttle, unforgiven, xiaoming90
53.0048 USDC - $53.00
constructor: Add below conditions to make sure params are correct
require(start>=block.timestamp, "Back date"); require(duration>0, "Invalid duration")
Function adminWriteBathToken: Add a new check to see newBathToken is allocated properly
require(newBathToken!=address(0), "Invalid address");
Note: same goes for setNewBathTokenImplementation function
Function setBathHouseAdmin: Changing admin should be 2 step process and also check for address(0)
Note: do this for setBathTokenBathHouse function as well
Function openBathTokenSpawnAndSignal: User can spawn new bath token with 0 liquidity. Add a check to see
require(initialLiquidityNew>0 && initialLiquidityExistingBathToken>0, "incorrect value");
Function initialize: If attacker calls this function before bathHouse then attacker will get updated as bathHouse
Function setFeeBPS: _feeBPS should be in between 0 and 100. If it exceeds that then all withdraw will fail
require(_feeBPS>=0 && _feeBPS<=100, "Incorrect fees");
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x4non, 0xDjango, 0xNazgul, 0xf15ers, 0xkatana, Chom, DavidGialdi, Dravee, ElKu, FSchmoede, Fitraldys, Funen, GimelSec, JC, Kaiziron, MaratCerby, Metatron, MiloTruck, Picodes, Randyyy, RoiEvenHaim, SmartSek, Tomio, UnusualTurtle, WatchPug, Waze, _Adam, antonttc, asutorufos, berndartmueller, blackscale, blockdev, c3phas, catchup, csanuragjain, defsec, delfin454000, ellahi, fatherOfBlocks, gzeon, hansfriese, ilan, joestakey, minhquanym, oyc_109, pauliax, pedroais, reassor, rfa, rotcivegaf, sach1r0, samruna, sashik_eth, simon135, z3s
31.3128 USDC - $31.31
Function getIndexFromElement: No need to keeping assigned variable. If index is found function will return within loop. If control is completing loop and coming outside to BathPair.sol#L318 then it already means that index was not found
Function openBathTokenSpawnAndSignal: No need to check getBathTokenfromAsset(newBathTokenUnderlying) == address(0) as this is already checked in _createBathToken
Function withdraw: No need to have owner argument. _withdraw is burning from msg.sender which eliminates the risk
Function can_offer: does not do anything and can be removed
Function isClosed: If this is meant to be hardcoded as false then better use a variable
Function depositWithETH: Check amount!=0
Function withdrawForETH: Check shares!=0
Function swapWithETH: Check pay_amt!=0
Note: do same for swapForETH and all other functions using pay_amt