Halborn Logo

Ninja Spin - Seascape


Prepared by:

Halborn Logo

HALBORN

Last Updated 04/26/2024

Date of Engagement by: September 2nd, 2022 - September 9th, 2022

Summary

100% of all REPORTED Findings have been addressed

All findings

7

Critical

0

High

1

Medium

1

Low

2

Informational

3


1. INTRODUCTION

Seascape engaged Halborn to conduct a security audit on their Ninja Spin smart contracts beginning on September 2nd, 2022 and ending on September 9th, 2022. The security assessment was scoped to some smart contracts provided in the GitHub repository blocklords/bigbang-smartcontracts.

2. AUDIT SUMMARY

The team at Halborn was provided a week for the engagement and assigned one full-time security engineer to audit 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 audit is to:

    • Ensure that smart contract functions operate as intended

    • Identify potential security issues with the smart contracts

In summary, Halborn identified some security risks that were addressed and acknowledged by the Seascape team.

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 audit. 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 audit:

    • 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 hotspots or bugs. (MythX)

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

    • Testnet deployment (Brownie, Remix IDE)

4. SCOPE

IN-SCOPE: The security assessment was scoped to the following smart contracts:

    • BigBangNFT.sol

    • BigBangFactory.sol

    • BigBangGame.sol

Located on the Commit ID: 4e6b040ec981df4f8ce9993bea75563ca5b9fa07

5. RISK METHODOLOGY

Vulnerabilities or issues observed by Halborn are ranked based on the risk assessment methodology by measuring the LIKELIHOOD of a security incident and the IMPACT should an incident occur. This framework works for communicating the characteristics and impacts of technology vulnerabilities. The quantitative model ensures repeatable and accurate measurement while enabling users to see the underlying vulnerability characteristics that were used to generate the Risk scores. For every vulnerability, a risk level will be calculated on a scale of 5 to 1 with 5 being the highest likelihood or impact.
RISK SCALE - LIKELIHOOD
  • 5 - Almost certain an incident will occur.
  • 4 - High probability of an incident occurring.
  • 3 - Potential of a security incident in the long term.
  • 2 - Low probability of an incident occurring.
  • 1 - Very unlikely issue will cause an incident.
RISK SCALE - IMPACT
  • 5 - May cause devastating and unrecoverable impact or loss.
  • 4 - May cause a significant level of impact or loss.
  • 3 - May cause a partial impact or loss to many.
  • 2 - May cause temporary impact or loss.
  • 1 - May cause minimal or un-noticeable impact.
The risk level is then calculated using a sum of these two values, creating a value of 10 to 1 with 10 being the highest level of security risk.
Critical
High
Medium
Low
Informational
  • 10 - CRITICAL
  • 9 - 8 - HIGH
  • 7 - 6 - MEDIUM
  • 5 - 4 - LOW
  • 3 - 1 - VERY LOW AND INFORMATIONAL

6. SCOPE

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

7. Assessment Summary & Findings Overview

Critical

0

High

1

Medium

1

Low

2

Informational

3

Impact x Likelihood

HAL-01

HAL-02

HAL-05

HAL-03

HAL-04

HAL-07

HAL-06

Security analysisRisk levelRemediation Date
CONTRACT ADMIN CAN REVOKE AND RENOUNCE HIMSELFHighSolved - 09/13/2022
CONTRACT DOES NOT ALLOW MINTING NFT WITH THE ID 0MediumSolved - 09/13/2022
NATIVE TOKEN FUNCTIONALITY COULD NOT BE USEDLowSolved - 09/13/2022
MISSING ZERO ADDRESS CHECKLowSolved - 09/13/2022
UNUSED PARAMETERSInformationalAcknowledged
PRAGMA VERSIONInformationalAcknowledged
UNSAFE MATH CALCULATIONSInformationalAcknowledged

8. Findings & Tech Details

8.1 CONTRACT ADMIN CAN REVOKE AND RENOUNCE HIMSELF

// High

Description

The Owner of the contract is usually the account that deploys the contract. In the BigBangNFTFactory.sol smart contract, Only Admin can perform some privileged actions such as setNft(), addAdmin(), addGenerator() etc., the addAdmin() function is used to add an Admin role, and the renounceAdmin function is used to renounce being an Admin. It was observed that admin could revoke his role via renounceAdmin(). If an admin is mistakenly renounced, administrative access will result in the contract having no admin, eliminating the ability to call privileged functions. In such a case, contracts would have to be redeployed.

Code Location

BigBangNFTFactory.sol

   function renounceAdmin() public virtual
   {
      renounceRole(DEFAULT_ADMIN_ROLE, msg.sender);
   }
  • Deploy a BigBangNFTFactory using the owner address.
  • Execute BigBangNFTFactory.renounceAdmin() function as using the owner address.
Score
Impact: 5
Likelihood: 3
Recommendation

SOLVED: Now the owner cannot renounce his role. This issue was fixed in commit ID 7d4d7a60ef10d499be2c672a6b623211ef288246

8.2 CONTRACT DOES NOT ALLOW MINTING NFT WITH THE ID 0

// Medium

Description

The BigBangNFT.sol contract constructor contains an increment of the counter variable, making the IDs begin on 1.

ScapeStore.sol

    constructor() public ERC721("BigBang NFT", "BB") {
        nftId.increment();
    }

\color{black} \color{white}

It is known that some collections start at the NFT ID 0.

Score
Impact: 3
Likelihood: 3
Recommendation

SOLVED: The Ninja Scape Game can now start minting NFTs at ID 0. This issue was fixed in commit ID 63f46b35467cd3865416b1dae2aa99ea4363cf63

8.3 NATIVE TOKEN FUNCTIONALITY COULD NOT BE USED

// Low

Description

In the _safeTransfer() function from BigBangGame.sol smart contract, two different types of transfers could be performed. if _token address sent as parameter is different from 0, the function will perform a token.transfer(). If _token is address(0), then the contract will send the native coin to the sender.

It is not possible to add address(0) to the list of allowed tokens, as the addresses are validating this parameter to be different from 0. This makes it impossible to operate with native tokens.

Code Location

BigBangGame.sol

constructor(address _token, address _nft, address _factory, address _verifier) public {
    require(_token != address(0), "BBGame: Token can't be zero address");
    require(_nft != address(0), "BBGame: Nft can't be zero address");
    require(_verifier != address(0), "BBGame: Verifier can't be zero address");

    BBNft     = _nft;
    BBFactory = _factory;
    verifier    = _verifier;

    changeAllowed[_token] = true;
    token[typeId]     = _token;
  }

BigBangGame.sol

  function addToken(address _token) public onlyOwner {
    require(_token != address(0), "BBGame: Token can't be zero address");
    require(!changeAllowed[_token], "BBGame: This token is exist");

    changeAllowed[_token] = true;
    token[++typeId] = _token;

    emit AddToken(_token, typeId, block.timestamp);
  }

BigBangGame.sol

  function _safeTransfer(address _token, address _to, uint256 _amount) internal {
    if (_token != address(0)) {
      IERC20 _rewardToken = IERC20(_token);

      uint256 _balance = _rewardToken.balanceOf(address(this));
      require(_amount <= _balance, "BBGame: Do not have enough token to reward");

      uint256 _beforBalance = _rewardToken.balanceOf(_to);
      _rewardToken.transfer(_to, _amount);

      require(_rewardToken.balanceOf(_to) == _beforBalance + _amount, "BBGame: Invalid transfer");
    } else {

      uint256 _balance = address(this).balance;
      require(_amount <= _balance, "BBGame: Do not have enough token to reward");

      payable(_to).transfer(_amount);
    }
  }

  // Accept native tokens.
  receive() external payable {
    //React to receiving ether
  }
Score
Impact: 2
Likelihood: 3
Recommendation

SOLVED: The SeaScape Team now allows the BigBangGame.sol contract to add the address(0) to specify that the native token could be used. This issue was fixed in commit ID 6fbb5845900735349c19c5e751991b9854d9a2a5

8.4 MISSING ZERO ADDRESS CHECK

// Low

Description

There is no validation of the addresses in the mint() and access control functions. Addresses should be validated and checked that are different from zero when necessary. This issue is present in all the smart contracts, in the constructors and functions that use addresses as parameters. These examples show how the factory could be set up with a wrong address and how mint could burn NFTs if _owner is address(0)

BigBangNFTFactory.sol

    constructor(address _nft) public {
        nft = BigBangNFT(_nft);
        _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

BigBangNFTFactory.sol

   function mint(address _owner, uint256 _quality, uint256 _image) public onlyGenerator returns(uint256) {
      require (_quality > 0 && _quality < 6, "NFT Factory: invalid quality");
      return nft.mint(_owner, _quality, _image);
   }
Score
Impact: 2
Likelihood: 3
Recommendation

SOLVED: The SeaScape Team now checks the address inputs to ensure they are non-zero. This issue has been fixed in commit ID [7d4d7a60ef10d499be2c672a6b623211ef288246](https://github.com/Seastarinteractive/moonscape-smartcontracts/tree/7d4d7a60ef10d499be2c672a6b623211ef288246

8.5 UNUSED PARAMETERS

// Informational

Description

There are functions whose parameters are never used in some smart contracts. These parameters have no effect on the code.

Code Location

  • operator, from, tokenId, data (BigBangGame.sol#190)
  • BBFactory (BigBangGame.sol#231)
Score
Impact: 2
Likelihood: 1
Recommendation

ACKNOWLEDGED: The SeaScape team acknowledged this finding.

8.6 PRAGMA VERSION

// Informational

Description

The BigBangGame smart contracts use the pragma version 0.6.7 which was released on May 4, 2020. latest pragma version 0.8.16 released on August 8, 2022, solves different issues. It is also noticeable that Solidity versions after 0.8.0 also implement default overflow protection on arithmetic operations.

Reference: Solidity Releases

Code Location

note: All Ninja Spin smart contracts implement same pragma version.

BigBangGame.sol

pragma solidity 0.6.7;
Score
Impact: 1
Likelihood: 2
Recommendation

ACKNOWLEDGED: The SeaScape team acknowledged this finding.

8.7 UNSAFE MATH CALCULATIONS

// Informational

Description

Testing revealed that calculations within the smart contract did not make use of a safe math library. While Solidity pragma > 0.8.0 reverts to overflows by default, the code was making use of an older compiler version; thus it is vulnerable to integer overflows and underflows.

Halborn could not find a path to exploit the integer overflows; however, there might be some extreme test cases where this can be exploited by an attacker.

Code Location

```{language=solidity caption="BigBangGame.sol" firstnumber=128 hlines=148} function goldChangeToken(uint256 _gold, uint256 _typeId, uint8 _v, bytes32 _r, bytes32 _s) external { require(_gold > 0, "BBGame: The exchange amount must greater than zero"); require(checkToken(_typeId), "BBGame: Do not have this token type");

uint256 chainId;   
assembly {
    chainId := chainid()
}

{
  bytes memory prefix     = "\x19Ethereum Signed Message:\n32";
  bytes32 message         = keccak256(abi.encodePacked(_gold, msg.sender, nonce[msg.sender], address(this), chainId));
  bytes32 hash            = keccak256(abi.encodePacked(prefix, message));
  address recover         = ecrecover(hash, _v, _r, _s);

  require(recover == verifier, "BBGame: Verification failed about goldChangeToken");
}

nonce[msg.sender]++;

uint256 _tokenAmount = _gold * MULTIPLIER / ratio;
_safeTransfer(token[_typeId], msg.sender, _tokenAmount);

emit GoldChangeToken(msg.sender, _typeId, _gold, _tokenAmount, block.timestamp);

} ```

Score
Impact: 1
Likelihood: 1
Recommendation

ACKNOWLEDGED: The SeaScape team acknowledged this finding.

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.

Slither results

slither1.pngslither2.pngslither3.pngslither4.pngslither5.pngslither6.pngslither7.pngslither8.png
  • No major issues found by Slither.

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.

MythX results

  • No issues found by MythX.

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.