DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: low
Invalid

The referral system is vulnerable to Sybil attacks

Summary

The TradingAccountBranch.createTradingAccount function allows creating a trading account with a referralCode, but the present checks doesn't seem enough to prevent Sybil attacks.

Vulnerability Details

Below is TradingAccountBranch.createTradingAccount;

function createTradingAccount(
bytes memory referralCode,
bool isCustomReferralCode
)
public
virtual
returns (uint128 tradingAccountId)
{
// fetch storage slot for global config
GlobalConfiguration.Data storage globalConfiguration = GlobalConfiguration.load();
// increment next account id & output
tradingAccountId = ++globalConfiguration.nextAccountId;
// get refrence to account nft token
IAccountNFT tradingAccountToken = IAccountNFT(globalConfiguration.tradingAccountToken);
// create account record
TradingAccount.create(tradingAccountId, msg.sender);
// mint nft token to account owner
tradingAccountToken.mint(msg.sender, tradingAccountId);
emit LogCreateTradingAccount(tradingAccountId, msg.sender);
Referral.Data storage referral = Referral.load(msg.sender);
if (referralCode.length != 0 && referral.referralCode.length == 0) {
if (isCustomReferralCode) {
CustomReferralConfiguration.Data storage customReferral =
CustomReferralConfiguration.load(string(referralCode));
if (customReferral.referrer == address(0)) {
revert Errors.InvalidReferralCode();
}
referral.referralCode = referralCode;
referral.isCustomReferralCode = true;
} else {
address referrer = abi.decode(referralCode, (address));
if (referrer == msg.sender) {
revert Errors.InvalidReferralCode();
}
referral.referralCode = referralCode;
referral.isCustomReferralCode = false;
}
emit LogReferralSet(msg.sender, referral.getReferrerAddress(), referralCode, isCustomReferralCode);
}
return tradingAccountId;
}
/// @notice Creates a new trading account and multicalls using the provided data payload.
/// @param data The data payload to be multicalled.
/// @return results The array of results of the multicall.
function createTradingAccountAndMulticall(
bytes[] calldata data,
bytes memory referralCode,
bool isCustomReferralCode
)
external
payable
virtual
returns (bytes[] memory results)
{
uint128 tradingAccountId = createTradingAccount(referralCode, isCustomReferralCode);
results = new bytes[](data.length);
for (uint256 i; i < data.length; i++) {
bytes memory dataWithAccountId = bytes.concat(data[i][0:4], abi.encode(tradingAccountId), data[i][4:]);
(bool success, bytes memory result) = address(this).delegatecall(dataWithAccountId);
if (!success) {
uint256 len = result.length;
assembly {
revert(add(result, 0x20), len)
}
}
results[i] = result;
}

Since there is no limit to creating a tradingAccount per address, users can just open a new address and increase their referral score without being caught by the check below;

if (referrer == msg.sender) {
revert Errors.InvalidReferralCode();
}

Impact

It is possible to play with the referral system.

Tools Used

Foundry

Recommendations

Limiting the number of tradingAccounts that can be created per address and ideally requiring the referring account's deposited balance to be greater than 0 (or a MIN_AMOUNT you specify) are simple and highly effective measures that can be taken. It would be better to add some on-chain checks than relying on off-chain filtering to do all Sybil Attack prevention.

Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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