MorpheusAI

MorpheusAI
Foundry
22,500 USDC
View results
Submission Details
Severity: medium
Invalid

Lack of whitelisted ERC20 tokens (stETH) leads to inconsistent initialization of Pool (risk of DoS)

Summary

The Distribution_init function in Distribution.sol implementation permits the inclusion of any token without undergoing a legitimacy check. This vulnerability creates the potential for a malicious actor to exploit the loophole by depositing counterfeit or non-valuable tokens into the contract allowing for the creation of Pools that are not validated properly.

Vulnerability Details

According to Morpheus documentation (https://github.com/MorpheusAIs/Morpheus/blob/main/!KEYDOCS%20README%20FIRST!/2.WhitePaper.md) the expected token for interacting with the contracts is stETH. For example:

“The Morpheus full node comes with a wallet or the user can connect their existing wallet. This enables the user to sign and send transactions recommended by their Smart Agent. So users will be able to participate in the proofs through the Morpheus software. However Capital Providers are not required to have a full node for example. They can interact directly with the Smart Contracts on Ethereum / Arbitrum using stETH”.

There are more references throughout the documentation about the expected token stETH, however inside the Distribution.sol there are currently no validations in place that can verify the correct creation of the pools using only this token. In this sense, the Distribution_init function allows to set incorrect data, with invalid pools according to the tests provided below. Moreover, this function is only meant to be called with a valid ERC20 token (the stETH) but the function is generic enough to work with any token.

##Proof of Concept

As a proof of concept a fake token contract was included into the /tokens folder named FooToken.sol

At a glance, this fake token is not ERC20 compliant, uses a floating pragma and utilizes the necessary means to be accepted into the Pool creation:

pragma solidity >=0.7.0 <0.9.0;
import "hardhat/console.sol";
contract FooToken {
string public constant name = "FooToken";
string public constant symbol = "FOO";
uint8 public constant decimals = 18;
uint256 public totalPooledEther;
uint256 public totalShares;
uint256 totalSupply_;
mapping(address => uint256) balances;
mapping(address => mapping(address => uint256)) allowed;
mapping(address => uint256) private shares;
constructor() {
totalSupply_ = 1000000 * 10 ** decimals;
balances[msg.sender] = totalSupply_;
totalPooledEther = 10 ** 10;
console.log("Injecting Fake Token:", name );
}
function totalSupply() public view returns (uint256) {
return totalSupply_;
}
function mint(address _account, uint256 _amount) external {
uint256 sharesAmount = getSharesByPooledEth(_amount);
_mintShares(_account, sharesAmount);
totalPooledEther += _amount;
}
function _mintShares(address _recipient, uint256 _sharesAmount) internal returns (uint256 newTotalShares) {
require(_recipient != address(0), "MINT_TO_ZERO_ADDR");
totalShares += _sharesAmount;
shares[_recipient] += _sharesAmount;
return totalShares;
}
function getSharesByPooledEth(uint256 _ethAmount) public view returns (uint256) {
return (_ethAmount * totalShares) / totalPooledEther;
}
function balanceOf(address tokenOwner) public view returns (uint) {
return balances[tokenOwner];
}
function approve(address delegate, uint numTokens) public returns (bool) {
allowed[msg.sender][delegate] = numTokens;
return true;
}
}

This fake token can be used in the depositedToken variable and will be included during the Pool creation:

let rewardToken: MOR;
let depositToken: FooToken;

When running the test and the FooToken is injected, we verify that our Fake, non compliant token is accepted into the Distribution_init function when this should be prohibited:

Distribution
Injecting Fake Token: FooToken
UUPS proxy functionality
#constructor
✔ should disable initialize function (40ms)
#Distribution_init
✔ should set correct data after creation
✔ should create pools with correct data (113ms)
✔ should revert if try to call init function twice
#_authorizeUpgrade
✔ should correctly upgrade (46ms)
✔ should revert if caller is not the owner
✔ should revert if `isNotUpgradeable == true`
#createPool
✔ should create pool with correct data (41ms)
✔ should correctly pool with constant reward
✔ should revert if caller is not owner
should revert if try to create pool with incorrect data
if `payoutStart == 0`
if `decreaseInterval == 0`
#editPool
✔ should edit pool with correct data
✔ should revert if try to change pool type
✔ should revert if caller is not owner
✔ should revert if pool doesn't exist
should revert if try to edit pool with incorrect data
if `decreaseInterval == 0`

Impact

Ensuring the legitimacy of the token is crucial in verifying pool creation. Failure to do so may lead to the generation of invalid pools, causing unforeseen issues in the other functionalities of the Distribution.sol contract, such as a DoS.

Tools Used

Manual verification

Recommendations

A token whitelist should be implemented, where only approved tokens can be deposited into the contract. Maybe since the admin is trusted, an onlyOwner function can be added to the contract, which allows the owner to add and remove tokens from the whitelist.

References

https://solodit.xyz/issues/lack-of-token-whitelisting-leads-to-deposit-of-fake-tokens-auditone-none-coinlend-markdown
https://solodit.xyz/issues/h-2-adversary-can-brick-bounty-payouts-by-calling-fundbountytoken-but-funding-it-with-an-erc721-token-instead-sherlock-openq-openq-git
https://solodit.xyz/issues/h-8-bounties-can-be-broken-by-funding-them-with-malicious-erc20-tokens-sherlock-openq-openq-git

Updates

Lead Judging Commences

inallhonesty Lead Judge
over 1 year ago
inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue
mrjorystewartbaxter Submitter
over 1 year ago
inallhonesty Lead Judge
over 1 year ago
mrjorystewartbaxter Submitter
over 1 year ago
inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.