Halborn Logo

NFT Invoice - Truffles


Prepared by:

Halborn Logo

HALBORN

Last Updated 04/26/2024

Date of Engagement by: July 24th, 2023 - July 31st, 2023

Summary

75% of all REPORTED Findings have been addressed

All findings

8

Critical

0

High

0

Medium

0

Low

2

Informational

6


1. INTRODUCTION

\client is a platform that enables sellers to generate invoices and buyers to settle them via their internal payment partners. The invoices generated on the platform are minted as Non-Fungible Tokens (NFTs) for increased transparency, while preserving the privacy of both buyers and sellers by keeping their information confidential.

\client engaged Halborn to conduct a security assessment on their smart contracts beginning on 2023-07-24 and ending on 2023-07-31. The security assessment was scoped to the smart contracts provided in the truffles-nft-invoice GitHub repository. Commit hashes and further details can be found in the Scope section of this report.

2. ASSESSMENT SUMMARY

The team at Halborn was provided 1.5 weeks for the engagement and assigned a full-time security engineer to verify the security of the smart contracts in scope. The security engineer is a blockchain and smart contract security expert with advanced penetration testing and smart contract hacking skills, and deep knowledge of multiple blockchain protocols.

The purpose of the assessment is to:

    • Identify potential security issues within the smart contracts

    • Verify whether the smart contracts work as expected

In summary, Halborn did not identify any significant issues; however, some recommendations were given to reduce the likelihood and impact of risks, which were successfully addressed by \client.

3. TEST APPROACH & METHODOLOGY

Halborn performed a combination of manual and automated security testing to balance efficiency, timeliness, practicality, and accuracy in regard to the scope of this assessment. While manual testing is recommended to uncover flaws in logic, process, and implementation; automated testing techniques help enhance coverage of the code and can quickly identify items that do not follow the security best practices. The following phases and associated tools were used during the assessment:

    • Research into architecture and purpose

    • Smart contract manual code review and walkthrough

    • Graphing out functionality and contract logic/connectivity/functions (solgraph)

    • Manual assessment of use and safety for the critical Solidity variables and functions in scope to identify any arithmetic related vulnerability classes

    • Manual testing by custom scripts

    • Scanning of solidity files for vulnerabilities, security hot-spots or bugs. (MythX)

    • Static Analysis of security for scoped contract, and imported functions. (Slither)

    • Testnet deployment (Foundry)

Code repositories:

    1. Truffles NFT Invoice

    2. Repository: contracts

    3. Commit ID: 2d1a6334139ed9d6c60ff44e16c5a4198ebab737

    4. Branch: main

    5. Smart contracts in scope:

      1. contracts/Truffles.sol

      2. contracts/Authorizable.sol

    6. contracts/Truffles.sol

    7. contracts/Authorizable.sol

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:
EXPLOITABILIY 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

Out-of-Scope: New features/implementations after the remediation commit IDs.

6. Assessment Summary & Findings Overview

Critical

0

High

0

Medium

0

Low

2

Informational

6

Security analysisRisk levelRemediation Date
LACK OF THE TWO STEP OWNERSHIP TRANSFER PATTERNLowSolved - 08/04/2023
MISSING ZERO ADDRESS CHECKLowSolved - 08/04/2023
INVOICE TYPE CHECK MISSINGInformationalSolved - 08/04/2023
LACK OF REENTRANCYGUARDInformational-
LACK OF REENTRANCYGUARDInformational-
REDUNDANT CHECK IN THE REMOVEAUTHORIZED FUNCTIONInformationalSolved - 08/04/2023
CONTRACT PAUSE FEATURE MISSINGInformationalSolved - 08/07/2023
FLOATING PRAGMAInformationalSolved - 08/04/2023

7. Findings & Tech Details

7.1 LACK OF THE TWO STEP OWNERSHIP TRANSFER PATTERN

// Low

Description

The Authorizable contract is inherited by the Truffles contract and implements the Ownable pattern. However, the assessment revealed that the solution does not support the two-step-ownership-transfer pattern. The ownership transfer might be accidentally set to an inactive EOA account. In the case of account hijacking, all functionalities get under permanent control of the attacker.

Code Location

contracts/Authorizable.sol

contract Authorizable is Ownable {

mapping(address => bool) private authorized;
modifier onlyAuthorized() {
    require(authorized[msg.sender] ||  owner() == msg.sender, "Not authorized");
        _;
}

contracts/Truffles.sol

contract TRUFFLES is ERC721, ERC721Enumerable, ERC721URIStorage, Authorizable {
BVSS
Recommendation

SOLVED: The \client team solved this finding in commit fc36014: the Ownable contract was replaced with the Ownable2Step.sol from the OpenZeppelin library within the Authorizable contract to establish a secure approach for conducting two-step ownership transfers.

7.2 MISSING ZERO ADDRESS CHECK

// Low

Description

The functions addEligibleHolder, removeEligibleHolder and isEligibleHolder do not perform verification to ensure that no addresses provided as parameters are the zero addresses. Consequently, there is a risk of accidentally setting an eligible holder address to the zero address, leading to unintended behavior or potential vulnerabilities in the future.

Code Location

contracts/Truffles.sol

function addEligibleHolder(address _org) public onlyAuthorized {
    s_eligibleHolders[_org] = true;
}

/// @notice remove eligible holders for NFT
/// @dev remove eligible address to eligibleHolders maping : onlyAuthorized
/// @param _org address of already eligible holder
function removeEligibleHolder(address _org) public onlyAuthorized {
    s_eligibleHolders[_org] = false;
}

contracts/Truffles.sol

function isEligibleHolder(address _holder)
    public
    view
    returns (bool _isEligibleHolder)
{
    return (s_eligibleHolders[_holder]);
}
BVSS
Recommendation

SOLVED: The \client team solved this finding in commit fc36014: the function addEligibleHolder was updated with a validation step to ensure that the provided address is not the zero address before making modifications to the headlineholders' eligibility. Similarly, the removeEligibleHolder function now includes a validation check to confirm that the given address corresponds to an existing holder.

7.3 INVOICE TYPE CHECK MISSING

// Informational

Description

The settlePrivateInvoice function allows authorized addresses to mark the corresponding NFT of a private invoice as settled when the full amount of the invoice is paid. However, it lacks a check to verify that the provided NFT is of the private invoice type. Consequently, both private and public invoices could be settled indifferently. If the provided NFT ID corresponds to a public invoice, it is added to the s_isPrivateInvoicePaid mapping, even though its type does not match, leading to data inconsistency.

In addition, private invoices do not store the amount for the NFT unlike public ones, so the function also does not have a proper check to verify that the full amount of the invoice is paid.

Code Location

contracts/Truffles.sol

function mintPrivateInvoice(
    address to,
    uint256 tokenId,
    string memory uri
) public onlyAuthorized {
    _safeMint(to, tokenId);
    _setTokenURI(tokenId, uri);
    s_nftType[tokenId] = NftType.PrivateInvoice;
}

contracts/Truffles.sol

function settlePrivateInvoice(uint256 _nftID) public onlyAuthorized {
    require(_exists(_nftID), "NFT not minted");
    s_isPrivateInvoicePaid[_nftID] = true;

}
BVSS
Recommendation

SOLVED: The \client team solved this finding in commit fc36014: A validation step was introduced to ensure that the provided NFT ID is that of a private invoice before modifying the s_isPrivateInvoicePaid mapping.

7.4 LACK OF REENTRANCYGUARD

// Informational

Description
Finding description placeholder
Score
Impact:
Likelihood:

7.5 LACK OF REENTRANCYGUARD

// Informational

Description
Finding description placeholder
Score
Impact:
Likelihood:

7.6 REDUNDANT CHECK IN THE REMOVEAUTHORIZED FUNCTION

// Informational

Description

In the Authorizable contract, the addAuthorized function allows the owner to add an address to the list of authorized addresses. It requires that the provided address is not the zero address, and it can only be executed by the contract owner because of the onlyOwner modifier.

The removeAuthorized function allows the owner to remove an address from the list of authorized addresses. It requires that the provided address is not the zero address and is different from the senders, which has to be the owner.

The checks performed in the latter function are redundant, since the addAuthorized function already prevents adding the zero address as an authorized address. Therefore, this check is unnecessary and result in extra gas overhead.

Code Location

contracts/Authorizable.sol

function addAuthorized(address _toAdd) onlyOwner public {
    require(_toAdd != address(0));
    authorized[_toAdd] = true;
}

contracts/Authorizable.sol

function removeAuthorized(address _toRemove) onlyOwner public {
    require(_toRemove != address(0));
    require(_toRemove != msg.sender);
    authorized[_toRemove] = false;
}
Score
Recommendation

SOLVED: The \client team solved this finding in commit fc36014: the redundant check was removed from the removeAuthorized function.

7.7 CONTRACT PAUSE FEATURE MISSING

// Informational

Description

It was identified that the Owner cannot pause the Truffles contract. In the case of a security incident, this means that the owner lacks the ability to halt the minting or settlement of Invoices, potentially leading to further complications.

Code Location

  • contracts/Truffles.sol
Score
Recommendation

SOLVED: The \client team solved this finding in commit 2e2a367: The contract now incorporates a pausing mechanism, which introduces two additional functions: pauseContract and unpauseContract. This enhancement is complemented by the inclusion of the whenNotPaused modifier within several functions, including mintPrivateInvoice, settlePrivateInvoice, mintPublicInvoice, and _beforeTokenTransfer.

7.8 FLOATING PRAGMA

// Informational

Description

The Truffles contract uses the Solidity pragma ^0.8.9. It's essential to deploy the contract with the exact compiler version and flags that have undergone thorough testing. Locking the pragma to a specific version helps to ensure that contracts are not accidentally deployed using outdated compiler versions, which might introduce bugs negatively impacting the contract system, or excessively new pragma versions that haven't undergone extensive testing.

Code Location

contracts/Truffles.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
Score
Recommendation

SOLVED: The \client team solved this finding in commit fc36014: the pragma version has been locked and upgraded to match the version used in the Authorizable contract.

contracts/Truffles.sol

// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

8. Review Notes

In the manual testing phase, the following scenarios were simulated. The scenarios listed below were selected based on the severity of the vulnerabilities Halborn was testing the program for.

ACCESS CONTROL

Description

In the Truffles contract, the functions mintPublicInvoice and mintPrivateInvoice serve the purpose of minting non-fungible tokens for public and private invoices, respectively. It is crucial to note that these functions should only be accessible to the contract owner or authorized addresses, which have been added by the owner.

To ensure the integrity of the access control mechanism and prevent unauthorized minting by malicious users or any unauthorized addresses, rigorous testing has been conducted. The tests verify that the proper access controls are in place, guaranteeing that only the designated individuals can initiate the minting process.

By carrying out these tests and enforcing the necessary access controls, the Truffles contract maintains a secure and trusted environment for minting non-fungible tokens, safeguarding against potential security risks and unauthorized actions.

Results

No vulnerabilities were identified.

IMPROPER ADDITION AND DELETION OF AUTHORISED

Description

In the Authorizable contract, the addAuthorized and removeAuthorized functions enable the owner to include or exclude authorized addresses. These authorized addresses gain the privilege to call functions in the Truffles contract, enabling those addresses to extract invoices and assist in contract management.

To ensure the integrity of the system, testing was run to verify that the provided address cannot be set to zero.

Results

No vulnerabilities were identified.

MINTING INVOICE WITH SAME ID

Description

The Truffles contract incorporates the mintPublicInvoice and mintPrivateInvoice functions, both dedicated to minting non-fungible tokens for public and private invoices. These functions necessitate certain values as parameters, including the tokenId.

To uphold the uniqueness and integrity of the minted invoices, comprehensive tests have been conducted to verify that no two invoices can share the same tokenId value.

Results

No vulnerabilities were identified.

9. Automated Testing

STATIC ANALYSIS REPORT

Description

Halborn used automated testing techniques to enhance the coverage of certain areas of the smart contracts in scope. Among the tools used was Slither, a Solidity static analysis framework. After Halborn verified the smart contracts in the repository and was able to compile them correctly into their ABIs and binary format, Slither was run against the contracts. This tool can statically verify mathematical relationships between Solidity variables to detect invalid or inconsistent usage of the contracts' APIs across the entire code-base.

Results

Truffles

slither_truff2.png

Authorizable

slither_auth.png

All the issues flagged by Slither were found to be either false positives or issues already reported, like the reentrancy issue in HAL-04 .

AUTOMATED SECURITY SCAN

Description

Halborn used automated security scanners to assist with detection of well-known security issues and to identify low-hanging fruits on the targets for this engagement. Among the tools used was MythX, a security analysis service for Ethereum smart contracts. MythX performed a scan on the smart contracts and sent the compiled results to the analyzers in order to locate any vulnerabilities.

Results

mythx.png

The issue flagged by MythX was reported in HAL-07.

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.