Summary
The updateReferrerInfo
function is designed to update the referral information of a user, including the referrer address, referrer rate, and authority rate. The function validates the input parameters and stores the referral information in the referralInfoMap
mapping. The ReferralInfo
struct contains three arguments: referrer
, referrerRate
, and authorityRate
.
Vulnerability Details
The key issue lies in the way the referral information is stored in the referralInfoMap
. Instead of using the user’s address (the caller of the function) as the key, the _referrer
address is used as the key. This leads to a situation where the referral information is incorrectly assigned to the referrer, rather than the user who is making the referral.
Code Snippet
https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/storage/SystemConfigStorage.sol#L32
mapping(address => ReferralInfo) public referralInfoMap;
https://github.com/Cyfrin/2024-08-tadle/blob/04fd8634701697184a3f3a5558b41c109866e5f8/src/core/SystemConfig.sol#L41-L80
function updateReferrerInfo(
address _referrer,
uint256 _referrerRate,
uint256 _authorityRate
) external {
if (_msgSender() == _referrer) {
revert InvalidReferrer(_referrer);
}
if (_referrer == address(0x0)) {
revert Errors.ZeroAddress();
}
if (_referrerRate < baseReferralRate) {
revert InvalidReferrerRate(_referrerRate);
}
uint256 referralExtraRate = referralExtraRateMap[_referrer];
uint256 totalRate = baseReferralRate + referralExtraRate;
if (totalRate > Constants.REFERRAL_RATE_DECIMAL_SCALER) {
revert InvalidTotalRate(totalRate);
}
if (_referrerRate + _authorityRate != totalRate) {
revert InvalidRate(_referrerRate, _authorityRate, totalRate);
}
@-> ReferralInfo storage referralInfo = referralInfoMap[_referrer];
referralInfo.referrer = _referrer;
referralInfo.referrerRate = _referrerRate;
referralInfo.authorityRate = _authorityRate;
emit UpdateReferrerInfo(
msg.sender,
_referrer,
_referrerRate,
_authorityRate
);
}
Impact
Incorrect Referral Info Assignment: When a user (e.g., Alice) calls updateReferrerInfo
to set Bob as their referrer, the mapping incorrectly stores Bob's address as the key. As a result, when Alice or any other user checks Alice's referral info, the parameters (referrer, referrerRate, authorityRate) will be zero, meaning Alice's referral info is never properly set.
Unauthorized Referral Info Update: If Bob checks his referral info, he will see that the referrer
, referrerRate
, and authorityRate
have been updated without his permission, as the mapping incorrectly assigns Alice's settings to him.
Proof Of Concept
Alice call the updateReferrerInfo
and make the bob as their account referrer.
But the mapping set the referrer address as the key instead of the alice address.
So that referral info stuct parameters for the alice is still zero .
And the referral info for the bob is updated .
Proof Of Code
For testing the POC, run the following command:
forge test --mt test_IncorrectRefrralAssignment -vvvv
pragma solidity ^0.8.13;
import {PreMarketsTest} from "./PreMarkets.t.sol";
import {ISystemConfig} from "../src/interfaces/ISystemConfig.sol";
import {console2} from "forge-std/Test.sol";
contract ReferrerTest is PreMarketsTest {
address alice = address(0x123);
address bob = address(0x456);
function test_IncorrectRefrralAssignment() public {
vm.startPrank(user1);
systemConfig.updateReferralExtraRateMap(bob, 5);
vm.stopPrank();
vm.startPrank(alice);
address referrer = bob;
uint256 referrerRate = baseReferralRate;
uint256 authorityRate = 5;
systemConfig.updateReferrerInfo(bob, referrerRate, authorityRate);
systemConfig.getReferralInfo(alice);
systemConfig.getReferralInfo(bob);
vm.stopPrank();
}
}
Here are the logs of The POC
UpgradeableProxy::getReferralInfo(0x0000000000000000000000000000000000000123) [staticcall]
│ ├─ [7110] SystemConfig::getReferralInfo(0x0000000000000000000000000000000000000123) [delegatecall]
│ │ └─ ← [Return] ReferralInfo({ referrer: 0x0000000000000000000000000000000000000000, referrerRate: 0, authorityRate: 0 })
│ └─ ← [Return] ReferralInfo({ referrer: 0x0000000000000000000000000000000000000000, referrerRate: 0, authorityRate: 0 })
├─ [1631] UpgradeableProxy::getReferralInfo(0x0000000000000000000000000000000000000456) [staticcall]
│ ├─ [1110] SystemConfig::getReferralInfo(0x0000000000000000000000000000000000000456) [delegatecall]
│ │ └─ ← [Return] ReferralInfo({ referrer: 0x0000000000000000000000000000000000000456, referrerRate: 300000 [3e5], authorityRate: 5 })
│ └─ ← [Return] ReferralInfo({ referrer: 0x0000000000000000000000000000000000000456, referrerRate: 300000 [3e5], authorityRate: 5 })
├─ [0]
Tools Used
Foundry
Recommendations
The function should be modified to use the address of the user making the referral (the caller of the function) as the key in the referralInfoMap
. This ensures that the referral information is correctly associated with the user who is performing the action.
function updateReferrerInfo(
address _referrer,
uint256 _referrerRate,
uint256 _authorityRate
) external {
if (_msgSender() == _referrer) {
revert InvalidReferrer(_referrer);
}
if (_referrer == address(0x0)) {
revert Errors.ZeroAddress();
}
if (_referrerRate < baseReferralRate) {
revert InvalidReferrerRate(_referrerRate);
}
uint256 referralExtraRate = referralExtraRateMap[_referrer];
uint256 totalRate = baseReferralRate + referralExtraRate;
if (totalRate > Constants.REFERRAL_RATE_DECIMAL_SCALER) {
revert InvalidTotalRate(totalRate);
}
if (_referrerRate + _authorityRate != totalRate) {
revert InvalidRate(_referrerRate, _authorityRate, totalRate);
}
-- ReferralInfo storage referralInfo = referralInfoMap[_referrer];
++ ReferralInfo storage referralInfo = referralInfoMap[msg.sender];
referralInfo.referrer = _referrer;
referralInfo.referrerRate = _referrerRate;
referralInfo.authorityRate = _authorityRate;
emit UpdateReferrerInfo(
msg.sender,
_referrer,
_referrerRate,
_authorityRate
);
}