Malt Finance contest - Meta0xNull'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: 8/32

Findings: 5

Award: $3,234.62

🌟 Selected for report: 8

🚀 Solo Findings: 2

Findings Information

🌟 Selected for report: Meta0xNull

Labels

bug
duplicate
2 (Med Risk)

Awards

1103.7569 USDC - $1,103.76

External Links

Handle

Meta0xNull

Vulnerability details

Impact

TIMELOCK_ROLE Can Withdraw All FUND from the Contracts via emergencyWithdrawGAS(), emergencyWithdraw(), partialWithdrawGAS(), partialWithdraw().

While I believe developer have good intention to use these functions. It often associate with Rug Pull by developer in the eyes of investors because Rug Pull is not uncommon in Defi. Investors lose all their hard earn money.

Read More: $10.8M Stolen, Developers Implicated in Alleged Smart Contract 'Rug Pull' https://www.coindesk.com/tech/2020/12/02/108m-stolen-developers-implicated-in-alleged-smart-contract-rug-pull/

Read More: The Rise of Cryptocurrency Exit Scams and DeFi Rug Pulls https://www.cylynx.io/blog/the-rise-of-cryptocurrency-exit-scams-and-defi-rug-pulls/

Proof of Concept

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

Tools Used

Manual Review

  1. Pause the Contract and Disable All Functions when Bad Thing Happen rather than Withdraw All Fund to a random address.
  2. If Withdraw Fund can't avoid, a Multi Sig ETH Address should be hardcoded into the contract to ensure the fund move to a safe wallet.

#0 - 0xScotch

2021-12-08T12:47:54Z

#263

#1 - GalloDaSballo

2022-01-08T17:14:36Z

This is not a duplicate of #263, where 263 talks about sidestepping the delay of the timelock, this finding talks about the high degree of power that the TIMELOCK_ROLE has.

This is a typical "admin privilege" finding, it's very important to disclose admin privileges to users so that they can make informed decisions

In this case the TIMELOCK_ROLE can effectively rug the protocol, however this is contingent on the account that has the role to pull the rug.

Because of it's reliance on external factors, am downgrading the finding to medium severity

Findings Information

Labels

bug
duplicate
2 (Med Risk)

Awards

20.04 USDC - $20.04

External Links

Handle

Meta0xNull

Vulnerability details

Impact

A victim transaction trades a crypto-currency asset X (ex: ETH, DAI, SAI, VERI) to another crypto-asset Y makes a large purchase. A bot sniffs out the transaction and Front-Runs the victim by purchasing asset Y before the large trade is approved. This purchase raises the price of asset-Y for the victim trader and increases the slippage ( Expected price increase or decrease in price based on the volume to be traded and the available liquidity).

Because of this high purchase of asset Y, its price goes up, and Victim buys at a higher price of asset Y, then the attacker sells at a higher price.

Sandwich Attack Explain: https://medium.com/coinmonks/defi-sandwich-attack-explain-776f6f43b2fd

The router.swapExactTokensForTokens: amountOutMin set to 0 mean Sandwich Attack Bot Can Fully Take Advantage of Swing Traders and Stabilizer Node until drain their fund until 0. This is achievable because Swing Traders and Stabilizer Node are trying to ensure Token Price = $1 and thus their behavior are predicable and thus Both Are Very Vulnerable to Sandwich Attack Bot.

No Sandwich Attack Bot: Buy Token @ 0.99 Sell Token @ 1.01

With Sandwich Attack Bot: Buy Token >= 1.01 Sell Token <= 0.99

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/DexHandlers/UniswapHandler.sol#L148-L153 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/DexHandlers/UniswapHandler.sol#L173-L178

Tools Used

Manual Review

The amountOutMin should not be 0. Swing Traders and Stabilizer Node should be allowed to Set Their Own Slippage But Not Zero.

#0 - 0xScotch

2021-12-10T17:41:03Z

#219

#1 - GalloDaSballo

2022-01-09T23:21:13Z

Duplicate of #219

Findings Information

🌟 Selected for report: Meta0xNull

Labels

bug
2 (Med Risk)
sponsor disputed

Awards

1103.7569 USDC - $1,103.76

External Links

Handle

Meta0xNull

Vulnerability details

Impact

There are a lot of different roles in Malt Finance to handle different tasks. All these roles only can set by Admin. If anything happen to Admin and he/she no longer available, the protocol will start countdown to the end of life.

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L890-L1013 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/RewardSystem/RewardDistributor.sol#L291-L345 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Bonding.sol#L327-L355 Many More Sol....

Tools Used

Manual Review

  1. Some tasks don't really need a special role like StabilizerNode. Should allow any community members to run their own StabilizerNode without approval needed.
  2. Consider transfer Admin to Multisig or DAO.

#0 - 0xScotch

2021-12-03T14:03:35Z

This is a known risk that is by design. We will migrate to DAO control of parameters when the protocol matures.

#1 - GalloDaSballo

2022-01-09T23:22:25Z

I respect the wardens work for flagging this up. Because any of these exploits are contingent on the Admin being malicious, I'll downgrade to Medium Severity

Findings Information

🌟 Selected for report: Meta0xNull

Also found by: 0x1f8b

Labels

bug
1 (Low Risk)
disagree with severity
sponsor confirmed

Awards

165.5635 USDC - $165.56

External Links

Handle

Meta0xNull

Vulnerability details

Impact

Is Not Uncommon Normal Users Accidentally Send Tokens into Contract.

ENS Airdrop is a Good Example Normal Users Accidentally Send Tokens into Contract: https://discuss.ens.domains/t/social-amend-airdrop-proposal-to-include-accidentally-returned-funds/6975

In UniswapHandler.sol, sellMalt(), addLiquidity() and removeLiquidity() Have No Access Control. When Normal Users Accidently Deposit Tokens into the Contract, Any Random Persons/Bot Can Withdraw the Tokens because it will safeTransfer to msg.sender who find out there is token balance in the contract.

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/DexHandlers/UniswapHandler.sol#L185-L219 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/DexHandlers/UniswapHandler.sol#L221-L245

Tools Used

Manual Review

Add relevant access control, probably Only StabilizerNode and Admin have Access to this contract various functions like sellMalt(), addLiquidity() and removeLiquidity() etc.

#0 - 0xScotch

2021-12-10T01:25:56Z

Disagree with severity as there is no part of the protocol that would leave funds in this contract. Therefore there is no direct risk of protocol user's funds. This can only lead to lost funds if a random user sends funds directly to this contract.

#1 - GalloDaSballo

2022-01-09T22:44:32Z

As the Sponsor said, the contract won't have any additional funds. As such there's no particular risk for funds. Am going to mark this as low because there seems to be an inconsistency (lack of clarity) between the buyMalt and the sellMalt, I believe allowing both to be called by anyone is completely fine as the contract is just a utility tool

Findings Information

🌟 Selected for report: Meta0xNull

Also found by: BouSalman

Labels

bug
1 (Low Risk)
sponsor confirmed

Awards

165.5635 USDC - $165.56

External Links

Handle

Meta0xNull

Vulnerability details

Impact

A wrong user input or wallets defaulting to the zero addresses for a missing input can lead to the contract needing to redeploy or Users'FUND Locked inside the Contract.

Proof of Concept

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

Tools Used

Manual Review

requires Addresses is not zero.

require(_admin != address(0), "Address Can't Be Zero")

#0 - GalloDaSballo

2022-01-18T14:34:17Z

Lack of address checks is a low severity finding, agree with the finding

Findings Information

🌟 Selected for report: cmichel

Also found by: 0x1f8b, Meta0xNull, WatchPug, defsec, jayjonah8, leastwood, stonesandtrees

Labels

bug
duplicate
1 (Low Risk)

Awards

21.9968 USDC - $22.00

External Links

Handle

Meta0xNull

Vulnerability details

Impact

<code> function initialize( </code> <code> address _timelock,</code> <code> address initialAdmin</code> <code> ) external initializer {</code> <code> _adminSetup(_timelock);</code> <code> _setupRole(ADMIN_ROLE, initialAdmin);</code>

The are multiple contracts using Initializable requires to call the initialize() function with suitable parameters.

However, there is no access control in place to avoid an attacker to front-run this call. As a result of that, an attacker could temporarily block the initialization front-running each deployment, using invalid parameters in the initialize call.

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/TransferService.sol#L21-L27 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Malt.sol#L22-L40 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/PoolTransferVerification.sol#L28-L42 More...

Tools Used

Manual Review

  1. Use Constructor Rather Than initialize()

  2. If initialize() Is Unavoidable, then Hardcode an ETH Address in initialize(): require( msg.sender == 0xYOURETHADDRESS );

#0 - 0xScotch

2021-12-08T13:03:10Z

#245

#1 - GalloDaSballo

2022-01-22T15:20:05Z

Duplicate of #245

Findings Information

🌟 Selected for report: Meta0xNull

Also found by: pauliax

Labels

bug
1 (Low Risk)
sponsor confirmed

Awards

165.5635 USDC - $165.56

External Links

Handle

Meta0xNull

Vulnerability details

Impact

* [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}.

There are multiple contracts that import Permissions.sol and using Deprecated Function _setupRole() with Security Problem that Applicable to all these contracts because all of the contracts use initialize() Rather Than Constructor.

Proof of Concept

https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol#L174-L186 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Permissions.sol#L53 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Permissions.sol#L117 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Permissions.sol#L121

Tools Used

Manual Review

Replace _setupRole() with _grantRole()

#0 - GalloDaSballo

2022-01-24T00:36:58Z

Great find by the warden, in lack of any exploitative POC, I agree with low severity

Findings Information

🌟 Selected for report: Meta0xNull

Labels

bug
1 (Low Risk)
sponsor confirmed

Awards

367.919 USDC - $367.92

External Links

Handle

Meta0xNull

Vulnerability details

Impact

The current ownership transfer process involves the current TIMELOCK_ROLE calling reassignGlobalAdmin(). If the nominated EOA account is not a valid account, it is entirely possible the owner may accidentally transfer ownership to an uncontrolled account, breaking all functions with the TIMELOCK_ROLE modifier.

Proof of Concept

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

Tools Used

Manual Review

Consider implementing a two step process where the TIMELOCK_ROLE nominates an account and the nominated account needs to call an accept_TIMELOCK_ROLE() function for the transfer of ownership to fully succeed. This ensures the nominated EOA account is a valid and active account.

#0 - GalloDaSballo

2022-01-25T00:54:48Z

Personally I don't believe the "2 step governance change" to be a low severity finding, however the majority of contest rates it as such so we'll allow

Findings Information

🌟 Selected for report: WatchPug

Also found by: GiveMeTestEther, Meta0xNull, robee, ye0lde

Labels

bug
duplicate
G (Gas Optimization)

Awards

7.4171 USDC - $7.42

External Links

Handle

Meta0xNull

Vulnerability details

Impact

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.

Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/RewardSystem/RewardDistributor.sol#L275 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/DAO.sol#L56 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L223 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Auction.sol#L230 More...

Tools Used

Manual Review

Shorten the revert strings to fit in 32 bytes.

Or consider using Custom Errors (solc >=0.8.4).

#0 - 0xScotch

2021-12-09T22:22:29Z

#317

#1 - GalloDaSballo

2021-12-27T22:14:35Z

Duplicate of #317

Findings Information

🌟 Selected for report: Meta0xNull

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

56.5245 USDC - $56.52

External Links

Handle

Meta0xNull

Vulnerability details

Impact

The local variable used as for loop index need not be initialized to 0 because the default value is 0. Avoiding this anti-pattern can save a few opcodes and therefore a tiny bit of gas.

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/TransferService.sol#L87 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/AuctionBurnReserveSkew.sol#L54 https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/Malt.sol#L34-L37 More...

Tools Used

Manual Review

Remove explicit 0 initialization of for loop index variable.

Before: for (uint i = 0; After for (uint i;

#0 - GalloDaSballo

2022-01-16T15:43:19Z

Assigning zero will add an extra 3 gas cost (MSTORE)

Findings Information

🌟 Selected for report: Meta0xNull

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

56.5245 USDC - $56.52

External Links

Handle

Meta0xNull

Vulnerability details

Impact

removeVerifier() loops follows this for-each pattern: for (uint256 i = 0; i < array.length; i++) { // do something with array[i] }

In such for loops, the array.length is read on every iteration, instead of caching it once in a local variable and read it again using the local variable.

Proof of Concept

https://github.com/code-423n4/2021-11-malt/blob/main/src/contracts/TransferService.sol#L87-L88

Tools Used

Manual Review

Read these values from memory once, cache them in local variables and then read them again using the local variables. For example:

Before: for (uint i = 0; i < verifierList.length - 1; i = i + 1) { if (verifierList[i] == _address) {

After: uint256 verifierList_temp = verifierList

for (uint i = 0; i < verifierList_temp.length - 1; i = i + 1) { if (verifierList_temp[i] == _address) {

#0 - GalloDaSballo

2022-01-16T15:42:31Z

Agree with finding, it's best to: -> Cache the contents of the list (avoid multiple HOT SLOADS) -> Cache the length of the list (avoid multiple array boundaries check)

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