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: 26/77
Findings: 2
Award: $205.39
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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/f4f0246bb26277249c1d5afe6201d4d9096e52e6/src/contracts/proxies/ODSafeManager.sol#L205 https://github.com/open-dollar/od-contracts/blob/f4f0246bb26277249c1d5afe6201d4d9096e52e6/src/contracts/proxies/ODSafeManager.sol#L112 https://github.com/open-dollar/od-contracts/blob/f4f0246bb26277249c1d5afe6201d4d9096e52e6/src/contracts/proxies/SAFEHandler.sol#L11
The allowHandler
function in ODSafeManager.sol
contract is designed to be called by the SAFEHandler
to grant permission for transferring debt and collateral to the target Safe. However, the SAFEHandler.sol contract lacks the necessary method to do so. As a result, users interested in entering the system won't be able to do so, thus disrupting the intended functionality of the contracts.Because of the design flow in allowHandler
function the other intention here would have been that SafeOwner will call this function to allow permission for it's own safe but in either case this modifier will revert all the time
modifier handlerAllowed(address _handler) { if (msg.sender != _handler && handlerCan[_handler][msg.sender] == 0) revert HandlerNotAllowed(); _; }
function allowHandler(address _usr, uint256 _ok) external { handlerCan[msg.sender][_usr] = _ok; emit AllowHandler(msg.sender, _usr, _ok); }
lets suppose alice owns safe A and BoB Owna safe B
msg.sender
as approver
and _usr as the caller. _usr
is supposed to call the 'enterSystem' typically the owner(alice's proxy) of the safe or manager of the safe.allowHandler
.Bob calls this function through it's proxy because safe is owned by Bob's proxy, to grant permission to migrating the safe (lockedCollateral, generatedDebt) from a src handler(bob's handler) to the safe handler(alice's handler).SAFEHandler.sol
it will revert all the time
https://github.com/open-dollar/od-contracts/blob/f4f0246bb26277249c1d5afe6201d4d9096e52e6/src/contracts/proxies/SAFEHandler.sol#L11ODSafeManager.sol
contract then allowHandler
function will set msg.sender
as approver
and _usr
(address of the alice's proxy or address of safe manager) as the caller that will call 'enterSystem'.enterSystem(address _src, uint256 _safe)
with _src = bob's proxy address or original safe address of bob ,_safe = safe id of the safe owned by the alice.safeAllowed(_safe)
modifier will not revert but handlerAllowed(_src)
modifer will always revert because
handlerCan[_handler][msg.sender] is 0 all the time.modifier handlerAllowed(address _handler) { if (msg.sender != _handler && handlerCan[_handler][msg.sender] == 0) revert HandlerNotAllowed(); _; }
vscode
EITHER ADD A METHOD IN SAFEHandler
CONTRACT TO CALL allowHandler
OR
MODIFY allowHandler
FUNCTION TO REGISTER HANDLER ADDRESS MANUALLY
function allowHandler(address _usr, uint256 _ok,uint256 _safeId) external { - handlerCan[msg.sender][_usr] = _ok; + address _owner = _safeData[_safeId].owner; + require(_owner == msg.sender,"error"); + address _safeHandler= _safeData[_safeId].safeHandler; + handlerCan[safeHandler][_usr] = _ok; emit AllowHandler(msg.sender, _usr, _ok); }
Other
#0 - c4-pre-sort
2023-10-26T06:13:43Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-10-26T06:13:54Z
raymondfam marked the issue as duplicate of #76
#2 - c4-pre-sort
2023-10-26T19:04:45Z
raymondfam marked the issue as duplicate of #380
#3 - c4-judge
2023-11-02T18:23:50Z
MiloTruck marked the issue as not a duplicate
#4 - c4-judge
2023-11-02T18:24:00Z
MiloTruck marked the issue as duplicate of #297
#5 - c4-judge
2023-11-02T18:53:40Z
MiloTruck marked the issue as satisfactory
🌟 Selected for report: MrPotatoMagic
Also found by: Bughunter101, COSMIC-BEE-REACH, HChang26, Stormreckson, T1MOH, Tendency, hals, josephdara, klau5, merlin, tnquanghuy0512, twcctop
37.1417 USDC - $37.14
The allowSAFE
function in the ODSafeManager
contract allows the owner of a SAFE to grant or revoke permissions for another user address to manage the SAFE. However, the function does not differentiate between adding and revoking permissions. This lack of differentiation means that both the owner and the current SAFE manager can use this function to either add a backup SAFE manager or remove the current/Other SAFE manager.
The impact of this vulnerability is that it allows for potentially unauthorized or unintended changes to the SAFE manager. An owner who may intend to add a backup SAFE manager which current manager can remove and a current manager could add a backup manager who is not intended to have control. This can lead to security risks and unintended consequences.
allowSAFE(_safe, _SafeManager1, 1)
and allowSAFE(_safe, _SafeManager2, 1)
to grant
permissions to a manager.
The desired result is to add managers.allowSAFE(_safe, _unauthorizedBackupManager, 1)
to grant permissions to an
unauthorized address.allowSAFE(_safe, _SafeManager2, 1)
to grant permissions to an
unauthorized address.// SPDX-License-Identifier: GPL-3.0 pragma solidity 0.8.19; import "forge-std/console.sol"; import {Vault721} from "@contracts/proxies/Vault721.sol"; import {ODSafeManager} from "@contracts/proxies/ODSafeManager.sol"; import {ISAFEEngine} from "@interfaces/ISAFEEngine.sol"; import {Test} from "lib/forge-std/src/Test.sol"; import {DummySAFEEngine} from "test/mocks/SAFEEngineForTest.sol"; contract SafeManagerTest is Test { DummySAFEEngine mockSafeEngine; ODSafeManager safeManager; address public governer; address public user1; address public user2; address public user3; // address public randomDestination; address public unauthorizedManager; address public user1Proxy; address public user2Proxy; address public user3Proxy; uint256 public safeId; function setUp() public { governer = address(0x0111); user1 = address(0x0222); user2 = address(0x0333); user3 = address(0x0444); // randomDestination = address(0x0555); unauthorizedManager = address(0x0666); mockSafeEngine = new DummySAFEEngine(); Vault721 vault721 = new Vault721(governer); safeManager = new ODSafeManager( address(mockSafeEngine), address(vault721) ); user1Proxy = vault721.build(user1); user2Proxy = vault721.build(user2); user3Proxy = vault721.build(user3); safeId = safeManager.openSAFE(bytes32("WETH"), address(user1Proxy)); } function test_transferBad() public { vm.startPrank(user1Proxy); // call this `function allowSAFE(address _usr, uint256 _ok)` safeManager.allowSAFE(safeId, address(user2Proxy), 1); safeManager.allowSAFE(safeId, address(user3Proxy), 1); vm.stopPrank(); vm.startPrank(user2Proxy); // now safeManager.allowSAFE calls `function allowSAFE(uint256 _safe, address _usr, uint256 _ok)` to remove the user2Proxy safeManager.allowSAFE(safeId, address(user3Proxy), 0); // now safeManage adds unauthorizedManager to the safe safeManager.allowSAFE(safeId, address(unauthorizedManager), 1); } }
Manual Review, vscode, Foundry testing
remove modifier safeAllowed(uint256 _safe)
function allowSAFE(uint256 _safe, address _usr, uint256 _ok) external {
address _owner = _safeData[_safe].owner;
Access Control
#0 - c4-pre-sort
2023-10-26T06:08:11Z
raymondfam marked the issue as low quality report
#1 - c4-pre-sort
2023-10-26T06:08:15Z
raymondfam marked the issue as primary issue
#2 - raymondfam
2023-10-26T06:08:43Z
Invalid assumptions. There can only be one Safe Manager.
#3 - c4-judge
2023-11-02T04:10:10Z
MiloTruck marked the issue as unsatisfactory: Invalid
#4 - MiloTruck
2023-11-02T04:12:43Z
Edit: This as confirmed by the sponsor as not intended behavior, my bad.
#5 - c4-judge
2023-11-02T05:42:57Z
MiloTruck removed the grade
#6 - c4-judge
2023-11-02T05:43:47Z
MiloTruck marked the issue as duplicate of #171
#7 - c4-judge
2023-11-02T08:44:20Z
MiloTruck marked the issue as satisfactory