Open codehawks-bot opened 1 year ago
Great write up and recommendation.
Likelihood: Medium Impact: High
High is deserved. One could argue the likelihood is low, but I clearly stated that USDC would be an acceptable currency. Great writeup. Great recommendation. Deserved selected.
This should be M severity given the low likelihood. Somebody mentioned the stats in the discord.
https://discord.com/channels/1127263608246636635/1129041993323004044/1146389688651886716
Hello, I want to give my opinion, reporting on this one because it is the selected one.
The impact mentioned here is that all the funds in the contract are locked forever making every participant lose their funds.
.
But remember, the current implementation doesn't work with fees on transfer
tokens, so USDC, DAI, WETH, USDT
couldn't be used and there is no fund at risk.
And if there is no fund at risk, So it shouldn't be high
.
The previous comment does not make sense. The issue points out that the assets are at risk if the user is blacklisted. That is common for USDC - what will be one of the whitelisted tokens.
The issue should remain high, as the PatrickAlpha states. This problem in this kind of application is huge as the seller will not get any rewards for his work because of conditions that are influenced by a third party (USDC contract).
The severity, in this case, needs to be checked based on the type of application. I see this as a significant problem for the Escrow contract.
I mentioned that as USDC
is fees on transfer
token, in the first place, it couldn't be used. (So there is no fund at risk).
The current implementation doesn't work with fees on transfer tokens.
It is true that USDC is a fee on transfer tokens. However, currently, the fee is set to zero. Still, you are pointing out another possible issue that the protocol does not take into account, but you have to address both of them. Nothing changes that this blacklist issue occurs.
I think USDC is the most popular coin and if the team had mentioned that they would not use the fee-on-transfer
tokens then most probably they were not talking about USDC.
Blacklisting is a legit issue and due to impact-and-likelihood, it is up to the judges to give H/M.
By the way, I did not submit this issue or similar so I am a neutral judge.
Escalating this to be low.
A bit on blacklisting: Blacklisting issues are usually deemed invalid in other contest platforms if no harm to the users/protocol. The reason is that usually an address that gets blacklisted will only cause harm to itself.
I remind you that for an actor to be blacklisted he needs to do a malicious action that is picked up by circle and the US government. read more: https://f.hubspotusercontent30.net/hubfs/9304636/PDF/Centre_Blacklisting_Policy_20200512.pdf
The issue claims any of the addresses involved is blacklisted, none of the participants can receive their funds.
This is not true - If only one party is blacklisted he cannot receive his funds (which makes sense because he is blacklisted). The other parties can retrieve their funds - lets consider each actor:
arbitor
- being blacklisted will prevent him from earning the arbiterFee
. Without an arbiter incentive to solve disputes - disputes will not be solved either way (known issue/no arbiter issue)buyer
- if buyer
becomes blacklisted to prevent payment to seller
it won't prevent payment because buyerAward
will be 0
and the transfer will not be attemptedseller
- if seller
becomes blacklisted to prevent payment to buyer
it won't prevent payment because buyerAward + arbiterFee
will be the contract balance and the transfer will not be attempted.I have commented on each test scenario in the POC to explain why the scenario is invalid
function testExpectRevertInConfirmReceiptIfSellerIsBlacklisted() external blacklistAddress(seller) {
vm.startPrank(buyer);
vm.expectRevert(abi.encodeWithSelector(IERC20WithBlacklist.ERC20WithBlacklist__Blacklisted.selector));
deployedEscrow.confirmReceipt();
vm.stopPrank(); // @ESCALATION: If seller is blacklisted he cannot receive funds (design of blacklist)
}
function testExpectRevertInResolveDisputeIfSellerIsBlacklisted() external blacklistAddress(seller) startDispute {
vm.startPrank(arbiter);
vm.expectRevert(abi.encodeWithSelector(IERC20WithBlacklist.ERC20WithBlacklist__Blacklisted.selector));
deployedEscrow.resolveDispute(BUYER_AWARD_ON_DISPUTE);
vm.stopPrank(); // @ESCALATION: If seller is blacklisted, the remaining of the funds can go back to the buyer by
// setting BUYER_AWARD_ON_DISPUTE to required funds. The function will not revert
}
function testExpectRevertInResolveDisputeIfBuyerIsBlacklisted() external blacklistAddress(buyer) startDispute {
vm.startPrank(arbiter);
vm.expectRevert(abi.encodeWithSelector(IERC20WithBlacklist.ERC20WithBlacklist__Blacklisted.selector));
deployedEscrow.resolveDispute(BUYER_AWARD_ON_DISPUTE);
vm.stopPrank(); // @ESCALATION: If buyer is blacklisted, BUYER_AWARD_ON_DISPUTE = 0 will not revert and seller
// will receive rest of the funds.
}
function testExpectRevertInResolveDisputeIfArbiterIsBlacklisted() external blacklistAddress(arbiter) startDispute {
vm.startPrank(arbiter);
vm.expectRevert(abi.encodeWithSelector(IERC20WithBlacklist.ERC20WithBlacklist__Blacklisted.selector));
deployedEscrow.resolveDispute(BUYER_AWARD_ON_DISPUTE); // @ESCALATION: Arbiter is trusted... should not be blacklister.
// If blacklisted, he won't receive payment so no incentive to solve disputes either way (known issue/no arbiter issue)
vm.stopPrank();
}
In the unlikely case that the buyer or seller act maliciously and get blacklisted (they should be trusted) the escrow can still perform as planned and no funds should be lost on the innocent party.
To conclude: Lets forget the fact that they are trusted - being blacklisted will not prevent payment to other actors I see this as low impact (no funds lost) and low likelihood (trusted actors need to be blacklisted)
Interesting... Checking
After consulting with the team, and reading the comments, we are moving to medium. The likelihood of this happening is low, but the impact is high, so this should be fixed.
High - Funds can be lost if any participant is blacklisted
Severity
High Risk
Relevant GitHub Links
https://github.com/Cyfrin/2023-07-escrow/blob/fa9f20a273f1e004c1b11985ef0518b2b8ae1b10/src/Escrow.sol#L98
https://github.com/Cyfrin/2023-07-escrow/blob/fa9f20a273f1e004c1b11985ef0518b2b8ae1b10/src/Escrow.sol#L120
https://github.com/Cyfrin/2023-07-escrow/blob/fa9f20a273f1e004c1b11985ef0518b2b8ae1b10/src/Escrow.sol#L123
https://github.com/Cyfrin/2023-07-escrow/blob/fa9f20a273f1e004c1b11985ef0518b2b8ae1b10/src/Escrow.sol#L127
Summary
It is stated in the readme and by Patrick Collins that the protocol will only accept vetted tokens such as
USDC
,DAI
,WETH
, etc..Yet by using these popular tokens such as
USDC
, there exists a case where the funds for every participant will locked permanently. This is due to the blacklist system which is implemented byUSDC
and many other popular well-reputed tokens.in
Escrow.sol
, if any of the participants is blacklisted (due to illegal activities, using Tornado Cash for privacy reasons, spite by one of the participants), the funds for every participant will be locked permanently.Vulnerability Details
In the
Escrow.sol
contract, when an escrow is created, there are two ways to "get the funds out".buyer
calls the functionconfirmReceipt()
which will transfer the entire contract token balance to theseller
address.buyer
or theseller
can callinitiateDispute()
and thearbiter
can call the functionresolveDispute
which will transfer part of the funds to thebuyer
address, a fee to thearbiter
address and finally, the potential remaining funds in the contract to theseller
addressIf one the participant's addresses that is set to receive funds is blacklisted however, the functions will revert and essentially "brick" the contract, making it impossible to ever recover the funds.
Blacklisting is certainly not uncommon and is used many of the popular token used for payments, such as the stablecoin
USDC
. An address can get blacklisted for illegal activities, some were blacklisted for just using tools likeTornado Cash
to keep their transactions private and it is also perfectly possible for a disgruntled participant to intentionally blacklist his address to block the withdrawal of funds.Essentially, if any of the addresses involved is blacklisted, none of the participants can receive their funds.
We have included a POC to showcase how it works in all the cases. You can get the POC file in the following gist: https://gist.github.com/TheNaubit/b0cc2e6b4d1ae2bea637d9d89d9b5b19
To run it, has paste the content of the gist inside a file called
WithdrawFailBlacklisted.t.sol
inside thetest
folder in the project. Then run them with the following command:Affected code:
For reference, there are other contests with similar findings like:
Impact
When this issue happens, all the funds in the contract are locked forever making every participant to lose their funds.
Tools Used
Manual review & Foundry
Recommendations
There are one solution with two parts:
withdraw
function only callable when the escrow is finished where each participant can withdraw the funds they own. In this case, if any participant is blacklisted, at least the rest will be able to get the rest of the funds.