Platform: Code4rena
Start Date: 17/07/2023
Pot Size: $85,500 USDC
Total HM: 11
Participants: 26
Period: 14 days
Judge: Picodes
Total Solo HM: 1
Id: 263
League: ETH
Rank: 3/26
Findings: 5
Award: $7,481.06
π Selected for report: 0
π Solo Findings: 0
2660.5978 USDC - $2,660.60
https://github.com/code-423n4/2023-07-lens/blob/main/contracts/FollowNFT.sol#L480-L520
A user can follow multiple times, specifically by first following through LensHub#follow
, which then calls FollowNFT#follow
. After that, the user can call LensV2Migration#batchMigrateFollows
if they had a pre-upgrade followNFT. Furthermore, they can repeat this process as many times as they have pre-upgrade NFTs accessible to them.
The FollowNFT#tryMigrate function does not verify if the profileId being migrated is already following.
A malicious user with a pre-upgrade FollowNFT can exploit this by first calling FollowNFT#follow through LensHub and then proceeding to migrate their FollowNFT.
As a result, this leads to double following, and in a scenario where everyone follows this pattern, a profile will end up with twice as many followers as it should have. However, these followers will only be backed by half the number of unique profiles due to the duplication.
Manual Review
Ensure that the followerProfileId in FollowNFT#tryMigrate is not following already. A similar check used in follow
function can be used here:
if (_followTokenIdByFollowerProfileId[followerProfileId] != 0) { revert AlreadyFollowing(); }
Invalid Validation
#0 - c4-pre-sort
2023-08-03T16:34:48Z
141345 marked the issue as duplicate of #146
#1 - c4-judge
2023-08-28T15:56:26Z
Picodes marked the issue as satisfactory
798.1793 USDC - $798.18
Judge has assessed an item in Issue #112 as 2 risk. The relevant finding follows:
A profile can follow itself by receiving a pre-upgrade followNFT and then using the batchMigrateFollows function."
#0 - c4-judge
2023-08-28T16:10:22Z
Picodes marked the issue as duplicate of #106
#1 - c4-judge
2023-08-28T16:10:26Z
Picodes marked the issue as satisfactory
#2 - c4-judge
2023-09-03T16:58:00Z
Picodes marked the issue as partial-50
2660.5978 USDC - $2,660.60
https://github.com/code-423n4/2023-07-lens/blob/main/contracts/misc/LensV2Migration.sol#L37 https://github.com/code-423n4/2023-07-lens/blob/main/contracts/FollowNFT.sol#L502
Adversary that possesses a followNFT that was minted before the upgrade, can send it to a target address that owns a particular profile and then execute the LensV2Migration#batchMigrateFollows
function. By providing the necessary inputs such as the profileId, idOfProfileFollowed, and the transferred tokenId, the adversary successfully enforces the target profile to follow the specified profile.
This introduces several potential attacks:
The tryMigrate function does not consider the fact that any address will be forced to be the ownerOf a tokenId if it gets sent that token. This is common to all NFTs. Alice cannot stop Bob from sending her his NFTs.
For follow tokens that were minted before the upgrade, the batchMigrateFollows which downstream calls the tryMigrate function, is used to re-register the profileId that owns those tokens and grant them functionalities equivalent to those of tokens minted after the upgrade.
As an owner of a pre-upgrade followNFT, you can forcefully register ANY address with your tokenId. This can be achieved by simply sending your followNFT to an address that owns a specific profileId and then calling the LensV2Migration#batchMigrateFollows
function, providing that profileId, idOfProfileFollowed, and the tokenId you just transferred.
Here are the checks that tryMigrate does:
// Migrated FollowNFTs should have `originalFollowTimestamp` set if (_followDataByFollowTokenId[followTokenId].originalFollowTimestamp != 0) { return 0; // Already migrated } if (_followedProfileId != idOfProfileFollowed) { revert Errors.InvalidParameter(); } if (!_exists(followTokenId)) { return 0; // Doesn't exist } address followTokenOwner = ownerOf(followTokenId); // ProfileNFT and FollowNFT should be in the same account if (followerProfileOwner != followTokenOwner) { return 0; // Not holding both Profile & Follow NFTs together }
If the tokenId has not been used in migration before, and the adversary's input parameters are valid, and the tokenId exists, the first three checks will pass. The last check ensures that the owner of the followTokenId is the same as the followerProfileOwner. Since the adversary can control the ownerOf a followTokenId (by sending it to any address they wish) and can also provide any profile as an input parameter, this check will pass. Consequently, it would lead to the registration of that profile as a follower of any profile the adversary desires.
Manual Review
I suggest allowing only the protocol or current owner of a followNFT to migrate his followNFT. Normal users should not be able to migrate another profile's followNFT unless with a signature. This way, if a malicious user sends his followNFT to another address, he won't be able to tryMigrate because he is not the current owner of the token, while the token recipient can tryMigrate at will.
Invalid Validation
#0 - vicnaum
2023-08-08T17:25:56Z
This is a valid issue and should be fixed before the upgrade. There are several similar issued in the duplicates section which we also confirm. This shouldn't be High because users assets are not at risk, it's rather a way of minting unexpected assets (and also all other duplicate issues are ranked as Medium).
#1 - c4-sponsor
2023-08-08T17:26:03Z
vicnaum marked the issue as disagree with severity
#2 - c4-judge
2023-08-28T16:00:34Z
Picodes changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-08-28T16:00:40Z
Picodes marked the issue as primary issue
#4 - c4-judge
2023-08-28T16:05:57Z
Picodes marked issue #104 as primary and marked this issue as a duplicate of 104
#5 - c4-judge
2023-08-28T16:07:11Z
Picodes marked the issue as satisfactory
#6 - c4-judge
2023-08-28T16:09:12Z
Picodes marked the issue as not a duplicate
#7 - c4-judge
2023-08-28T16:09:18Z
Picodes changed the severity to QA (Quality Assurance)
#8 - c4-judge
2023-08-28T16:10:01Z
This previously downgraded issue has been upgraded by Picodes
#9 - c4-judge
2023-08-28T16:10:02Z
This previously downgraded issue has been upgraded by Picodes
#10 - c4-judge
2023-08-28T16:10:12Z
Picodes marked the issue as duplicate of #104
#11 - MiloTruck
2023-08-29T02:04:39Z
Hi @Picodes,
This isn't my issue but wouldn't it be fairer towards the warden if it was split into three with partial rewards?
The finding here has grouped all findings related to tryMigrate()
under one bug, which is the function has missing checks. As seen below, it has correctly identified the potential impacts mentioned in #40 and #106 in point 3 and 4:
- An adversary can force any profile they wish to follow a particular profile.
- The adversary can forcefully change the tokenId that an already migrated user or post-upgrade minter is registered with.
- If a profile gets blocked, the user can refollow by accepting a pre-upgrade followNFT and then using the batchMigrateFollows function.
- A profile can follow itself by receiving a pre-upgrade followNFT and then using the batchMigrateFollows function."
Therefore, I think it would be fair for it to be split into three and duplicated to #40 and #106 as well, perhaps with partial rewards due to the lack of explanation for each root cause and mitigation.
The alternative would be to group all three findings together and duplicate them under this issue, but I don't think that's correct given that their mitigations are different.
Also, impact 2 seems to be an overlap of impact 1 and #146 (fixing either of these would fix impact 2 as well), so I don't think it should be separated into an individual finding.
Thanks!
#12 - 0xJuancito
2023-08-29T02:52:44Z
Hi @Picodes. As the author of both #104 and #106, I would like to point out the difference in root and mitigations for both issues, as demonstrated on their respective Coded POCs on each issue + the Recommended Mitigation Steps, making them independent.
As for #40 and #146 they are also independent issues as well, with their own root and mitigations.
#13 - Emedudu
2023-08-29T04:32:49Z
Hi @Picodes ,
This issue mentioned 4 different issues, as can be found under the impact section:
Personally, I'm not sure of the best way to judge this to be fair to all the wardens. If judges are allowed to split a finding, then this should probably be split into 4 findings.
Thanks for your effort!
#14 - vicnaum
2023-08-30T15:53:42Z
Me and @donosonaumczuk also agree with splitting this into 4 separate issues as proposed above
#15 - Picodes
2023-09-03T17:12:09Z
So before the QA period, I had split this issue in 2: one dup of #104 and one dup of #106 (https://github.com/code-423n4/2023-07-lens-findings/issues/189).
To me, this report is primarily a duplicate of #104 as the same scenario is described. Then, it seems that the warden correctly identified scenarios #40 and #106 but doesn't go into details and doesn't provide a mitigation.
I see 3 bugs:
partial-50
partial-50
.Then, for scenario 2 described here, it's another impact but not another issue in my opinion.
#16 - c4-judge
2023-09-03T17:12:42Z
Picodes marked the issue as not a duplicate
#17 - c4-judge
2023-09-03T17:12:47Z
Picodes changed the severity to QA (Quality Assurance)
#18 - c4-judge
2023-09-03T17:13:07Z
This previously downgraded issue has been upgraded by Picodes
#19 - c4-judge
2023-09-03T17:13:07Z
This previously downgraded issue has been upgraded by Picodes
#20 - c4-judge
2023-09-03T17:13:17Z
Picodes marked the issue as duplicate of #104
1330.2989 USDC - $1,330.30
Judge has assessed an item in Issue #112 as 2 risk. The relevant finding follows:
If a profile gets blocked, the user can refollow by accepting a pre-upgrade followNFT and then using the batchMigrateFollows function.
#0 - c4-judge
2023-09-03T17:13:55Z
Picodes marked the issue as duplicate of #40
#1 - c4-judge
2023-09-03T17:14:00Z
Picodes marked the issue as partial-50
π Selected for report: MiloTruck
Also found by: 0xAnah, AlexCzm, Bughunter101, BugzyVonBuggernaut, DavidGiladi, Emmanuel, Iurii3, Kaysoft, MohammedRizwan, Prestige, Rolezn, Sathish9098, Stormreckson, adeolu, descharre, evmboi32, fatherOfBlocks, ginlee, ihtishamsudo, juancito, mrudenko, tnquanghuy0512
31.3772 USDC - $31.38
First follower is spending more gas
the first follower of a profile is spending more gas than subsequent followers. This will be unfair to them.
As we can see in FollowLib#_follow
, if that profile has no followNFT, the first follower does the deployment, which will consume more gas.
function _follow()private returns (uint256){ ... address followNFT = _profileToFollow.followNFT; if (followNFT == address(0)) { followNFT = _deployFollowNFT(idOfProfileToFollow); _profileToFollow.followNFT = followNFT; } ... }
Manual Review
Consider deploying a followNFT at profile creation, so profile owner pays for that, while all followers spend same amount of gas to follow.
Non EOA is allowed to have a profile and follow other profiles
I think this is an issue because a user that wants a lot of followers can have many contract accounts follow him. Also, this would lead to bad UX in frontend as there will be a lot of fake profiles.
Given the peculiarity of this protocol, I consider this to be a valid issue.
Looking at LensHub#createProfile function, we can see that there is no check that prevents a non EOA from being the createProfileParams.to
.
This will allow minting of profiles to a non EOA account.
Manual Review
Consider adding an onlyEOA modifier to LensHub#createProfile