Platform: Code4rena
Start Date: 16/09/2021
Pot Size: $50,000 USDC
Total HM: 26
Participants: 30
Period: 7 days
Judge: GalloDaSballo
Total Solo HM: 17
Id: 36
League: ETH
Rank: 7/30
Findings: 5
Award: $3,127.24
🌟 Selected for report: 3
🚀 Solo Findings: 2
901.7387 USDC - $901.74
jonah1005
The function settleAuction
Auction.sol#L69-L109 calls withdrawBounty
. However, there's no safety checks in addBounty
function.Auction.sol#L126-L138 The attacker can add malicious contract through addBounty
and hijack the control flow of settleAuction
through withdrawBounty
.
The authetication checks located before reentrancy point.
require(auctionOngoing); require(hasBonded); require(bondTimestamp + ONE_DAY > block.number); require(msg.sender == auctionBonder);
The attacker can enter settleAuction
twice.
I:
-> calls settleAuction
-> pass authentication checks
-> enter II in withdrawBounty
II:
-> calls settleAuction
again but with normal parameters
-> the auction.auctionOngoing()
is now return false. Attacker can mint token in the basket contract
-> calls mint
in the basket. This would call handleFees
and make ibRate smaller than the current Ibrate
-> mint large amount of basket tokens.
-> return to I
I:
-> the auction contract calls basket.updateIBRatio
and set ibRate back to the newRate
(original one).
-> burn all basket tokens at a higher ibRate.
-> Since basket.lastFee
is set when II calls mint. The ibRate
would not change this time.
uint256 a = factory.auctionMultiplier() * basket.ibRatio(); uint256 b = (bondTimestamp - auctionStart) * BASE / factory.auctionDecrement(); uint256 newRatio = a - b;
The attacker could possibly drain all the tokens in the basket by launching a flashloan attack.
I consider this is a high-risk issue.
As stated above.
None
There are two issues involved.
addBounty
should not accept whatever tokens. A possible fix is to add a list that only DAO can decide which kind of tokens are accepted.settleAuction
should add a nonReentrant
modifier. settleAuction
is a sensitive function and should treat it with extra caution.
(ref: https://docs.openzeppelin.com/contracts/4.x/api/security#ReentrancyGuard)An attacker can pass whatever inputTokens
and outputTokens
to settleAuction
. While it seems to be safe as the reentrancy point located before the balance check. require(IERC20(pendingTokens[i]).balanceOf(address(basket)) >= tokensNeeded);
It's against the best practice. Recommend to fix it as well.
#0 - frank-beard
2021-09-29T19:10:32Z
#1 - GalloDaSballo
2021-12-19T15:45:13Z
Duplicate of #31
🌟 Selected for report: jonah1005
1001.9319 USDC - $1,001.93
jonah1005
The aution contract decides a new ibRatio
in the function settleAuction
. Auction.sol#L89-L91
uint256 a = factory.auctionMultiplier() * basket.ibRatio(); uint256 b = (bondTimestamp - auctionStart) * BASE / factory.auctionDecrement(); uint256 newRatio = a - b;
There's a chance that newRatio
would be really close to zero. This imposes too much risk on the protocol. The network may not really be healthy all the time. Solana and Arbitrum were down and Ethereum was suffered a forking issue recently. Also, the network may be jammed from time to time. This could cause huge damage to a protocol. Please refer to Black Thursday for makerdao 8.32 million was liquidated for 0 dai
Given the chance that all user may lose their money, I consider this is a medium-risk issue.
Black Thursfay for makerdao 8.32 million was liquidated for 0 dai bug-impacting-over-50-of-ethereum-clients-leads-to-fork
None
I recommend setting a minimum ibRatio
when a publisher publishes a new index. The auction should be killed if the ibRatio
is too low.
#0 - GalloDaSballo
2021-12-18T15:08:48Z
Agree that newRatio
decreases over time, if nothing is done it will eventually get close to 0, at which point tokensNeeded
will tend to 0, which would mean that the amount of underlying necessary to redeem the bond decreases over time
This can hypothetically allow to redeem the bond for extremely cheap if not for free
The bond is denominated in the basketToken which effectively represents the value of the "mixture" of the tokens
Over time, you're getting the same bond (basket) for less tokens (tokensNeeded), which also means that the basket itself is loosing value (because you can extra the excess value via bonding)
Sounds to me like the entire settleAuction
mechanism is devaluing the basket over time which arguably is a high severity vulnerability.
Will continue the judging and may end up getting in touch with the sponsor over some additional risks.
The finding is valid because it shows that the protocol can break given a specific circumstance, I do believe the warden could have found a higher severity by digging deeper
🌟 Selected for report: jonah1005
1001.9319 USDC - $1,001.93
jonah1005
The aution contract decides a new ibRatio
in the function settleAuction
. Auction.sol#L89-L91
uint256 a = factory.auctionMultiplier() * basket.ibRatio(); uint256 b = (bondTimestamp - auctionStart) * BASE / factory.auctionDecrement(); uint256 newRatio = a - b;
In this equation, a
would not always be greater than b
. The auctionBonder
may lock the token in bondForRebalance()
at a point that a-b
would always revert.
The contract should not allow users to lock the token at the point that not gonna succeed. Given the possible (huge) loss of the user may suffer, I consider this is a medium-risk issue.
Here's a web3.py script to trigger this bug.
basket.functions.publishNewIndex([dai.address], [deposit_amount]).transact() for i in range(4 * 60 * 24): w3.provider.make_request('evm_mine', []) basket.functions.publishNewIndex([dai.address], [deposit_amount]).transact() print('auction on going', auction.functions.auctionOngoing().call()) for i in range(20000): w3.provider.make_request('evm_mine', []) all_token = basket.functions.balanceOf(user).call() basket.functions.approve(auction.address, all_token).transact() auction.functions.bondForRebalance().transact() # error Log # {'code': -32603, 'message': 'Error: VM Exception while processing transaction: reverted with panic code 0x11 (Arithmetic operation underflowed or overflowed outside of an unchecked block)'} auction.functions.settleAuction([], [], [], [], []).transact()
None
Recommend to calculate the new irate in bondForRebalance
. I understand the auctionBonder
should take the risk to get the profit. However, the contract should protect the user in the first place when this auction is doomed to fail.
#0 - GalloDaSballo
2021-12-18T15:57:40Z
I do not believe the users are at risk of a specific loss as they can always redeem their shares for underlying Basket.burn
However, this is still a valid finding, and the external conditions make medium severity appropriate
jonah1005
Basket.sol#L224-L228 calls approve that do not handle non-standard erc20 behavior.
Since the auction contract calls setNewWeights
in function settleAuction
, auctionBonder
may lock its bond and never successfully settles the auction. This leads to the auctionBonder
loss he's bond and the basket and the auction becomes suspended.
auctionBonder
would lose his bond and the contract would be suspended. I consider this a high-risk issue.
USDT may be a classic non-standard erc20 token.
Here's a short POC.
usdt.functions.approve(basket.address, 100).transact() ## the second tx would be reverted as the allowance is not zero usdt.functions.approve(basket.address, 50).transact()
None
Recommend to use safeApprove
instead and set the allowance to 0 before calling it.
function approveUnderlying(address spender) private { for (uint256 i = 0; i < weights.length; i++) { IERC20(tokens[i]).safeApprove(spender, 0); IERC20(tokens[i]).safeApprove(spender, type(uint256).max); } }
#0 - frank-beard
2021-09-28T21:50:54Z
#1 - GalloDaSballo
2021-12-19T15:53:17Z
Agree with the finding, because this is contingent on an external condition (token being USDT) the finding is of medium severity
90.1739 USDC - $90.17
jonah1005
The contract allows anyone to add arbitrary any token as a bounty. Auction.sol#L126-L138. A reasonable BOT may withdraw all the active bounties.
However, the contract does not have any safety checks. An attacker may provide a malicious token contract as a bounty and steal tokens from auctionBonder
.
Calling a malicious unknown contract may cause damage. Please refer to the Thorchain incident. https://twitter.com/0xedenau/status/1418585105686089729
I consider this is a medium-risk issue.
As stated above.
None
addBounty
should not accept whatever tokens. A possible fix is to add a list that only DAO can decide which kind of tokens are accepted.
#0 - GalloDaSballo
2021-12-20T00:56:14Z
Duplicate of #55