Open hats-bug-reporter[bot] opened 2 months ago
Avoiding Vouch Penalties on POHv2
This is a duplicate of https://github.com/hats-finance/Proof-Of-Humanity-V2-0xef0709445d394a22704850c772a28a863bb780b0/issues/129
Hello @clesaege
This is not a duplicate of issue #129.
The root cause of issue #129 is the lack of a check for the voucherHumanity.vouching
status for V1 profiles.
In contrast, the root cause of issue #134 is the incorrect usage of the accountHumanity[_account]
variable.
Additionally, the impacts of the two issues are different.
This vulnerability allows all V1 profiles to bypass revocation requests and avoid penalties for malicious vouching on both POHv1 and POHv2.
Fixing one issue will not resolve the other. The vulnerability will persist in the one that remains unfixed.
I wasn't finished^^. The thing is that you reported two different issues, the first one being already identified. I'll answer to the second one too.
Avoiding Revocation Requests on POHv2
We'll double check internally but looks valid.
The humanityId is retrieved by checking in the array directly while it should have been retrieved using humanityOf(address _account)
to account for V1 profiles. This means that if we have a V1 profile, we'll access an empty humanity which will pass this check even when it shouldn't.
This means that if there is a revocation request, this will not prevent transfer. This can be used to doge a revocation request (but only once as if we transfer back we'll now have a V2 profile). Note that it is already possible to doge the first revocation request (you can watch the mempool and transfer immediately, this is why we have a transferCooldown, this way if you doge a revocation on one platform by transferring, you'll be blocked on the other platform for the duration of the cooldown and a honest user can ask for revocation during this time). Here the dodging is a bit worse as you can wait until the revocation request time has passed or a ruling is final to transfer your profile (while the mempool doge doesn't allow you to gain much time as you can get a revocation request just after the transfer of another platform).
This is definitely not High (Issues that allows a takeover of the registry) but does fit Medium (Issues that can lead to improper profile creation/removal but only at small scale or to steal funds).
We'll need to test, but I think this can be solved by using humanityOf
.
function ccDischargeHumanity(
address _account
) external onlyCrossChain returns (bytes20 humanityId, uint40 expirationTime) {
humanityId = humanityOf(_account]);
Humanity storage humanity = humanityData[humanityId];
require(humanity.nbPendingRequests == 0);
if (humanity.owner == _account && block.timestamp < humanity.expirationTime) {
require(!humanity.vouching);
expirationTime = humanity.expirationTime;
delete humanity.owner;
} else {
// Should revert in case account is not registered.
expirationTime = forkModule.tryRemove(_account);
}
emit HumanityDischargedDirectly(humanityId);
}
Yes, humanityOf
can solve this issue. The edge case involving the 0x0 address will be handled by the forkModule.tryRemove(_account)
call within the ccDischargeHumanity
function.
function humanityOf(address _account) public view override returns (bytes20 humanityId) {
humanityId = accountHumanity[_account];
Humanity storage humanity = humanityData[humanityId];
if (humanity.owner != _account || humanity.expirationTime < block.timestamp) {
if (forkModule.isRegistered(_account)) humanityId = bytes20(_account);
@> else humanityId = bytes20(0x0);
}
}
Github username: -- Twitter username: -- Submission hash (on-chain): 0xe5dc4705e84915d0e5acbc15c722ad7b64240a024498f42039f03f3ec27416d4 Severity: high
Description: Description
V1 users can bypass the entire vouching, challenge, and appeal process by transferring their humanity to another chain using the
CrossChainProofOfHumanity::transferHumanity
function. In contrast, when claiming or renewing their humanity through theProofOfHumanityExtended
contract’sclaimHumanity
orrenewHumanity
functions, V1 users must go through the entire claiming process.The
CrossChainProofOfHumanity::transferHumanity
function can be invoked by users of any Proof of Humanity (PoH) version. This function callsProofOfHumanityExtended::ccDischargeHumanity
to remove the owner of the humanity record. The issue arises because V1 users can directly call this function to switch chains:If a V1 user has not created a profile on POHv2, the state variables in the
ProofOfHumanityExtended
contract corresponding to v1 users will lack complete data. TheaccountHumanity
variable, in particular, will be unset for V1 profiles that have not created humanity on POHv2. This variable is used in theccDischargeHumanity
function, which will always have a0
humanityId for all V1 humanity profiles that lack a V2 profile:As the
0
humanityId remains unassigned, the correspondinghumanityData
variable will have no data. Consequently, whenever a V1 user callsccDischargeHumanity
without creating a V2 humanity, theirhumanityData
will contain only default data.This loophole allows V1 users with pending requests to switch chains and retain their humanity, as this condition is always satisfied:
Since any V1 humanity user’s
humanityData
will contain default values, thehumanity.nbPendingRequests
variable will always default to0
.Moreover, since V1 users without a POHv2 profile will have the
humanity.owner
set to0x0
, this section of code will never execute:Instead, the following block will execute, calling the
forkModule::tryRemove
function:The
forkModule
contract’stryRemove
function only checks four conditions:This function does not check other critical indicators such as request count, pending requests, vouch status, or humanity status, leading to inconsistent data in POHv2.
As a result, any V1 user without a V2 profile on POHv2 can switch chains via
ccDischargeHumanity
as long as their humanity on V1 is registered, not expired, created before the fork time and not removed from the ForkModule. The has several adverse effects:Avoiding Revocation Requests on POHv1: A V1 user facing a revocation request on POHv1 can call the
transferHumanity
function before therule
orexecuteRequest
functions are called, switching chains and retaining their humanity on the new chain, thereby avoiding revocation. This is possible because there is no check for therequests.length
in the whole transfer process.Avoiding Vouch Penalties on POHv1: A V1 user penalized for malicious vouching on POHv1 can invoke the
transferHumanity
function before theprocessVouches
function is called, switch chains, and retain their humanity on the new chain, effectively avoiding penalties. This is possible because there is no check for thehasVouched
status in the whole transfer process.Additionally, in POHv2, the
accountHumanity
variable is only set when a user claims or renews humanity through the_requestHumanity
function:Therefore, V1 users who do not create a V2 profile will have their
accountHumanity
set to0
by default.Avoiding Revocation Requests on POHv2: If a revocation request is made against a V1 user on POHv2 using
revokeHumanity
function inProofOfHumanityExtended
contract, thehumanityData
updated during in this function will use the V1 user’s defaulthumanityId
, which is their address. While logically correct, the issue arises with theccDischargeHumanity
function. Since this function retrieves the humanityId from theaccountHumanity
variable, all V1 users will have a default humanityId of0
if they did not create humanity on v2. As a result, V1 users can switch chains usingtransferHumanity
even if a revocation request is active against them, effectively avoiding revocation.Avoiding Vouch Penalties on POHv2: Similar to point 3, the
humanityData
for a V1 user is updated using the defaulthumanityId
whenprocessVouches
function is called, which is their address. However, in theccDischargeHumanity
function, a V1 user retrieves the humanity data for the0
humanityId, which is empty by default. This allows V1 users to switch chains usingtransferHumanity
beforeprocessVouches
function is called even if they face penalties for malicious vouching, effectively avoiding the penalty.In all these cases, V1 humanity users can circumvent penalties.
Mitigation
accountHumanity
variable line in theccDischargeHumanity
function and use thehumanityId
directly:transferHumanity
function. V1 users should first create a V2 profile before being allowed to switch chains.