Platform: Code4rena
Start Date: 18/10/2023
Pot Size: $36,500 USDC
Total HM: 17
Participants: 77
Period: 7 days
Judge: MiloTruck
Total Solo HM: 5
Id: 297
League: ETH
Rank: 15/77
Findings: 3
Award: $414.58
🌟 Selected for report: 0
🚀 Solo Findings: 0
21.9995 USDC - $22.00
https://github.com/open-dollar/od-contracts/blob/v1.5.5-audit/src/contracts/proxies/ODProxy.sol#L26-L35 https://github.com/open-dollar/od-contracts/blob/v1.5.5-audit/src/contracts/proxies/ODSafeManager.sol#L49-L53
ODProxy which is also the _safeOwner
will not be able to call any relevant function in ODSafeManager.sol
.
The _safeOwner
in ODSafeManager.sol
refers to the proxy contract address related to the original user. This is confirmed in the function openSAFE()
where vault721.mint(_usr, _safeId)
is called. Here _usr
is the address of the ODProxy.
Now the issue is that ODProxy.sol
, which is the proxy contract of the original holder of NFT/safe. It doesn't have a function that does normal low-level call and instead have an Execute()
function which only does a delegatecall
which forces this contract to never become msg.sender
of any call it does.
Now consider the initial stages of the contract when a new NFT/safe has been minted and is mapped under an ODProxy/_safeOwner.
The ODProxy/_safeOwner will not be able to call any function that has the _safeAllowed()
modifier.
Because _safeOwner is a contract and will only do a delegate call and hence can not call the function allowSAFE()
msg.sender == _safeData[_safe].owner
will never be satisfied because the msg.sender will never be _safeOwner but the original owner of the Proxy and NFT/Safe.
This will lead to the _safeOwner
stuck in the contract and not able to call any of these functions even if these are meant to be called by it
allowSAFE() -> this will not even let proxy to let any other use those functions modifySAFECollateralization() transferCollateral() transferCollateral() transferInternalCoins() quitSystem() enterSystem() moveSAFE() addSAFE() removeSAFE() protectSAFE()
which is almost all functions that are relevant to the SAFE.
Manual Review
Some mitigations can be
_safeOwner
to be the real owner of the NFT which is also the ODProxy.OWNER. Change the mapping to contain this real owner as this will resolve this issue.ODProxy.sol
which include a normal low-level call.
This will introduce another issue that since the proxy will become msg.sender and then it will have another proxy of itself while minting or transfering NFT/safe, since the protocol will treat it as another address without a proxy attached to it.
This can be mitigated introducing a mapping in Vault721.sol
mapping (address Proxy => bool) isProxy
This will be true
for the addresses that are proxies and will be made true
when the proxy is deployed for a contract and will check that
if a contract is proxy then another proxy is not deployed for it.
DoS
#0 - c4-pre-sort
2023-10-26T17:29:52Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-10-26T17:30:03Z
raymondfam marked the issue as duplicate of #76
#2 - c4-pre-sort
2023-10-26T19:05:12Z
raymondfam marked the issue as duplicate of #380
#3 - c4-judge
2023-11-02T18:29:37Z
MiloTruck marked the issue as not a duplicate
#4 - c4-judge
2023-11-02T18:29:50Z
MiloTruck marked the issue as duplicate of #294
#5 - c4-judge
2023-11-08T00:21:46Z
MiloTruck changed the severity to 2 (Med Risk)
#6 - c4-judge
2023-11-08T00:24:54Z
MiloTruck marked the issue as unsatisfactory: Insufficient proof
#7 - MiloTruck
2023-11-08T00:25:53Z
This report assumes that ODProxy
delegate call directly into the ODSafeManager
contract, and doesn't highlight the key issue which is that BasicActions.sol
has missing functions.
#8 - c4-judge
2023-11-11T08:18:37Z
MiloTruck removed the grade
#9 - c4-judge
2023-11-11T08:18:59Z
MiloTruck marked the issue as satisfactory
#10 - MiloTruck
2023-11-11T08:19:19Z
224.3342 USDC - $224.33
Minting of the New Safes/NFTs from Vault721.sol
will revert, leading to a temporary DOS.
The function Vault721.sol
is used to mint the new safes. This function is only called by the safeManager
.
The current implementation of the ODSafeManager.sol
is such that variable _safeId
keeps check of the number of Safes that have been deployed. And this variable starts from 0 whenever a new ODSafeManager
is deployed.
So when the governor of Vault721.sol
update the address of SafeManager
, and a new ODSafeManager.sol
contract is assigned to it, _safeId
will initialize from 0 (as the ODSafeManager.sol
have a fixed address of Vault721
).
So, when a new safe is minted its tokenId/safeId will be 0, which will revert the _safeMint
function that is present in Vault721.sol/mint()
, because the tokenId is already minted earlier.
This can be escalated when a malicious user front-runs the Vault721.sol/initiaizeManager()
function at the early stages of the contract and non-zero safes/NFTs, making a DOS situation when the governor changes the manager contract manually to ODManager.sol
, because the mint will now always revert with this implementation of ODSafeManager.sol.
Manual Review
There should present a function to directly change the value of ODSafeManager.sol/_safeId
state variable or the function to sync the tokens minted and _safeId
. Access given to Vault721.sol
.
Function present in ODSafeManager.sol
function sync(uint _newSafeId) external { require(msg.sender == address(vault721), 'SafeMngr: Only Vault721'); _safeId = _newSafeId; }
Function present in Vault721.sol
,
uint internal totalMinted; function syncSend() external onlyGovernor{ IODSafeManager(safeManager).sync(totalMinted); }
and should also include the code inside the mint function to update the totalMinted
.
DoS
#0 - c4-pre-sort
2023-10-26T05:48:36Z
raymondfam marked the issue as low quality report
#1 - c4-pre-sort
2023-10-26T05:49:04Z
raymondfam marked the issue as duplicate of #37
#2 - c4-judge
2023-11-01T20:28:21Z
MiloTruck marked the issue as not a duplicate
#3 - c4-judge
2023-11-01T20:31:55Z
MiloTruck marked the issue as primary issue
#4 - c4-judge
2023-11-08T00:07:37Z
MiloTruck changed the severity to 2 (Med Risk)
#5 - c4-judge
2023-11-08T00:08:05Z
MiloTruck marked issue #381 as primary and marked this issue as a duplicate of 381
#6 - c4-judge
2023-11-08T00:12:16Z
MiloTruck marked the issue as satisfactory
🌟 Selected for report: tnquanghuy0512
Also found by: 0xprinc, Baki, COSMIC-BEE-REACH, MrPotatoMagic, Tendency
168.2507 USDC - $168.25
https://github.com/open-dollar/od-contracts/blob/v1.5.5-audit/src/contracts/proxies/SAFEHandler.sol#L11 https://github.com/open-dollar/od-contracts/blob/v1.5.5-audit/src/contracts/proxies/ODSafeManager.sol#L59-L62 https://github.com/open-dollar/od-contracts/blob/v1.5.5-audit/src/contracts/proxies/ODSafeManager.sol#L112-L115 https://github.com/open-dollar/od-contracts/blob/v1.5.5-audit/src/contracts/proxies/ODSafeManager.sol#L118 https://github.com/open-dollar/od-contracts/blob/v1.5.5-audit/src/contracts/proxies/ODSafeManager.sol#L189 https://github.com/open-dollar/od-contracts/blob/v1.5.5-audit/src/contracts/proxies/ODSafeManager.sol#L205
All the functions with handlerAllowed
modifier are unusable and can never be called.
Handler is deployed and assigned to the Safe/NFT when ODSafeManager.sol/openSAFE()
function is called.
The SafeHandler.sol
have only a constructor and not any function so it can not do any external call and hence can not become a msg.sender
for any transaction.
Now, the function ODSafeManager.sol/allowHandler()
can not be called by the SafeHandler.sol
because it don't have a function to do external call. This means it can not approve any other address also.
mapping(address _safeHandler => mapping(address _caller => uint256 _ok)) public handlerCan; function allowHandler(address _usr, uint256 _ok) external { handlerCan[msg.sender][_usr] = _ok;
So, the modifier handlerAllowed()
will always fail as there is no one allowed by safeHandler
and safeHandler
can not do an external call by itself.
modifier handlerAllowed(address _handler) { if (msg.sender != _handler && handlerCan[_handler][msg.sender] == 0) revert HandlerNotAllowed(); _; }
And hence every function containing this modifier will not execute making them non-functional.
Functions include: quitSystem() enterSystem()
Manual Review
safeHandler
should contain an OWNER variable which is changed after the NFT/safe is transferred to some other user.
There should also be present a function(only be called by the OWNER) to do an external call to other contracts so that the following issue can be mitigated.DoS
#0 - c4-pre-sort
2023-10-26T19:26:41Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-10-26T19:26:56Z
raymondfam marked the issue as duplicate of #380
#2 - c4-judge
2023-11-02T18:24:09Z
MiloTruck marked the issue as not a duplicate
#3 - c4-judge
2023-11-02T18:24:16Z
MiloTruck marked the issue as duplicate of #297
#4 - c4-judge
2023-11-02T18:53:30Z
MiloTruck changed the severity to 2 (Med Risk)
#5 - c4-judge
2023-11-02T18:53:36Z
MiloTruck marked the issue as satisfactory