Platform: Code4rena
Start Date: 05/10/2022
Pot Size: $50,000 USDC
Total HM: 2
Participants: 80
Period: 5 days
Judge: GalloDaSballo
Id: 168
League: ETH
Rank: 5/80
Findings: 3
Award: $2,969.45
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: dipp
Also found by: 0x4non, 0x52, 0xRobocop, 0xc0ffEE, 8olidity, Ch_301, Jeiwan, Junnon, KIntern_NA, Lambda, M4TZ1P, MiloTruck, Nyx, PaludoX0, Ruhum, RustyRabbit, Soosh, TomJ, Trust, arcoun, aviggiano, bardamu, cryptonue, csanuragjain, d3e4, enckrish, exd0tpy, hansfriese, jayphbee, joestakey, ladboy233, minhquanym, minhtrng, nicobevi, obront, polymorphism, rokinot, romand, rotcivegaf, rvierdiiev, saian, serial-coder, trustindistrust, zzykxx
114.8239 USDC - $114.82
https://github.com/code-423n4/2022-10-blur/blob/2fdaa6e13b544c8c11d1c022a575f16c3a72e3bf/contracts/matchingPolicies/StandardPolicyERC1155.sol#L33 https://github.com/code-423n4/2022-10-blur/blob/2fdaa6e13b544c8c11d1c022a575f16c3a72e3bf/contracts/matchingPolicies/StandardPolicyERC1155.sol#L59
ERC1155 tokens can support variable supply of each token ID. Therefore, ERC1155 NFTs can have amount more than 1. However, the StandardPolicyERC1155.sol policy always returns 1 as the matched amount, which is what will be traded. The key issue is that order.amount is never taken into consideration by the policy. So, victim may offer to buy with X ETH, Y amount of token Z, but attacker will sell them 1 for the same price.
Buyers may get much fewer NFTs than they pay for when buying ERC1155 tokens.
Manual audit https://betterprogramming.pub/erc1155-example-to-mint-multiple-nfts-cc716673d556
Instead of using hardcoded amount 1 for ERC1155 tokens, use order.amount. Also, the policy must make sure the buy and sell amounts are equal.
#0 - GalloDaSballo
2022-10-13T22:28:49Z
🌟 Selected for report: Soosh
Also found by: 0x1f8b, 0x4non, 0xc0ffEE, RaymondFam, Ruhum, RustyRabbit, Trust, arcoun, cccz, d3e4, minhtrng, rotcivegaf, saian, sakshamguruji
In the Blur security model, users approve their NFTs / WETH to the ExecutionDelegate (ED) contract. Only whitelisted contracts may call the ED transfer functions, which currently should only be the exchange proxy address. There are two rugging opportunities that arise from the implementation of the security model:
Dishonest or hacked owner may immediately steal all WETH and NFTs trusted to the exchange.
Owner performs an implementation upgrade to BlurExchange. The new implementation has a backdoor function, which allows owner to call ExecutionDelegate's transfer functions with any parameters. Owner calls backdoor function to claim all trusted assets.
Manual audit
BlurExchange brands itself as a non-custodial NFT protocol. As such it should not have the capability to claim user's holdings for itself. An easy solution is an addition of time lock functionality:
#0 - GalloDaSballo
2022-10-14T20:42:27Z
#1 - GalloDaSballo
2022-10-27T15:47:21Z
R
#2 - trust1995
2022-10-29T00:11:58Z
I believe this report introduces two significant centralization threats not discussed in referenced dup (esp. description point 2).
#3 - trust1995
2022-11-15T09:19:12Z
I will make a fact-based case of why this is a Med finding.
Firstly, it is not a dup of #713. #713 discusses a new executionDelegate which won't have revokedApproval set as previous one. That's not an actual issue because in Blur funds are approved to the executionDelegate, so a new delegate will not have users' approvals to begin with.
This report describes 2 centralization risks. Risk 1: Existing executionDelegate accepts any transferERC721 / transferERC20 / transferERC1155 calls from Exchange proxy address. Since there is no timelock (or a provable one), there is nothing stopping malicious or hacked owner from immediately upgrading the Exchange to this contract:
function _stealTokens( address token, address from, address to, uint256 tokenId, uint256 amount, AssetType assetType ) external onlyOwner { /* Call execution delegate. */ if (assetType == AssetType.ERC721) { executionDelegate.transferERC721(token, from, to, tokenId); } else if (assetType == AssetType.ERC1155) { executionDelegate.transferERC1155(token, from, to, tokenId, amount); } else if (assetType == AssetType.ERC20) { executionDelegate.transferERC20(token, from, to, amount); }
I will reiterate that this risk is not speculation on future code, it is a demonstration that existing code + dishonest owner results in rug pull of all Exchange funds. The existing code allows adding arbitrary abuse of approval, which is overly dangerous.
Risk 2: This risk shows admin can do a complete rug pull without any contract upgrade. Contracts are approved here:
function approveContract(address _contract) onlyOwner external { contracts[_contract] = true; emit ApproveContract(_contract); }
Then, they will pass the approvedContract() modifier:
modifier approvedContract() { require(contracts[msg.sender], "Contract is not approved to make transfers"); _; }
Which makes all the transfer calls accepted from a new approvedContract:
function transferERC721(address collection, address from, address to, uint256 tokenId) approvedContract external { require(revokedApproval[from] == false, "User has revoked approval"); IERC721(collection).safeTransferFrom(from, to, tokenId); }
So we've demonstrated a dead simple path where a malicious / hacked owner can abuse approvals to existing executionDelegate by adding a new evil contract to approved contract list and calling transfer functions on executionDelegate from that contract.
Again there is zero speculation on future code, only a demonstration of how existing code facilitates a rug pull. Speculation would be to state that owner will rug pull the project, we are merely pointing out that owner can rug pull the project.
I think this establishes clearly enough that owner can indeed instantly rug all user funds. Now we can take a look at previous similar findings: https://github.com/code-423n4/2022-06-illuminate-findings/issues/282 - shows admin can approve() malicious tokens and thereby steal user funds. The suggested fix is the same as mine, to put behind a timelock. It's essentially the same impact and similar call to risk 2. https://github.com/code-423n4/2022-10-thegraph-findings/issues/300 - simply stating that Governor has infinite approval on bridge funds is enough for M finding.
Also we can take a look at countless inferior centralization threats marked as M+ : https://gist.github.com/GalloDaSballo/881e7a45ac14481519fb88f34fdb8837 And only recently, a much weaker centralization threat in ArtGobblers was awarded M ( malicious admin can prevent new mints).
Hopefully this makes the case clear enough but would be happy to factually discuss any objection.
#4 - GalloDaSballo
2022-11-15T23:34:57Z
Dup of #235
🌟 Selected for report: 0x4non
Also found by: 0x1f8b, 0xNazgul, 0xSmartContract, Deivitto, IllIllI, Lambda, RaymondFam, Rolezn, RustyRabbit, Trust, arcoun, bin2chen, brgltd, csanuragjain, d3e4, enckrish, exd0tpy, ladboy233, nicobevi, rbserver, rotcivegaf, simon135, zzykxx
416.821 USDC - $416.82
The domain separator is supposed to guarantee the hash signed can only be applied in the context intended by the signer. However, the separator used by all order signature types in BlurExchange, does not differentiate between different implementations of the contract. The "verifyingContract" field in DOMAIN_SEPARATOR is set with address(this), which is the proxy address, not implementation.
User's submitted order is binding through logical updates of the contract. This has unforeseeable consequences for users of the platform.
Manual audit
Use the implementation address as well as the proxy address in the domain separator. An easy way is to add an immutable variable impl_address
which is set in the BlurExchange constructor.
#0 - minhquanym
2022-10-11T08:11:01Z
version
will be changed when BlurExchange implementation is updated
#1 - trust1995
2022-10-12T20:17:08Z
Possibly, still I wouldn't count on developers bumping the version for each update, more often then not it will just lie around in the codebase. Good observation though!
#2 - blur-io-toad
2022-10-16T17:17:01Z
All upgrades will be made publicly and will give users time to adjust their listings as necessary. Won't be making changes as upgrading the version
can still keep listings safe.
#3 - GalloDaSballo
2022-10-28T23:07:24Z
Will give a second look, but similarly to _gap like finding, we cannot make conjectures about code that is not in-scope.
#4 - GalloDaSballo
2022-10-29T00:40:33Z
I thin that because of the logical similarity to the Gap reports, the finding is valid.
But because we cannot determine a risk based on future code, I will downgraded to QA, Low Severity.
We can do this with recommendation that the sponsor does check the version value on new upgrades, however we cannot comment on the security of code from the future.
L