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: 40/99
Findings: 6
Award: $192.46
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: MiloTruck
Also found by: CertoraInc, PP1004, SmartSek, VAD37, WatchPug, berndartmueller, cccz, minhquanym, oyc_109, sorrynotsorry, unforgiven
The first depositor is able to steal subsequent deposits by directly transferring funds to the strategy. This attack vector is the same as the following audit report:
In general terms, the attacker deposits a small amount and will receive N shares. They then directly transfer a large amount of tokens directly to the strategy. Based on the share issuance logic, future deposits that are less than the transferred amount will receive 0 shares, effectively stealing their deposited tokens. The attacker is then able to withdraw all tokens in the strategy.
Exploit Walkthrough:
(totalSupply == 0) ? shares = assets : shares = (assets.mul(totalSupply)).div(_pool);
At this point, totalSupply = 1 wei. totalBefore will be roughly 100 WETH. valueAdded = 10 WETH.
Therefore:
1 * 10 / 100 = 0 strategy tokens
Manual review
Per the PrePO submission linked above, warden GreyArt does a great job explaining recommended mitigations.
#0 - KenzoAgada
2022-06-05T10:51:43Z
Duplicate of #330.
#1 - bghughes
2022-06-07T22:53:55Z
Duplicate of #330
8.2687 USDC - $8.27
##Impact
Funds could be unretrievable if msg.sender
is a smart contract and its fallback
function uses more than 2300 gas.
##Proof of Concept
The native transfer
function has a gas limit of 2300 units.
Solidity Docs
##Tools Used Manual Review
##Recommended Mitigation Steps
Consider using the native call
function alongside a boolean check for its return value.
#0 - bghughes
2022-06-07T22:24:41Z
Duplicate of #82
🌟 Selected for report: berndartmueller
Also found by: 0x1f8b, 0xDjango, 0xsomeone, ACai, Bahurum, BouSalman, CertoraInc, Deivitto, Dravee, GimelSec, IllIllI, JMukesh, Kaiziron, PP1004, Ruhum, SmartSek, VAD37, WatchPug, _Adam, aez121, antonttc, blockdev, broccolirob, camden, cccz, cryptphi, defsec, dipp, ellahi, fatherOfBlocks, gzeon, horsefacts, ilan, jayjonah8, joestakey, kenta, kenzo, minhquanym, oyc_109, pauliax, pedroais, peritoflores, sashik_eth, shenwilly, simon135, throttle, xiaoming90, z3s
0.1022 USDC - $0.10
Unsafe transfer
could lead to loss of funds for users.
Tokens that don’t implement the current EIP20 specifications might silently return false
on a failed call instead of reverting.
This would lead to user _shares
being burned and him losing the power to claim his assets.
If the asset in question was USDT the transfer would always revert as USDT return void
on transfers.
Manual Review
I recommend using safeTransfer
and safeTransferFrom
functions from OpenZeppelin’s SafeERC20 library that contain a return value check and can handle non-standard-compliant tokens.
#0 - KenzoAgada
2022-06-05T10:24:59Z
Duplicate of #316.
#1 - bghughes
2022-06-07T22:24:56Z
Duplicate of #316
🌟 Selected for report: IllIllI
Also found by: 0xDjango, Dravee, SmartSek, StErMi, oyc_109, pauliax, rotcivegaf, sashik_eth, shenwilly, xiaoming90
23.3384 USDC - $23.34
Compromised admin
can drain underlyingToken
from BathToken.sol
.
admin
calls setMarket
and sets RubiconMarketAddress
to malicious address.admin
calls approveMarket
.RubiconMarketAddress
can reallocate funds from BathToken.sol
to wherever he wants.Manual Review
Consider implementing timelock constraints on functions with onlyAdmin
modifiers.
Examples of similar issues ranked as high can be found here (issues H-07, H-09, H-10).
#0 - bghughes
2022-06-03T22:12:49Z
Acknowledged risk in the centralized system: #43 #133 #344
#1 - HickupHH3
2022-06-18T04:02:28Z
Duplicate of #249
#2 - HickupHH3
2022-06-18T04:03:23Z
While the mitigation was generic, the user did point to specific functions and explained the admin rug vector.
🌟 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
52.1176 USDC - $52.12
pragma solidity =0.7.6;
Older compilers might be susceptible to bugs. A list of known compiler bugs and their severity can be found here: https://etherscan.io/solcbuginfo
I recommend changing the solidity version pragma to the latest version to enforce the use of an up-to-date compiler.
If _proxyAdmin
is accidentally set to zero the contract would have to be redeployed.
BathHouse.sol#L122
Fees could accidentally be sent to zero address via feeTo
BathHouse.sol#L330
If market
, _feeTo
or token
are accidentally set to zero the contract would have to be redeployed.
BathToken.sol#L181
We recommend implementing a zero address check
approve
is used instead of safeApprove
or other preferred methodsAlthough safeApprove
has been deprecated it is still preferable to approve
.
Ideally safeIncreaseAllowance
and safeDecreaseAllowance
are recommended over safeApprove
.
safeTransferFrom
instead of transferFrom
Calling ERC20.transferFrom
without handling the returned value is unsafe.
Consider using OpenZeppelin's SafeERC20 library with safe versions of transfer functions.
Example: BathToken.sol#L565
initialize
function can be frontrunContract might have to be redeployed if attacker calls initialize
before initial deployer.
To avoid having to redeploy the contract or potentially being hacked by the attacker I recommend using a factory to promptly call initialize
after deployment and verify that the transaction succeeded.
Link below doesnt represent all instances: BathToken.sol#L181
returns
usageSometimes a named return is used instead of an unnamed one.
I recommend sticking with one or the other for better readability and consistency.
mark-to-marketed
Please fix to market-to-market
BathToken.sol#L49
DOMAIN_SEPARATOR
is not set as constantSet DOMAIN_SEPARATOR
as constant or remove capitalisation.
https://github.com/code-423n4/2022-05-rubicon/blob/8c312a63a91193c6a192a9aab44ff980fbfd7741/contracts/rubiconPools/BathToken.sol#L66
Block of code below should be deleted.
// /// @notice ERC-20 interface as derived from EIP-20 // contract ERC20 { // function totalSupply() public view returns (uint256); // function balanceOf(address guy) public view returns (uint256); // function allowance(address src, address guy) public view returns (uint256); // function approve(address guy, uint256 wad) public returns (bool); // function transfer(address dst, uint256 wad) public returns (bool); // function transferFrom( // address src, // address dst, // uint256 wad // ) public returns (bool); // }
After discussion with sponsor it was established that when calling placeOffer
BathPair.sol
is simultaneously and exclusively placing a bid AND ask. Not possible to place just a bid without an ask or vice versa.
Therefore it would be best to remove or
from The function that places a bid and/or ask
code comment.
BathToken.sol#L306
🌟 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
30.8421 USDC - $30.84
Shortening revert strings to fit in 32 bytes will decrease deployment time gas and will decrease runtime gas when the revert condition has been met.
BathHouse.sol#L162-178 BathToken.sol#L512)
require(IBathPair(_bathPairAddress).initialized() != true, "BathPair already initialized");
Could be changed to:
require(IBathPair( !_bathPairAddress ).initialized(), "BathPair already initialized");
This would improve gas and readability.
Please remove modifier onlyBathHouse
in BathPair.sol
as it is never used.
BathPair.sol#L142-L145
for
loop gas optimizationfor (uint256 index = 0; index < ids.length; index++) { uint256 _id = ids[index]; scrubStrategistTrade(_id); }
Gas could be saved by:
Example:
size = ids.length; for (uint256 index; index < size;) { uint256 _id = ids[index]; scrubStrategistTrade(_id); unchecked { ++index; } }