Platform: Code4rena
Start Date: 08/09/2023
Pot Size: $70,000 USDC
Total HM: 8
Participants: 84
Period: 6 days
Judge: gzeon
Total Solo HM: 2
Id: 285
League: ETH
Rank: 82/84
Findings: 1
Award: $12.79
π Selected for report: 0
π Solo Findings: 0
π Selected for report: castle_chain
Also found by: 0xAadi, 0xHelium, 0xLook, 0xblackskull, 0xfuje, 0xmystery, 0xnev, 0xpiken, 7ashraf, BARW, Bauchibred, Bughunter101, Ch_301, JP_Courses, Kaysoft, Krace, MohammedRizwan, SanketKogekar, Sathish9098, alexzoid, ast3ros, btk, catellatech, degensec, fatherOfBlocks, grearlake, imtybik, jkoppel, jolah1, klau5, lsaudit, m_Rassska, merlin, mrudenko, nobody2018, rokinot, rvierdiiev, sandy
12.7917 USDC - $12.79
The Escrow.sol
smart contract receive assets from investors and send back those assets to investors when they withdraw. However some tokens (e.g. USDC, USDT) have a contract level admin controlled address blocklist. If an address is blocked, then transfers to and from that address are forbidden. see here
If a user invest in a pool using one of those tokens and after get added to blocklist by these tokens owner, the invested funds get trapped into escrow contract forever.
Here is an example of blockable token: https://github.com/d-xo/weird-erc20/blob/04fbd6411f201da59a895dd6d3506913a525dfa9/src/BlockList.sol#L1-L29
// Copyright (C) 2020 d-xo // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.6.12; import {ERC20} from "./ERC20.sol"; contract BlockableToken is ERC20 { // --- Access Control --- address owner; modifier auth() { require(msg.sender == owner, "unauthorised"); _; } // --- BlockList --- mapping(address => bool) blocked; function block(address usr) auth public { blocked[usr] = true; } function allow(address usr) auth public { blocked[usr] = false; } // --- Init --- constructor(uint _totalSupply) ERC20(_totalSupply) public { owner = msg.sender; } // --- Token --- function transferFrom(address src, address dst, uint wad) override public returns (bool) { require(!blocked[src], "blocked"); require(!blocked[dst], "blocked"); return super.transferFrom(src, dst, wad); } }
As we can see if an address is blocked it can never receive or transfer tokens again. This situation result in user funds being trapped everywhere he invested ( in this case Cantrifuge ).
However Centrifuge did not implement a withdraw function in the Escrow.sol
contract to allow an emergency withdraw of tokens that can potentially be stuck inside forever.
Manual review
Add an emergency admin withdraw function to withdraw an user funds if he gets blocked by asset contract owner. Here is an example:
function withdraw(address asset, address user) external onlyAdmin { IERC20(asset).transfer(msg.sender, balances[user]); } modifier onlyAdmin() { if(msg.sender != admin) revert(); _; }
For more transparancy this function should only be called by admin after a governance vote.
ERC20
#0 - c4-pre-sort
2023-09-14T21:30:42Z
raymondfam marked the issue as primary issue
#1 - c4-pre-sort
2023-09-14T21:30:51Z
raymondfam marked the issue as sufficient quality report
#2 - c4-pre-sort
2023-09-17T03:47:33Z
raymondfam marked the issue as high quality report
#3 - c4-sponsor
2023-09-18T12:27:05Z
hieronx marked the issue as disagree with severity
#4 - hieronx
2023-09-18T12:28:05Z
This is a a general issue with blacklisted tokens, and many DeFi protocols will have similar behaviour (a protocol like MakerDAO also does not have such emergency withdraw functions). By adding such a function, centralization risk is significantly increased, which is not worth it. I would consider this low
.
#5 - c4-sponsor
2023-09-18T12:28:48Z
hieronx (sponsor) acknowledged
#6 - c4-judge
2023-09-25T14:22:18Z
gzeon-c4 changed the severity to QA (Quality Assurance)
#7 - c4-judge
2023-09-26T18:18:35Z
gzeon-c4 marked the issue as grade-b
#8 - 0xbtk
2023-09-27T15:15:25Z
I believe that this don't falls under "Low risk" as there is a risk of permanent frozen funds:
Low risk (e.g. assets are not at risk: state handling, function incorrect as to spec, issues with comments).
Medium severity seems appropriate based on c4 severity categorization:
Assets not at direct risk, but the function of the protocol or its availability could be impacted, or leak value with a hypothetical attack path with stated assumptions, but external requirements.
wdyt @gzeon ?
#9 - gzeon-c4
2023-09-27T17:30:39Z
I think an external blacklist is clearly out-of-scope.