hats-finance / Circles-0x6ca9ca24d78af44582951825bef9eadcb210e5cf

Circles Protocol contracts
https://aboutcircles.com
GNU Affero General Public License v3.0
0 stars 0 forks source link

malicious user can exploit invite system #80

Open hats-bug-reporter[bot] opened 1 week ago

hats-bug-reporter[bot] commented 1 week ago

Github username: -- Twitter username: -- Submission hash (on-chain): 0x2991846bf11bf6f3ff583582a62b8f990433178e5528c57ff6aeaed44654d285 Severity: medium

Description: Description\ Malicious user can invite more people than expected within the same time period. After the invitationOnlyTime, new users need to be invited by already registered users. There is a cost for the inviter and a bonus for the new user.

    uint256 private constant WELCOME_BONUS = 48 * EXA;
    uint256 private constant INVITATION_COST = 2 * WELCOME_BONUS;

According to tests, user needs to wait around 4 days to call the personalMint() function and mint > 96 tokens to invite new user to the protocol. If user wants to invite 3 people, he need to wait around 12 days to mint the invitation cost for 3 people to be able to invite them.

The issue is that malicious user can take unfair advantage and invite 4 people and gain some extra tokens within the same 12-day period.

Consider these 2 scenarios:

Normal Conditions

Exploit

As we can see, this is an unfair advantage because in the first scenario, normal user will be able to invite just 3 users after 12 days, but in the second scenario, user can invite 4 people after 12 days (same duration).

POC\ This test shows that under normal conditions, users who wait for 12 days can only invite 3 users.

Now, the test below shows that user who exploits the invite system can invite 4 people plus have 47 extra tokens in the same period above (12 days).

function test_exploit() public{
         address Alice = makeAddr("Alice");
         address Alice2 = makeAddr("Alice2");

        ITokenV1 tokenAlice = signupInV1(Alice);

        mintV1Tokens(tokenAlice);

        vm.startPrank(Alice);
        tokenAlice.stop();
        assertTrue(tokenAlice.stopped(), "Token not stopped");
        vm.warp(1670630401 + 10);
        mockHub.registerHuman(address(0), bytes32(0));
        assertTrue(mockHub.isHuman(Alice), "Alice not registered");
        vm.stopPrank();

        skipTime(4 days + 1 hours);

        vm.prank(Alice);
        mockHub.personalMint();

        vm.prank(Alice);
        mockHub.trust(Alice2, type(uint96).max);
        vm.prank(Alice2);
        mockHub.registerHuman(Alice, bytes32(0));
        assertTrue(mockHub.isHuman(Alice2), "Bob not registered");

        skipTime(8 days);
        vm.prank(Alice);
        mockHub.personalMint();

        vm.prank(Alice2);
        mockHub.personalMint();

        uint aliceBalance = mockHub.balanceOf(Alice, mockHub.toTokenId(Alice));
        uint alice2Balance = mockHub.balanceOf(Alice2, mockHub.toTokenId(Alice2));

        console.log("alice balance", aliceBalance);
        console.log("alice2 balance", alice2Balance);

        // checking balance for inviting 4 people
        uint INVITATION_COST = 96e18;
        assert(aliceBalance - INVITATION_COST * 2 > 0);
        assert(alice2Balance - INVITATION_COST * 2 > 0);

        // checking for extra 47 tokens
        assert(alice2Balance - INVITATION_COST * 2 > 47e18);

    }

Recommendation\ Burning user tokens for invitation costs is easily escapable because user can create multiple accounts by inviting themselves, and these accounts can now be used to invite more people.

One solution is to require users to pay the invitation cost using the native token.

benjaminbollen commented 1 week ago

Thank you for your report on the potential exploitation of the invite system. After review, we've determined this is not an issue.

The scenario you've described, where a user creates additional accounts to gain more invitations, is an understood aspect of our permissionless design. The invitation cost mechanism in personal Circles functions as intended, and such actions leave clear marks on the network topology.

We appreciate your thorough analysis of our invitation system and its potential interactions with multiple accounts. Your attention to these network dynamics contributes to a broader understanding of our system's design. Thank you for your participation in this security review.

0xdanial commented 1 week ago

@benjaminbollen, thanks for your comment.

with the current structure, the invite system doesn't make sense due to the following math:

Invite-based systems are usually implemented to make registration to the protocol more valuable and rare. That's why you have invitation costs and bonuses. However, if the structure allows users to invite almost unlimited people over time, it reduce the value of invites, rendering the whole inviting system pointless.

If you don't plan to change the structure, I think this report should at least be considered Low according to Hats Finance rules:

Issues where the behavior of the contracts differs from the intended behavior (as described in the docs and by common sense), but no funds are at risk.

Thanks

0xarshia commented 1 week ago

@benjaminbollen i want to firstly thank the sponsor for carefully checking the findings, shows the amount of passion they have for security of their protocol

however issue seems valid because bad actor literally abusing the functionality in order to break the system and break the logic of invitation cost and as this was designed in order to prevent exactly this abuse of system but we see it fails to protect badactor doing it so

so either logic is useless (its NOT and have great purpose) or issue is valid cause logic needs some changes in order to work as expected

and because there is no place in the doc specifying this is a "acceptable risk" hence again should be labled valid i think

otherwise in this platform sponsors would be able to dodge the paying prize by saying its "acceptable risk" this is why almost all the contest platforms require making acceptable risks transparent in contest start page.