code-423n4 / 2023-07-tapioca-findings

13 stars 9 forks source link

Collateral can be locked in BigBang contract when `debtStartPoint` is nonzero #1046

Open code423n4 opened 1 year ago

code423n4 commented 1 year ago

Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/Penrose.sol#L395-L397 https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L242-L255 https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L180-L201 https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L512-L520 https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L309-L317 https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L263-L271

Vulnerability details

Impact

Following conditions have to be met for this issue to happen:

Now BigBang.getDebtRate() will always revert and the collateral from the first borrower is locked, because BigBang.getDebtRate() is used in BigBang._accrue(), and BigBang._accrue() is used in every function that involves totalBorrow like in BigBang.liquidate(), BigBang.repay().

The reason for the revert is that in BigBang.getDebtRate(), totalBorrow.elastic which gets assigned to the variable _currentDebt (line 186 BigBang.sol) will not be 0, and then on line 192 in the BigBang contract, the _currentDebt is smaller than debtStartPoint which causes the revert.

As a consequence the collateral is trapped as repay or liquidate requires to call accrue before hand.

Proof of Concept

The following gist contains a proof of concept to demonstrate this issue. A non-ETH bigbang market (wbtc market) is deployed with Penrose::registerBigBang. Note that the debtStartPoint parameter in the init data is non-zero (set to be 1e18).

First we set up the primary eth market: Some weth is minted and deposited to the ETH market. Then some assets were borrowed against the collateral. This is necessary condition for this bug to happen, which is the ETH market to have some borrowed asset. However, this condition is very likely to be fulfilled, as the primary ETH market would be deployed before any non-eth market.

Now, an innocent user is adding collateral and borrows in the non-eth market (the wbtc market). The issue occurs when the user borrows less than the debtStartPoint. If the user should borrow less than the debtStartPoint, the BigBang::accrue will revert and the collateral is trapped in this Market.

https://gist.github.com/zzzitron/a6d6377b73130819f15f1e5a2e2a2ba9

The bug happens here in the line 192 in the BigBang.

179     /// @notice returns the current debt rate
180     function getDebtRate() public view returns (uint256) {                                                                        
181         if (_isEthMarket) return penrose.bigBangEthDebtRate(); // default 0.5%                                                    
182         if (totalBorrow.elastic == 0) return minDebtRate;                                                                         
183         
184         uint256 _ethMarketTotalDebt = BigBang(penrose.bigBangEthMarket())                                                         
185             .getTotalDebt();                                                                                                      
186         uint256 _currentDebt = totalBorrow.elastic;
187         uint256 _maxDebtPoint = (_ethMarketTotalDebt *                                                                            
188             debtRateAgainstEthMarket) / 1e18;                                                                                     
189 
190         if (_currentDebt >= _maxDebtPoint) return maxDebtRate;
191 
192         uint256 debtPercentage = ((_currentDebt - debtStartPoint) *
193             DEBT_PRECISION) / (_maxDebtPoint - debtStartPoint);
194         uint256 debt = ((maxDebtRate - minDebtRate) * debtPercentage) /
195             DEBT_PRECISION +
196             minDebtRate;
197 

Tools Used

Manual review

Recommended Mitigation Steps

Consider adding a require statement to BigBang.borrow() to make sure that the borrow amount has to be >= debtStartPoint.

// BigBang
// borrow
247        require(amount >= debtStartPoint);

Assessed type

Other

c4-pre-sort commented 1 year ago

minhquanym marked the issue as duplicate of #1370

c4-pre-sort commented 1 year ago

minhquanym marked the issue as not a duplicate

c4-pre-sort commented 1 year ago

minhquanym marked the issue as primary issue

c4-sponsor commented 1 year ago

0xRektora (sponsor) confirmed

c4-judge commented 1 year ago

dmvt marked the issue as selected for report