Shiboo Token - Simplified - Casper Association


Prepared by:

Halborn Logo

HALBORN

Last Updated Unknown date

Date of Engagement: August 20th, 2024 - August 21st, 2024

Summary

100% of all REPORTED Findings have been addressed

All findings

4

Critical

0

High

0

Medium

0

Low

0

Informational

4


1. Introduction

Casper engaged Halborn to conduct a security assessment of the Shiboo Token contract, as well as to investigate suspicious activity that developers had detected in Mainnet transactions, beginning April 11th, 2024 and ending April 26th, 2024.


Shiboo Token is a new digital asset with a total and max supply of 10 billion tokens. Within this allocation, 5% is securely held in the Treasury, while the remaining 95% is dedicated to the Initial Liquidity Pool. The project team is committed to providing initial liquidity, employing a 5% sell tax mechanism to enhance liquidity and potentially decrease the token supply through burns.

2. Assessment Summary

The team at Halborn assigned a full-time security engineer to verify the security of the smart contract. The security engineer is a blockchain and smart-contract security expert with advanced penetration testing, smart-contract hacking, and deep knowledge of multiple blockchain protocols.

The purpose of this assessment is to:

    • Ensure that smart contract functions operate as intended.

    • Identify potential security issues with the smart contracts.

In summary, Halborn identified some improvements to reduce the likelihood and impact of risks, which have been acknowledged by the Shiboo team. The main ones are the following: 

    • The token has mint functionality disabled, so all code related to it can be removed to save gas.

    • The transfer_from function should emit the TransferFrom event, not the Transfer one.

    • The access control of the burn function is not necessary since the caller address is compared to with an input argument entered by the user.

    • The initial_admin key is initialized twice, remove one of them to save gas.

3. Test Approach and Methodology

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

    • Research into architecture, purpose, and use of the platform.

    • Manual code read and walk through.

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

    • Cross contract call controls.

    • Architecture related logical controls.

    • Scanning of Rust files for vulnerabilities.(cargo audit)

    • Deployment to testnet through casper-client.

    • Comprehensive review of transactions through the CPSR live explorer.


4. Suspicious activity investigation

The Shiboo token assessment was contracted to Halborn on special terms. Casper communicated that they found suspicious activity in relation to the Shiboo token, so there was an urgent need to assess the code and the current situation. The Shiboo team was concerned about a transaction where it appeared that tax on sell was being bypassed and also a situation where a user was running a script every few minutes to sell.


After evaluating the source code of the Shiboo token, Halborn concluded that there were no security issues with the token itself, but after analyzing transactions on the Mainnet, it was found a lot of activity related to the Shiboo token and the Friendly Market, as this token is listed on it, so Halborn strongly recommended analyzing the source code of the current version of Friendly Market deployed on Mainnet to validate the transactions between the two contracts.


However, the Friendly Market team was not able to provide the source code of the current commit deployed on mainnet. At this point, the analysis of suspicious activity has been based on the transactions tracked in the explorer and the source code of the Shiboo token.


The following sections will explain the investigation that has been done on the transactions that were flagged by the Shiboo team, some other suspicious transactions found on the blockchain and the corresponding conclusions.


Transaction suspected of bypass tax on sell

The first transaction analyzed was suspected of evading the sales tax. The Shiboo token applies fees only when transferring to specific contracts, which seemed to be the concern.

Initial exploration suggested a simple token transfer, but further analysis showed that the transaction involved a Session Code, which operates like a script containing various contract calls. Although the full source code of this Session Code isn't available, the events logged indicate that the Friendly Market contract was used to execute a transaction.


Reviewing the transaction logs, the arguments suggested that it executed the Friendly Market entry point named swap_exact_tokens_for_cspr_supporting_fee_on_transfer_tokens. Analyzing these arguments and the events logged, the following conclusions were drawn:

    • The session code executed a transaction at the Friendly Market entry point.

    • The transfer tax applied by the Shiboo token was correctly deducted, confirming that the transfer tax mechanism is functioning as expected.



Without access to the Friendly Market source code, it is impossible to confirm if there are issues with the transactions since the logic of the functions applied is unknown. The overall functionality of the swapping process seems adequate and correct.


Set of transactions in which a user executes a sales script every few minutes

The Shiboo development team was also concerned about frequent transactions from a single user, who appeared to be extracting liquidity at regular intervals.


Similar to the previous case, these transactions involved Session Code executions. The arguments used in these transactions indicated that the user was interacting with the Friendly Market’s remove_liquidity_cspr entry point to burn LP tokens and retrieve the native CSPR token. The logs showed no evidence of malicious activity, though it was noted that an approval event occurred twice with the same details and an unusually high approval amount.


However, as mentioned above, without having access to the source code of the session code executed by the user and without knowing all the logic implemented by the Friendly Market functions, it cannot be stated that any malicious activity is occurring in such contracts since the frequency of execution cannot be evidence of malicious activity with the information available to us.


Other suspicious activity

As with other blockchain technologies, due to the significant amounts of money involved, blockchain environments are attractive targets for hackers. Since transactions are public, attempts to exploit or bypass security mechanisms can sometimes be observed. An example related to the Shiboo token involved a transaction where a user attempted to modify the fee_recipient address to their own.


Fortunately, the token code is robust enough to prevent such attacks, triggering an error that only allows the legitimate fee recipient to make such changes.


Conclusion

The investigation did not find any definitive evidence of security breaches or malicious activity in the analyzed transactions. However, due to the limitations in accessing the full source code of relevant contracts, some uncertainties remain. Further review of the Friendly Market’s code is recommended to fully ensure the integrity of the transactions.


5. 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.

5.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

5.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}

5.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

6. SCOPE

REPOSITORY
(a) Repository: cep18
(b) Assessed Commit ID: 277b5a1
(c) Items in scope:
  • cep18
Out-of-Scope: Third party dependencies, Economic attacks.
Out-of-Scope: New features/implementations after the remediation commit IDs.

7. Assessment Summary & Findings Overview

Critical

0

High

0

Medium

0

Low

0

Informational

4

Security analysisRisk levelRemediation Date
Unused functionalities could be removed to optimize gasInformationalAcknowledged
Useless access control in burn functionInformationalAcknowledged
Repeated key initialization during deploymentInformationalAcknowledged
Inappropriate event in transfer_from functionInformationalAcknowledged

8. Findings & Tech Details

8.1 Unused functionalities could be removed to optimize gas

//

Informational

Description

Since mint functionality is not implemented in this contract, all the related code like mint function, the entry point creation or the access control deployed by minter_list could be removed in order to save some gas.


This also applies to the change_security and sec_check functions. The access control provided by these functions is only used in the mint one. All other privileged functions of the contract such as update_fee, update_fee_recipient or update_trading_pair make use of the only_fee_recipient function for access control.

Code Location

The mint function from cep18/src/main.rs file:

pub extern "C" fn mint() {
    if 0 == read_from::<u8>(ENABLE_MINT_BURN) {
        revert(Cep18Error::MintBurnDisabled);
    }

    sec_check(vec![SecurityBadge::Admin, SecurityBadge::Minter]);

    let owner: Key = runtime::get_named_arg(OWNER);
    let amount: U256 = runtime::get_named_arg(AMOUNT);

    let balances_uref = get_balances_uref();
    let total_supply_uref = get_total_supply_uref();
    let new_balance = {
        let balance = read_balance_from(balances_uref, owner);
        balance
            .checked_add(amount)
            .ok_or(Cep18Error::Overflow)
            .unwrap_or_revert()
    };
    let new_total_supply = {
        let total_supply: U256 = read_total_supply_from(total_supply_uref);
        total_supply
            .checked_add(amount)
            .ok_or(Cep18Error::Overflow)
            .unwrap_or_revert()
    };
    write_balance_to(balances_uref, owner, new_balance);
    write_total_supply_to(total_supply_uref, new_total_supply);
    events::record_event_dictionary(Event::Mint(Mint {
        recipient: owner,
        amount,
    }))
}

Code snippet of init function from cep18/src/main.rs file:

    let admin_list: Option<Vec<Key>> =
        utils::get_optional_named_arg_with_user_errors(ADMIN_LIST, Cep18Error::InvalidAdminList);
    let minter_list: Option<Vec<Key>> =
        utils::get_optional_named_arg_with_user_errors(MINTER_LIST, Cep18Error::InvalidMinterList);

    init_events();

    if let Some(minter_list) = minter_list {
        for minter in minter_list {
            dictionary_put(
                security_badges_dict,
                &base64::encode(minter.to_bytes().unwrap_or_revert()),
                SecurityBadge::Minter,
            );
        }
    }

Code snippet of change_security function from cep18/src/main.rs file:

pub extern "C" fn change_security() {
    if 0 == read_from::<u8>(ENABLE_MINT_BURN) {
        revert(Cep18Error::MintBurnDisabled);
    }
    sec_check(vec![SecurityBadge::Admin]);
    let admin_list: Option<Vec<Key>> =
        utils::get_optional_named_arg_with_user_errors(ADMIN_LIST, Cep18Error::InvalidAdminList);
    let minter_list: Option<Vec<Key>> =
        utils::get_optional_named_arg_with_user_errors(MINTER_LIST, Cep18Error::InvalidMinterList);
    let none_list: Option<Vec<Key>> =
        utils::get_optional_named_arg_with_user_errors(NONE_LIST, Cep18Error::InvalidNoneList);

    let mut badge_map: BTreeMap<Key, SecurityBadge> = BTreeMap::new();
    if let Some(minter_list) = minter_list {
        for account_key in minter_list {
            badge_map.insert(account_key, SecurityBadge::Minter);
        }
    }
    if let Some(admin_list) = admin_list {
        for account_key in admin_list {
            badge_map.insert(account_key, SecurityBadge::Admin);
        }
    }
    if let Some(none_list) = none_list {
        for account_key in none_list {
            badge_map.insert(account_key, SecurityBadge::None);
        }
    }

Code of sec_check function from cep18/src/utils.rs file:

pub fn sec_check(allowed_badge_list: Vec<SecurityBadge>) {
    let caller = get_immediate_caller_address()
        .unwrap_or_revert()
        .to_bytes()
        .unwrap_or_revert();
    if !allowed_badge_list.contains(
        &dictionary_get::<SecurityBadge>(get_uref(SECURITY_BADGES), &base64::encode(caller))
            .unwrap_or_revert()
            .unwrap_or_revert_with(Cep18Error::InsufficientRights),
    ) {
        revert(Cep18Error::InsufficientRights)
    }
}
BVSS
Recommendation

It is recommended to remove unused code to save some gas in the deployment.

Remediation Plan

ACKNOWLEDGED: The Shiboo team acknowledged this finding.

8.2 Useless access control in burn function

//

Informational

Description

The access control of the burn function is useless since the OWNER parameter used in the condition is one of the input arguments, so it can be any value chosen by the user.

Code Location

The burn function from cep18/src/main.rs file:

pub extern "C" fn burn() {
    if 0 == read_from::<u8>(ENABLE_MINT_BURN) {
        revert(Cep18Error::MintBurnDisabled);
    }

    let owner: Key = runtime::get_named_arg(OWNER);

    if owner != get_immediate_caller_address().unwrap_or_revert() {
        revert(Cep18Error::InvalidBurnTarget);
    }

    let amount: U256 = runtime::get_named_arg(AMOUNT);
    let balances_uref = get_balances_uref();
BVSS
Recommendation

It is recommended to delete useless access control in order to save some gas.

Remediation Plan

ACKNOWLEDGED: The Shiboo team acknowledged this finding.

8.3 Repeated key initialization during deployment

//

Informational

Description

During the execution of the install_contract function, the initial_admin variable is initialized from an input argument (if it exists) or it takes the caller's hash. After that, the init entry point is called with some arguments, being initial_admin one of them.


However, this variable is initialized again during the execution of the init function , taking the input argument of the entry point if it exists or the caller's hash, which in this case is the contract itself.


Since the first initialization is performed before calling the init function, entering the variable as input argument, and because the init function is only executed once, the second initialization of the initial_admin variable is not necessary.

Code Location

Code snippet install_contract function from cep18/src/main.rs file:

    // Call contract to initialize it
    let initial_admin: Key =
        utils::get_optional_named_arg_with_user_errors("initial_admin", Cep18Error::InvalidContext)
            .unwrap_or(get_caller().into());

    let mut init_args = runtime_args! {TOTAL_SUPPLY => total_supply, PACKAGE_HASH => package_hash, FEE_RECIPIENT => fee_recipient, FEE_BIPS => fee_bips, TRADING_PAIR => pair_contract, INITIAL_ADMIN => initial_admin};

    if let Some(admin_list) = admin_list {
        init_args.insert(ADMIN_LIST, admin_list).unwrap_or_revert();
    }
    if let Some(minter_list) = minter_list {
        init_args
            .insert(MINTER_LIST, minter_list)
            .unwrap_or_revert();
    }

    runtime::call_contract::<()>(contract_hash, INIT_ENTRY_POINT_NAME, init_args);

Code snippet init function from cep18/src/main.rs file:

pub extern "C" fn init() {
    if get_key(ALLOWANCES).is_some() {
        revert(Cep18Error::AlreadyInitialized);
    }
    let package_hash = get_named_arg::<Key>(PACKAGE_HASH);
    put_key(PACKAGE_HASH, package_hash);
    storage::new_dictionary(ALLOWANCES).unwrap_or_revert();
    let balances_uref = storage::new_dictionary(BALANCES).unwrap_or_revert();
    let initial_supply = runtime::get_named_arg(TOTAL_SUPPLY);
    let caller = get_caller();
    let initial_admin: Key =
        utils::get_optional_named_arg_with_user_errors("initial_admin", Cep18Error::InvalidContext)
            .unwrap_or(get_caller().into());

    write_balance_to(balances_uref, initial_admin, initial_supply);

BVSS
Recommendation

It is recommended to remove the second initialization of the initial_admin variable, in this case the argument is not going to be optional, so it is only necessary to assign it to the variable using the get_named_arg function.

Remediation Plan

ACKNOWLEDGED: The Shiboo team acknowledged this finding.

8.4 Inappropriate event in transfer_from function

//

Informational

Description

The event emitted in the transfer_from function is not the appropriate one. The event used is Transfer instead of TransferFrom.

Code Location

Code snippet install_contract function from cep18/src/main.rs file:

    let (amount_to_recipient, fee_amount) =
        transfer_balance(owner, recipient, amount).unwrap_or_revert();
    write_allowance_to(allowances_uref, owner, spender, new_spender_allowance);

    events::record_event_dictionary(Event::Transfer(Transfer {
        sender: owner,
        recipient,
        amount: amount_to_recipient,
    }));

    if fee_amount.gt(&0.into()) {
        events::record_event_dictionary(Event::Transfer(Transfer {
            sender: owner,
            recipient: read_fee_recipient_internal(),
            amount: fee_amount,
        }));
    }
}

Code snippet Event type from cep18/src/events.rs file:

pub enum Event {
    Mint(Mint),
    Burn(Burn),
    SetAllowance(SetAllowance),
    IncreaseAllowance(IncreaseAllowance),
    DecreaseAllowance(DecreaseAllowance),
    Transfer(Transfer),
    TransferFrom(TransferFrom),
    ChangeSecurity(ChangeSecurity),
}

BVSS
Recommendation

It is recommended to use the appropriate event type: TransferFrom instead of Transfer.

Remediation Plan

ACKNOWLEDGED: The Shiboo team acknowledged this finding.

9. Automated Testing

Halborn used automated security scanners to assist with detection of well-known security issues and vulnerabilities. Among the tools used was cargo audit, a security scanner for vulnerabilities reported to the RustSec Advisory Database. All vulnerabilities published in https://crates.io are stored in a repository named The RustSec Advisory Database. cargo audit is a human-readable version of the advisory database which performs a scanning on Cargo.lock. Security Detections are only in scope. To better assist the developers maintaining this code, the auditors are including the output with the dependencies tree, and this is included in the cargo audit output to better know the dependencies affected by unmaintained and vulnerable crates.


ID

Package

Description

RUSTSEC-2022-0093

ed25519-dalek

Version: 1.0.1

Double Public Key Signing Function Oracle Attack on ed25519-dalek
Upgrade to >=2

RUSTSEC-2022-0001

lmdb

Version: 0.8.0

Unmaintained

RUSTSEC-2022-0061

parity-wasm

Version: 0.42.2

Unmaintained

RUSTSEC-2022-0054

wee_alloc

Version: 0.4.5

Unmaintained

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.