Prepared by:
HALBORN
Last Updated 03/28/2024
Date of Engagement by: January 25th, 2024 - February 26th, 2024
100% of all REPORTED Findings have been addressed
All findings
7
Critical
0
High
0
Medium
1
Low
2
Informational
4
The CoreDAO team
engaged Halborn to conduct a security assessment on their smart contracts beginning on 01/25/2024 and ending on 02/26/2024. The security assessment was scoped to the smart contracts provided in the GitHub repository. Commit hashes and further details can be found in the Scope section of this report.
Halborn was provided 4 weeks for the engagement and assigned 1 full-time security engineer to review the security of the smart contracts in scope. The engineer is a blockchain and smart contract security experts 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.
Ensure that smart contract functionality operates as intended.
In summary, Halborn identified some security that were successfully addressed by the CoreDAO team.
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.
Static Analysis of security for scoped contract, and imported functions (slither
).
Testnet deployment (Foundry
).
External libraries and financial-related attacks.
New features/implementations after/with the remediation commit IDs.
EXPLOITABILIY METRIC () | METRIC VALUE | NUMERICAL 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 |
IMPACT METRIC () | METRIC VALUE | NUMERICAL 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 |
SEVERITY COEFFICIENT () | COEFFICIENT VALUE | NUMERICAL VALUE |
---|---|---|
Reversibility () | None (R:N) Partial (R:P) Full (R:F) | 1 0.5 0.25 |
Scope () | Changed (S:C) Unchanged (S:U) | 1.25 1 |
Severity | Score Value Range |
---|---|
Critical | 9 - 10 |
High | 7 - 8.9 |
Medium | 4.5 - 6.9 |
Low | 2 - 4.4 |
Informational | 0 - 1.9 |
Critical
0
High
0
Medium
1
Low
2
Informational
4
Security analysis | Risk level | Remediation Date |
---|---|---|
Incompatibility with Multi-Signature Wallets Due to .transfer Usage | Medium | Solved - 03/01/2024 |
Missing init functions on the upgradeable contracts | Low | Solved - 03/01/2024 |
Implementations Can Be Initialized | Low | Solved - 03/01/2024 |
Redundant "Instant Router" Variable Not Utilized in Contract | Informational | Solved - 03/01/2024 |
Finalization Parameter Not Configurable in Contract Initialization | Informational | Solved - 03/01/2024 |
Unsafe ERC20 operation(s) | Informational | Acknowledged - 03/01/2024 |
Inconsistent Initialization of Ownable in BitcoinRelayLogic Contract | Informational | Solved - 03/01/2024 |
// Medium
The smart contract functions slashIdleLocker
, slashThiefLocker
, and liquidateLocker
use Solidity's .transfer
method to send Ether (or native tokens) to recipients. While .transfer
is designed to prevent reentrancy attacks by limiting the gas sent to 2300, it poses significant limitations, particularly when interacting with contracts that require more gas to execute their fallback functions, such as multi-signature wallets. This limitation prevents multi-signature wallets from being used as recipients in these transactions, as they typically require more gas to accept and process incoming transfers.
function slashIdleLocker(
address _lockerTargetAddress,
uint _rewardAmount,
address _rewardRecipient,
uint _amount,
address _recipient
) external override nonReentrant whenNotPaused returns (bool) {
require(
_msgSender() == ccBurnRouter,
"Lockers: message sender is not ccBurn"
);
uint equivalentNativeToken = LockersLib.slashIdleLocker(
lockersMapping[_lockerTargetAddress],
libConstants,
libParams,
_rewardAmount,
_amount
);
// Transfers TNT to user
payable(_recipient).transfer(equivalentNativeToken*_amount/(_amount + _rewardAmount));
// Transfers TNT to slasher
uint rewardAmountInNativeToken = equivalentNativeToken - (equivalentNativeToken*_amount/(_amount + _rewardAmount));
payable(_rewardRecipient).transfer(rewardAmountInNativeToken);
emit LockerSlashed(
_lockerTargetAddress,
rewardAmountInNativeToken,
_rewardRecipient,
_amount,
_recipient,
equivalentNativeToken,
block.timestamp,
true
);
return true;
}
Replace the .transfer
method with Address.sendValue
, a function provided by OpenZeppelin's Address
utility library. sendValue
safely sends Ether while providing all available gas (minus a stipend for the transaction itself), thus accommodating contracts that consume more gas in their fallback functions.
SOLVED : The CoreDAO team solved the issue by using sendValue
function.
// Low
The contracts, which are designed to be upgradeable using the UUPS pattern, seem to be missing proper initialization functions. The initial setup for a UUPS upgradeable contract is crucial to ensure that the contract can be safely upgraded in the future without losing state or introducing vulnerabilities.
Code Location : https://github.com/coredao-org/btcwrap-contracts/blob/86a3b4f2d0569e1bd520a88356c31076a584ec8d/contracts/common/relay/BitcoinRelayLogic.sol#L11
To address this concern, ensure that all base contracts are properly initialized in the initialize
function.
SOLVED : The CoreDAO team solved the issue by calling the init
function.
// Low
The contracts are upgradable, inheriting from the Initializable
contract. However, the current implementations are missing the _disableInitializers()
function call in the constructors. Thus, an attacker can initialize the implementation. Usually, the initialized implementation has no direct impact on the proxy itself; however, it can be exploited in a phishing attack. In rare cases, the implementation might be mutable and may have an impact on the proxy.
It is recommended to call [_disableInitializers](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/utils/Initializable.sol#L145) within the contract's constructor to prevent the implementation from being initialized.
SOLVED : The CoreDAO team solved the issue by using disableInitializers
.
// Informational
Within the CcTransferRouterLogic
contract, an "instant router" variable setter (setInstantRouter
) is defined but appears to be redundant, as there is no subsequent utilization or reference to the "instant router" variable within the contract logic. This redundancy not only bloats the contract code but also introduces potential confusion regarding the contract's intended functionality and architecture.
The presence of unused variables and functions can obscure the contract's intended logic and make the codebase harder to maintain and understand.
Code Location : https://github.com/coredao-org/btcwrap-contracts/blob/main/contracts/routers/CcTransferRouterLogic.sol#L99
/// @notice Setter for instant router
/// @dev Only owner can call this
/// @param _instantRouter Address of the instant router contract
function setInstantRouter(address _instantRouter) external override nonZeroAddress(_instantRouter) onlyOwner {
_setInstantRouter(_instantRouter);
}
Conduct a thorough review of the contract code to identify and remove unused variables, functions, and logic paths. Specifically, evaluate the necessity of the "instant router" variable and its associated setter function. If confirmed as redundant, remove the setInstantRouter
function and any related code.
SOLVED : The CoreDAO team solved the issue by deleting redundant function/variable.
// Informational
The BitcoinRelayLogic
contract includes functionality to set the finalizationParameter
, which determines the number of confirmations required for a transaction to be considered finalized. While a setter function exists for adjusting this parameter post-deployment, the initial value of the finalizationParameter
is hardcoded to 3
during contract initialization. This design limits the contract's flexibility and adaptability at deployment, as it does not allow for the initial configuration of the finalization parameter.
Modify the initialize
function to accept the finalizationParameter
as an input argument. This adjustment will enable the contract deployer to set an appropriate initial value for the finalization parameter based on specific needs and conditions.
SOLVED : The CoreDAO team solved the issue by setting a parameter on the constructor.
// Informational
Currently, the contracts do not adequately handle atypical ERC20 tokens on the staking. According to the ERC20
specification, tokens should return "false" when a transfer fails, but it does not guarantee that the function will revert. This discrepancy could potentially lead to silent failures, making it difficult to detect issues when they occur.
For instance, from the below, It can be seen that [LDO](https://etherscan.io/token/0x5a98fcbea516cf06857215779fd812ca3bef1b32#code) does not revert If the user balance is not enough for the transfer.
Note : Even if It does not pose risk for CoreBTC, It is recommended to use safeApprove & safeTransfer/safeTransferFrom.
Consider using safeApprove - safeTransfer - safeTransferFrom from Openzeppelin.
ACKNOWLEDGED : The CoreDAO team acknowledged the issue.
// Informational
The BitcoinRelayLogic
contract, which implements Ownable2StepUpgradeable
among other functionalities, incorrectly uses OwnableUpgradeable.__Ownable_init()
for initialization instead of the expected Ownable2StepUpgradeable.__Ownable2Step_init()
.
/// @notice Gives a starting point for the relay
/// @param _height The starting height
/// @param _btcLightClient BTC light cient address
function initialize(
uint256 _height,
address _btcLightClient
) public initializer {
OwnableUpgradeable.__Ownable_init();
ReentrancyGuardUpgradeable.__ReentrancyGuard_init();
PausableUpgradeable.__Pausable_init();
// Relay parameters
btcLightClient = _btcLightClient;
_setFinalizationParameter(3);
initialHeight = _height;
}
Update the initialize
function within the BitcoinRelayLogic
contract to use the correct initialization method for Ownable2StepUpgradeable
. Replace OwnableUpgradeable.__Ownable_init()
with Ownable2StepUpgradeable.__Ownable2Step_init()
to ensure that all intended functionalities and security enhancements are correctly initialized and available.
SOLVED : The CoreDAO team solved the issue by calling Ownable2Step_init()
function.
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.
All issues identified by Slither were proved to be false positives or have been added to the issue list in this report.
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.
// Download the full report
* Use Google Chrome for best results
** Check "Background Graphics" in the print settings if needed