Platform: Code4rena
Start Date: 07/04/2023
Pot Size: $47,000 USDC
Total HM: 20
Participants: 120
Period: 6 days
Judge: GalloDaSballo
Total Solo HM: 4
Id: 230
League: ETH
Rank: 15/120
Findings: 3
Award: $564.03
🌟 Selected for report: 0
🚀 Solo Findings: 0
26.761 USDC - $26.76
In PrivatePool, there are functions that assume the trader has approved the contract to spend their funds. For example, these functions are buy(), change(), and deposit():
However, these functions do not reset the allowance based on the results of the trades. The allowance can be greater than the commission requires, up to infinity. For example, some DeFi projects on Ethereum Mainnet set a request on the frontend to sign a transaction for infinite allowance so that the user does not pay unnecessary fees next time they interact with the project.
The pool owner can use the execute() function to call ERC20(baseToken).transferFrom(victimAddress, maliciousOwner, anyAmountOfTokens)
on behalf of the pool, which has the approval from the victim to transfer their funds:
It should be noted that there is no implemented router for PrivatePool with ERC-20 base token. Therefore, interaction with such private pools is currently possible only directly, which makes the importance of the discovery HIGH.
It is recommended to remove the execute() function and reconsider the way airdrops are received, or to clear the trader's allowance at the end of the transactions.
#0 - c4-pre-sort
2023-04-20T16:40:02Z
0xSorryNotSorry marked the issue as duplicate of #184
#1 - c4-judge
2023-05-01T19:21:29Z
GalloDaSballo marked the issue as satisfactory
506.2665 USDC - $506.27
Ownership of the pool is determined through ownership of the Factory ERC721 NFT:
modifier onlyOwner() virtual { if (msg.sender != Factory(factory).ownerOf(uint160(address(this)))) { revert Unauthorized(); } _; }
The current owner can trade their ownership NFT in exchange for funds comparable to those locked in the pool. Or it could be the owner of an empty pool who transferred their NFT to someone else, and that person decided to provide liquidity to it.
However, before doing so, a malicious owner can use the PrivatePool.execute()
function to grant themselves unlimited approval to withdraw baseToken from the pool, as well as make themselves the NFT operator for this pool, giving them the ability to always withdraw any NFT from the pool:
privatePool.execute( pool.baseToken(), abi.encodeWithSignature( "approve(address,uint256)", hackerAddress, type(uint).max ) ); privatePool.execute( pool.nft(), abi.encodeWithSignature( "setApprovalForAll(address,bool)", hackerAddress, true ) )
Thus, the previous owner will be able to withdraw all the funds and NFTs from the pool, even if they are no longer the actual owner of the pool.
In its current form, the execute()
function is too dangerous and should be removed.
#0 - c4-pre-sort
2023-04-20T17:55:05Z
0xSorryNotSorry marked the issue as duplicate of #295
#1 - c4-judge
2023-04-28T11:02:47Z
GalloDaSballo marked the issue as duplicate of #230
#2 - c4-judge
2023-04-30T16:37:22Z
GalloDaSballo changed the severity to 2 (Med Risk)
#3 - c4-judge
2023-05-01T07:28:56Z
GalloDaSballo marked the issue as satisfactory
🌟 Selected for report: AkshaySrivastav
Also found by: 0x5rings, 0xbepresent, ABA, Bauchibred, BenRai, DadeKuma, ElKu, RaymondFam, Rolezn, adriro, btk, chaduke, devscrooge, dingo2077, minhtrng, nemveer, p0wd3r, rbserver, ulqiorra
30.9954 USDC - $31.00
Factory contract is Owned. However, it is recommended to use a two-step ownership mechanism, which reduces the probability of making a mistake when transferring ownership: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/3b117992e17ae67fcd19ea2bd988f64520813cd1/contracts/access/Ownable2Step.sol
#0 - c4-judge
2023-05-01T08:50:27Z
GalloDaSballo marked the issue as grade-c
#1 - c4-judge
2023-05-04T17:50:13Z
GalloDaSballo marked the issue as grade-b