Malt Finance contest - leastwood's results

Yield farmable, incentive-centric algorithmic stable coin.

General Information

Platform: Code4rena

Start Date: 25/11/2021

Pot Size: $80,000 USDC

Total HM: 35

Participants: 32

Period: 7 days

Judge: GalloDaSballo

Total Solo HM: 27

Id: 59

League: ETH

Malt Finance

Findings Distribution

Researcher Performance

Rank: 3/32

Findings: 10

Award: $7,426.18

🌟 Selected for report: 6

🚀 Solo Findings: 6

Findings Information

Labels

bug
duplicate
2 (Med Risk)

Awards

20.04 USDC - $20.04

External Links

Handle

leastwood

Vulnerability details

Vulnerability details

Impact

The purchaseAndBurn function in LiquidityExtension is called by the Auction contract when users purchase arbitrage tokens. An attacker can monitor the blockchain for calls to this function and launch a sandwich attack to steal funds, assuming the attacker is a well-funded actor.

A malicious user is potentially able to steal rewards from users. As such, this issue should be considered medium risk as this results in loss of funds, but requires the attacker to be well-funded.

Proof of Concept

Consider the following scenario:

  • An attacker buys Malt tokens from the uniswap pool.
  • They frontrun a user calling purchaseArbitrageTokens.
  • The user buys Malt tokens at an inflated price.
  • The attacker sells their Malt tokens and profits.

As shown above, attackers are able to siphon funds from users overtime.

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L184 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/LiquidityExtension.sol#L124 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/DexHandlers/UniswapHandler.sol#L131-L158

Tools Used

Manual code review Referenced Mochi review.

Consider performing slippage checks when users purchase arbitrage tokens by calling purchaseArbitrageTokens. This can be done by simply performing a lower bounds check on the output amount received when exchanging rewards tokens for Malt tokens. This change would need to be added into the purchaseAndBurn function in LiquidityExtension.

#0 - 0xScotch

2021-12-10T00:21:59Z

#219

#1 - GalloDaSballo

2022-01-24T13:32:15Z

Duplicate of #219

Findings Information

🌟 Selected for report: gpersoon

Also found by: ScopeLift, leastwood

Labels

bug
duplicate
2 (Med Risk)

Awards

298.0144 USDC - $298.01

External Links

Handle

leastwood

Vulnerability details

Impact

The notSameBlock modifier is added to sensitive functions where flashloans may put the protocol at risk. Malt Finance has preemptively addressed these concerns by limiting msg.sender from calling a set of functions multiple times within a single block. However, this can be bypassed if a flashloan attacker uses multiple contracts to act as separate EOA accounts. Therefore, notSameBlock provides security by obscurity and should be replaced with a fully fledged solution.

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Permissions.sol#L135-L141

Tools Used

Manual code review.

Consider checking that tx.origin == msg.sender in order to prevent contracts from calling such sensitive functions. However, careful consideration needs to be made as smart wallets won't be able to call the affected functions. It is also important to note that tx.origin can add additional security concerns if not implemented correctly.

#0 - 0xScotch

2021-12-08T11:39:52Z

#195

#1 - GalloDaSballo

2022-01-25T01:16:37Z

#195

Findings Information

🌟 Selected for report: danb

Also found by: WatchPug, leastwood

Labels

bug
duplicate
2 (Med Risk)

Awards

298.0144 USDC - $298.01

External Links

Handle

leastwood

Vulnerability details

Impact

The permissionless stabilize function in StabilizerNode is called to correct deviations in the Malt token price. When the price of Malt has appreciated above its peg, the function simply distributes rewards to LP token holders, effectively diluting the total Malt token supply. As the _distributeSupply function interacts with a Uniswap pool without performing slippage checks, an attacker can call this function and launch a sandwich attack in combination with a flash loan to steal funds.

A malicious user is potentially able to steal rewards that would otherwise be distributed to the protocol's users. As such, this issue should be considered high risk as this results in loss of funds.

Proof of Concept

Consider the following scenario:

  • An attacker flashloans and buys rewards tokens from the uniswap pool.
  • The attacks calls the stabilize function in StabilizerNode.
  • The StabilizerNode contract buys rewards tokens at an inflated price.
  • The attacker sells their rewards tokens and pays back their flashloan.

As shown above, attackers are able to siphon funds from users overtime.

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/StabilizerNode.sol#L236 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/DexHandlers/UniswapHandler.sol#L160-L183

Tools Used

Manual code review Referenced Mochi review.

Consider performing slippage checks within the _distributeSupply function. This can be done by simply performing a lower bounds check on the output amount received when exchanging Malt tokens for rewards tokens.

#0 - 0xScotch

2021-12-08T12:27:28Z

#56

#1 - GalloDaSballo

2022-01-23T15:46:22Z

Duplicate of #56

Findings Information

🌟 Selected for report: leastwood

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

1103.7569 USDC - $1,103.76

External Links

Handle

leastwood

Vulnerability details

Impact

purchaseArbitrageTokens enables users to commit collateral tokens and in return receive arbitrage tokens which are redeemable in the future for Malt tokens. Each auction specifies a commitment cap which when reached, prevents users from participating in the auction. However, realCommitment can be ignored by directly sending the LiquidityExtension contract collateral tokens and subsequently calling purchaseArbitrageTokens.

Proof of Concept

Consider the following scenario:

  • An auction is currently active.
  • A user sends collateral tokens to the LiquidityExtension contract.
  • The same user calls purchaseArbitrageTokens with amount 0.
  • The purchaseAndBurn call returns a positive purchased amount which is subsequently used in auction calculations.

As a result, a user could effectively influence the average malt price used throughout the Auction contract.

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L177-L214 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/LiquidityExtension.sol#L117-L128

Tools Used

Manual code review.

Consider adding a check to ensure that realCommitment != 0 in purchaseArbitrageTokens.

#0 - GalloDaSballo

2022-01-24T13:41:01Z

The warden has identified a way to side-step the cap on commitments. Because the commitments are used for calculating limits, but maltPurchased is used to calculate rewards, an exploiter can effectively use an auction to purchase as many arbitrage tokens as they desire.

Using any amount greater than zero will eventually allow to end the auction, however, by using 0 this process can be repeated continuously.

Agree with the finding and severity

Findings Information

🌟 Selected for report: leastwood

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

1103.7569 USDC - $1,103.76

External Links

Handle

leastwood

Vulnerability details

Impact

_startAuction utilises the SwingTrader contract to purchase Malt. If SwingTrader has insufficient capital to return the price of Malt back to its target price, an auction is triggered with the remaining amount. However, no auction is triggered if the current auction exists, but msg.sender is still rewarded for their call to stabilize.

Proof of Concept

_shouldAdjustSupply initially checks if the current auction is active, however, it does not check if the current auction exists. There is a key distinction between the auctionActive and auctionExists functions which are not used consistently. Hence, an auction which is inactive but exists would satisfy the edge case and result in triggerAuction simply returning.

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L382-L386 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L268-L272 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/StabilizerNode.sol#L342-L344 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L873-L888

Tools Used

Manual code review.

Consider using auctionExists and auctionActive consistently in StabilizerNode and Auction to ensure this edge case cannot be abused.

#0 - GalloDaSballo

2022-01-24T13:55:10Z

The warden found a way to effectively mint free malt by spamming the caller incentive by abusing a specific edge case.

Findings Information

🌟 Selected for report: leastwood

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

1103.7569 USDC - $1,103.76

External Links

Handle

leastwood

Vulnerability details

Impact

_calculateMaltRequiredForExit in AuctionEscapeHatch currently uses Malt's spot price to calculate the quantity to return to the exiting user. This spot price simply tracks the Uniswap pool's reserves which can easily be manipulated via a flash loan attack to extract funds from the protocol.

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/AuctionEscapeHatch.sol#L65-L92 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/AuctionEscapeHatch.sol#L193 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/DexHandlers/UniswapHandler.sol#L80-L109 https://shouldiusespotpriceasmyoracle.com/

Tools Used

Manual code review.

Consider implementing/integrating a TWAP oracle to track the price of Malt.

#0 - GalloDaSballo

2022-01-25T01:15:19Z

I feel like this issue highlights the design challenge that the sponsor will have to solve, on one hand the protocol is meant to stabilize the price of malt in specific pools (impossible to block / control every pool due to permissionless nature). At the same time in order to determine which direction to move the price to, they need to refer to the pricing of the underlying pools (in this case a UniV2Pool, most likely from QuickSwap)

Personally I understand the finding and think it's valid, however I don't believe there's easy answers as to how the sponsor should address this.

Whenever there's excess value there will be entities trying to seize it and perhaps through such a harsh environment this protocol can truly find a way to be sustainable.

That said, I'll mark the finding as valid, but believe this specific issue underlines the challenges that await the sponsor in making the protocol succesful

Findings Information

🌟 Selected for report: leastwood

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

1103.7569 USDC - $1,103.76

External Links

Handle

leastwood

Vulnerability details

Impact

addLiquidity is called when users reinvest their tokens through bonding events. The RewardReinvestor first transfers Malt and rewards tokens before adding liquidity to the token pool. addLiquidity provides protections against slippage by a margin of 5%, and any dust token amounts are transferred back to the caller. In this instance, the caller is the RewardReinvestor contract which further distributes the dust token amounts to the protocol's treasury. However, the token approval for this outcome is not handled properly. Dust approval amounts can accrue over time, leading to large Uniswap approval amounts by the UniswapHandler contract.

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/DexHandlers/UniswapHandler.sol#L212-L214 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/DexHandlers/UniswapHandler.sol#L216-L218

Tools Used

Manual code review.

Consider resetting the approval amount if either maltUsed < maltBalance or rewardUsed < rewardBalance in addLiquidity.

#0 - GalloDaSballo

2022-01-25T01:32:08Z

The UniV2Router will first calculate the amounts and then pull them from the msg.sender

This means that approvals may not be fully utilized, leaving traces of approvals here and there. This can cause issues with certain tokens (USDT comes to mind), and will also not trigger gas refunds.

Findings Information

🌟 Selected for report: leastwood

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

1103.7569 USDC - $1,103.76

External Links

Handle

leastwood

Vulnerability details

Impact

_distributeRewards attempts to reward LP token holders when the price of Malt exceeds its price target. Malt Finance is able to being Malt back to its peg by selling Malt and distributing rewards tokens to LP token holders. An external call to Auction is made via the allocateArbRewards function. Prior to this call, the StabilizerNode approves the contract for a fixed amount of tokens, however, the allocateArbRewards function does not necessarily utilise this entire amount. Hence, dust token approval amounts may accrue from within the StabilizerNode contract.

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/StabilizerNode.sol#L252-L253 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L809-L871

Tools Used

Manual code review

Consider resetting the approval amount if the input rewarded amount to allocateArbRewards is less than the output amount.

#0 - GalloDaSballo

2022-01-25T01:36:59Z

Finding is valid, incorrect approvals can cause reverts (USDT being an example), and not fully resetting will also negate gas refunds

Findings Information

🌟 Selected for report: leastwood

Labels

bug
2 (Med Risk)
sponsor confirmed

Awards

1103.7569 USDC - $1,103.76

External Links

Handle

leastwood

Vulnerability details

Impact

MaltDataLab integrates several MovingAverage contracts to fetch sensitive data for the Malt protocol. Primary data used by the protocol consists of the real value for LP tokens, the average price for Malt and average reserve ratios. trackMaltPrice, trackPoolReserves and trackPool are called by a restricted role denoted as the UPDATER_ROLE and represented by an EOA account and not another contract. Hence, the EOA account must consistently update the aforementioned functions to ensure the most up-to-date values. However, miners can censor calls to MaltDataLab and effectively extract value from other areas of the protocol which use stale values.

Proof of Concept

Consider the following attack vector:

  • The price of Malt exceeds the lower bound threshold and hence stabilize can be called by any user.
  • The _stabilityWindowOverride function is satisfied, hence the function will execute.
  • The state variable, exchangeRate, queries maltPriceAverage which may use an outdated exchange rate.
  • _startAuction is executed which rewards msg.sender with 100 Malt as an incentive for triggering an auction.
  • As the price is not subsequently updated, a malicious attacker could collude with a miner to censor further pool updates and continue calling stabilize on every fastAveragePeriod interval to extract incentive payments.
  • If the payments exceed what the UPDATER_ROLE is willing to pay to call trackMaltPrice, a user is able to sustain this attack.

This threatens the overall stability of the protocol and should be properly handled to prevent such attacks. However, the fact that MaltDataLab uses a series of spot price data points to calculate the MovingAverage also creates an area of concern as well-funded actors could still manipulate the MovingAverage contract by sandwiching calls to trackMaltPrice, trackPool and trackPoolReserves.

trackMaltPrice, trackPool, and trackPoolReserves should be added to the following areas of the code where applicable. https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Bonding.sol#L159 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Bonding.sol#L173 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Bonding.sol#L177 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L881 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L710 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/StabilizerNode.sol#L156 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/StabilizerNode.sol#L190 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/ImpliedCollateralService.sol#L105

Tools Used

Manual code review.

Consider adding calls to trackMaltPrice, trackPoolReserves and trackPool wherever the values are impacted by the protocol. This should ensure the protocol is tracking the most up-to-date values. Assuming the cumulative values are used in the MovingAverage contracts, then sensitive calls utilising MaltDataLab should be protected from flashloan attacks. However, currently this is not the case, rather MovingAverage consists of a series of spot price data points which can be manipulated by well-funded actors or via a flashloan. Therefore, there needs to be necessary changes made to MaltDataLab to use cumulative price updates as its moving average instead of spot price.

#0 - 0xScotch

2021-12-08T11:35:09Z

Gas issues were the reason updates weren't inlined into paths that update critical values. However based on some thoughts from the team over the past few weeks and suggestions in other audit findings I think we can reduce gas enough to make it viable to inline it

#1 - GalloDaSballo

2022-01-22T14:40:01Z

  1. Miners can censor transactions, that is a fact
  2. Relying on an external call can cause race conditions or situations where the call didn't happen (price didn't update in time)
  3. Arguably a malicious actor could collude with miners with the goal of extracting further value

However, the target chain for the deployment is Polygon, so unless they were to create custom validator code, and collude 1-1 with each validator (no flashbots on Polygon, yet) then the attack is increasingly more complex.

The attack is reliant on the externalities of the validator + somebody incentivized enough to exploit this.

I agree with he finding and believe after stabilize the system should auto-update the prices Because of the external requirements am downgrading to medium severity

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter