Quex V1 - Quex


Prepared by:

Halborn Logo

HALBORN

Last Updated 07/02/2025

Date of Engagement: June 24th, 2025 - June 25th, 2025

Summary

100% of all REPORTED Findings have been addressed

All findings

4

Critical

0

High

1

Medium

1

Low

2

Informational

0


1. Summary

Quex engaged Halborn to perform a security assessment of their smart contracts from June 24th to June 25th, 2025. The assessment scope was limited to the smart contracts provided to the Halborn team. Commit hashes and additional details are available in the Scope section of this report.

2. Assessment Summary

The Halborn team dedicated two days to this engagement, with one full-time security engineer assigned to evaluate the security of the smart contracts.

The assigned security engineer is an expert in blockchain and smart contract security, possessing advanced skills in penetration testing, smart contract exploitation, and extensive knowledge of multiple blockchain protocols.

The objectives of this assessment were to:

    • Verify that the smart contract functions operate as intended.

    • Identify potential security vulnerabilities within the smart contracts.


In summary, Halborn identified several areas for improvement to reduce the likelihood and impact of potential risks, which were mostly addressed by the Quex team. The primary recommendations were as follows:

    • Restrict internal flow logic to prohibit flow.consumer == address(this) to prevent unauthorized self-calls and manipulation of subscription funds.

    • Implement zero-address checks in the setOwner function to prevent permanent denial-of-service conditions through invalid ownership assignments.

    • Introduce per-consumer spending limits within the subscription model to mitigate griefing attacks via request spamming and prevent excessive balance reservation.

    • Ensure that ETH transfers to relayers in fulfillRequest and pushData are followed by proper return value checks to prevent silent failures and potential fund loss.

3. Test Approach and Methodology

Halborn employed a combination of manual, semi-automated, and automated security testing to optimize efficiency, timeliness, practicality, and accuracy within the scope of this assessment. Manual testing was essential for uncovering logical, procedural, and implementation flaws, while automated techniques enhanced code coverage and quickly identified deviations from best security practices. The following phases and tools were utilized throughout the assessment:

    • Research into the architecture and purpose of the smart contracts.

    • Manual review and walkthrough of the smart contracts' code.

    • Manual assessment of key Solidity variables and functions to identify potential vulnerability classes.

    • Manual testing using custom scripts.

    • Static security analysis of the scoped contracts and imported functions using Slither.

    • Local deployment and testing with Foundry & Hardhat.


4. RISK METHODOLOGY

Every vulnerability and issue observed by Halborn is ranked based on two sets of Metrics and a Severity Coefficient. This system is inspired by the industry standard Common Vulnerability Scoring System.
The two Metric sets are: Exploitability and Impact. Exploitability captures the ease and technical means by which vulnerabilities can be exploited and Impact describes the consequences of a successful exploit.
The Severity Coefficients is designed to further refine the accuracy of the ranking with two factors: Reversibility and Scope. These capture the impact of the vulnerability on the environment as well as the number of users and smart contracts affected.
The final score is a value between 0-10 rounded up to 1 decimal place and 10 corresponding to the highest security risk. This provides an objective and accurate rating of the severity of security vulnerabilities in smart contracts.
The system is designed to assist in identifying and prioritizing vulnerabilities based on their level of risk to address the most critical issues in a timely manner.

4.1 EXPLOITABILITY

Attack Origin (AO):
Captures whether the attack requires compromising a specific account.
Attack Cost (AC):
Captures the cost of exploiting the vulnerability incurred by the attacker relative to sending a single transaction on the relevant blockchain. Includes but is not limited to financial and computational cost.
Attack Complexity (AX):
Describes the conditions beyond the attacker’s control that must exist in order to exploit the vulnerability. Includes but is not limited to macro situation, available third-party liquidity and regulatory challenges.
Metrics:
EXPLOITABILITY METRIC (mem_e)METRIC VALUENUMERICAL VALUE
Attack Origin (AO)Arbitrary (AO:A)
Specific (AO:S)
1
0.2
Attack Cost (AC)Low (AC:L)
Medium (AC:M)
High (AC:H)
1
0.67
0.33
Attack Complexity (AX)Low (AX:L)
Medium (AX:M)
High (AX:H)
1
0.67
0.33
Exploitability EE is calculated using the following formula:

E=meE = \prod m_e

4.2 IMPACT

Confidentiality (C):
Measures the impact to the confidentiality of the information resources managed by the contract due to a successfully exploited vulnerability. Confidentiality refers to limiting access to authorized users only.
Integrity (I):
Measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of data stored and/or processed on-chain. Integrity impact directly affecting Deposit or Yield records is excluded.
Availability (A):
Measures the impact to the availability of the impacted component resulting from a successfully exploited vulnerability. This metric refers to smart contract features and functionality, not state. Availability impact directly affecting Deposit or Yield is excluded.
Deposit (D):
Measures the impact to the deposits made to the contract by either users or owners.
Yield (Y):
Measures the impact to the yield generated by the contract for either users or owners.
Metrics:
IMPACT METRIC (mIm_I)METRIC VALUENUMERICAL VALUE
Confidentiality (C)None (I:N)
Low (I:L)
Medium (I:M)
High (I:H)
Critical (I:C)
0
0.25
0.5
0.75
1
Integrity (I)None (I:N)
Low (I:L)
Medium (I:M)
High (I:H)
Critical (I:C)
0
0.25
0.5
0.75
1
Availability (A)None (A:N)
Low (A:L)
Medium (A:M)
High (A:H)
Critical (A:C)
0
0.25
0.5
0.75
1
Deposit (D)None (D:N)
Low (D:L)
Medium (D:M)
High (D:H)
Critical (D:C)
0
0.25
0.5
0.75
1
Yield (Y)None (Y:N)
Low (Y:L)
Medium (Y:M)
High (Y:H)
Critical (Y:C)
0
0.25
0.5
0.75
1
Impact II is calculated using the following formula:

I=max(mI)+mImax(mI)4I = max(m_I) + \frac{\sum{m_I} - max(m_I)}{4}

4.3 SEVERITY COEFFICIENT

Reversibility (R):
Describes the share of the exploited vulnerability effects that can be reversed. For upgradeable contracts, assume the contract private key is available.
Scope (S):
Captures whether a vulnerability in one vulnerable contract impacts resources in other contracts.
Metrics:
SEVERITY COEFFICIENT (CC)COEFFICIENT VALUENUMERICAL VALUE
Reversibility (rr)None (R:N)
Partial (R:P)
Full (R:F)
1
0.5
0.25
Scope (ss)Changed (S:C)
Unchanged (S:U)
1.25
1
Severity Coefficient CC is obtained by the following product:

C=rsC = rs

The Vulnerability Severity Score SS is obtained by:

S=min(10,EIC10)S = min(10, EIC * 10)

The score is rounded up to 1 decimal places.
SeverityScore Value Range
Critical9 - 10
High7 - 8.9
Medium4.5 - 6.9
Low2 - 4.4
Informational0 - 1.9

5. SCOPE

REPOSITORY
(a) Repository: quex-v1-contracts
(b) Assessed Commit ID: 1211630
(c) Items in scope:
  • contracts/facets/monetary/DepositManagerFacet.sol
  • contracts/facets/actions/QuexActionFacet.sol
Out-of-Scope: Third party dependencies and economic attacks.
Remediation Commit ID:
Out-of-Scope: New features/implementations after the remediation commit IDs.

6. Assessment Summary & Findings Overview

Critical

0

High

1

Medium

1

Low

2

Informational

0

Security analysisRisk levelRemediation Date
Any External Account Can Manipulate Subscription FundsHighSolved
Unchecked Return Values in Relayer ETH TransfersMediumSolved
Potential Subscription Ownership DoSLowSolved
Subscription Balance Griefing via Request SpammingLowRisk Accepted - 06/25/2025

7. Findings & Tech Details

7.1 Any External Account Can Manipulate Subscription Funds

//

High

Description

The DepositManagerFacet allows any external account to bypass the quexOnly modifier and execute restricted functions on any subscription without authorization as this check can be circumvented because the protocol allows unrestricted creation of "flows" that can make the diamond contract call itself.

modifier quexOnly() {
    if (msg.sender != address(this)) {
        revert IQuexActionRegistry.OnlyCallableInternally();
    }
    _;
}

Attack Scenario:

  1. Malicious Flow Creation: An attacker calls FlowFacet.createFlow() with:

    • flow.consumer = address(diamond) (makes diamond call itself)

    • flow.callback = DepositManagerFacet.reserve.selector (targets restricted function)

  2. Self-Call Trigger: The attacker calls QuexActionFacet.pushData() which executes:

    • bytes memory payload = abi.encodeWithSelector(flow.callback, flowId, message.dataItem, IdType.FlowId);

    • (bool success,) = flow.consumer.call{gas: flow.gasLimit}(payload);

  3. Access Control Bypass: Since flow.consumer equals the diamond address, the diamond makes an external call to itself. Inside the restricted function, msg.sender == address(diamond), satisfying the quexOnly check.


All functions protected by quexOnly can be called without authorization:

  • reserve(uint256 subscriptionId, uint256 amount) - Lock subscription funds

  • release(uint256 subscriptionId, uint256 amount) - Unlock reserved funds

  • fulfill(uint256 subscriptionId, uint256 reservedFee, uint256 actualFee) - Manipulate balances


Proof of Concept

The following test function demonstrates the access control vulnerability:

   function test_Exploit_DrainSubscription() public {
        uint256 initialBalance = IDepositManager(address(diamond)).balance(subscriptionId);
        assertEq(initialBalance, 10 ether);

        DataItem memory fulfillData = DataItem({timestamp: block.timestamp, error: 10000 ether, value: bytes("")});
        OracleMessage memory fulfillMsg = OracleMessage({actionId: actionId, dataItem: fulfillData, relayer: attacker});
        ETHSignature memory fulfillSig = _signOracleMessage(fulfillMsg, TD_validInQuex_inOraclePool);

        vm.prank(attacker);
        testObject.pushData{value: quexFee}(fulfillMsg, fulfillSig, newFlowId, TD_validInQuex_inOraclePool.tdId);
    }

Output:

The following output shows that access control can be bypassed to call reserve by calling pushData with custom arguments and a malicious flow.



BVSS
Recommendation

Prevent flows where flow.consumer == address(this) to block self-calls.

function createFlow(Flow memory flow) external returns (uint256 flowId) {
    require(flow.consumer != address(this), "Self-calls forbidden");
    // ... rest of function
}

Remediation Comment

SOLVED: The recommended mitigation has been implemented.

Remediation Hash

7.2 Unchecked Return Values in Relayer ETH Transfers

//

Medium

Description

Both fulfillRequest and pushData functions in QuexActionFacet performs relayer ETH transfers without checking the return values of low-level call() operations.

payable(message.relayer).call{value: refund}("");

This can result in silent failures where transfers fail but transactions continue, leading to fund loss for relayer.


BVSS
Recommendation

Verify the return value of the call() and implement appropriate failure handling.

Remediation Comment

SOLVED: Funds are redirected to the treasury in case of failure.

Remediation Hash

7.3 Potential Subscription Ownership DoS

//

Low

Description

The setOwner function in DepositManagerFacet lacks validation to prevent assigning the subscription owner to address(0).


This omission creates a permanent denial of service condition that renders the subscription completely unusable.

BVSS
Recommendation

Implement validation in the setOwner function to prevent assigning the zero address.

Remediation Comment

SOLVED: The recommended mitigation has been implemented.

Remediation Hash

7.4 Subscription Balance Griefing via Request Spamming

//

Low

Description

The subscription model in DepositManagerFacet allows whitelisted consumers to lock arbitrary amounts of a subscription's balance through requests. A malicious consumer can create multiple requests to reserve nearly the entire subscription balance, then abandon the requests, effectively denying the owner access to their funds even after the consumer is removed.

function reserveFunds(uint256 subscriptionId, QuexActionStorage.Request memory req)
    private
    returns (uint256 requestId)
{
    uint256 totalFee = req.quexFee + req.maxRelayerRefund + req.oraclePoolFee;
    DepositManagerFacet(address(this)).reserve(subscriptionId, totalFee);  
}

BVSS
Recommendation

It is recommended to add spending limits per consumer.

Remediation Comment

RISK ACCEPTED: The Quex team accepted the risk related to this finding with following comment - "This behavior is intentional and aligns with the design of the subscription model. Granting a consumer access to a subscription is a form of delegated trust. The subscription owner is expected to carefully evaluate whom they grant consumer rights..."

8. Automated Testing

Halborn employed automated testing techniques to improve coverage of specific areas within the smart contracts under review. One of the primary tools used was Slither, a static analysis framework for Solidity. After successfully compiling the smart contracts in the repository into their ABI and binary formats, Slither was executed against the contracts. This tool performs static verification of mathematical relationships between Solidity variables to identify invalid or inconsistent usage of the contracts' APIs throughout the entire codebase.


The security team conducted a comprehensive review of the findings generated by Slither. No significant issues were identified, as the findings were determined to be false positives.



Halborn strongly recommends conducting a follow-up assessment of the project either within six months or immediately following any material changes to the codebase, whichever comes first. This approach is crucial for maintaining the project’s integrity and addressing potential vulnerabilities introduced by code modifications.

© Halborn 2025. All rights reserved.