VAD37
high
forget apply onlyBalancer
modifier to mintRebalancer()
in UUSD.sol
. Allowing arbitrage attack steal entire pool and collateral when rebalancing
The permission-check modifier was not included on burnRebalancer()
and mintRebalancer(
).
This oversight allows anyone to mint and burn USSD tokens on the UUSD.sol contract without any restriction.
As the rebalance function is public, it can be triggered by anyone to mint/burn USSD tokens.
When undervalued, rebalancing sells the entire USSD token balance to buy DAI, followed by some collateral rebuying. Due to unlimited USSD minting, the USSD token peg is broken.
This compels USSDRebalancer.sol
to buy the entire DAI pool for rebalancing, significantly crashing the price and enabling the attacker to buy USSD tokens at a low price. Consequently, the attacker can force the balancer to sell all collateral, buy DAI, and then buy USSD tokens at a low price to balance the pool price.
The attacker can thus steal the entire pool with an infinite amount of cheaply bought USSD tokens.
UUSD.sol
allow balancer to mint and burn new USSD tokens.
However, the onlyBalancer
modifier is not applied to mintRebalancer()
function.
This allows anyone to mint new USSD tokens to UUSD.sol
contract.
rebalance()
call SellUSSDBuyCollateral()
when USSD is undervalued or 1 USSD = 0.99000 DAI.
File: src\USSDRebalancer.sol
098: } else if (ownval > 1e6 + threshold) {
099: // mint and buy collateral
100: // never sell too much USSD for DAI so it 'overshoots' (becomes more in quantity than DAI on the pool)
101: // otherwise could be arbitraged through mint/redeem
102: // the execution difference due to fee should be taken into accounting too
103: // take 1% safety margin (estimated as 2 x 0.5% fee)
104: IUSSD(USSD).mintRebalancer(((DAIamount / 1e12 - USSDamount)/2) * 99 / 100); // mint ourselves amount till balance recover
105: SellUSSDBuyCollateral();
106: }
mintRebalancer
suppose to be called by USSDRebalancer.sol
contract only.
When anyuser can mint ridiculous amount of USSD token to UUSD.sol
contract.
USSDRbalancer.sol
can be tricked to use all available USSD token to buy entire DAI pool to rebalance.
Because the UniswapV3 seed pool position is set from tick -880000 -> 880000
in the test file. The price can easily reach 0.00000000000001e18 DAI per USSD token.
File: test\USSDsimulator.test.js
261: tickLower = "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff29280"; // tick lower 0.01 * 1e-12 = 0.00000000000001 // -880000
262: tickUpper = "00000000000000000000000000000000000000000000000000000000000d6d80"; // tick upper 100 * 1e-12 = 0.00000000100 //880000
Even if the entire pool position is only available for tick hover around 5% deviation from the 1$ price, it is still profitable.
Because the DAI price is so low after sell USSD. The balancer contract will have lots of DAI, collateral token. And the uniswap pool will only have USSD available. This allow attacker to buy free USSD at low price.
Then call balancer pool again to sell all collateral, convert all to DAI, then buy USSD token to rebalance the pool price to 1 USSD = 1 DAI. At this point attacker really just have lots of free USSD to swap for DAI.
Exploiter can steal entire uniswap pool DAI and USSD entire collateral.
https://github.com/sherlock-audit/2023-05-USSD/blob/a5bbd619d7180cb858da97bd9aebd5ae0e5cca44/src/USSD.sol#L204-L215 https://github.com/sherlock-audit/2023-05-USSD/blob/main/ussd-contracts/contracts/USSDRebalancer.sol#L104-L105
Manual Review
include onlyBalancer
modifier to rebalancer function
function mintRebalancer(uint256 amount) public onlyBalancer override {
_mint(address(this), amount);
}
function burnRebalancer(uint256 amount) public onlyBalancer override {
_burn(address(this), amount);
}