Analog is a suite of interoperability protocols built on the Substrate SDK, designed to be a comprehensive solution for multi-chain and cross-chain development. In March 2024, Hacken completed a comprehensive Blockchain Protocol Audit of Analog’s Node, SDK, and Chain.
In this post, we aim to highlight the most interesting vulnerabilities, how they were discovered, their potential consequences, and the steps taken to mitigate them. For more information, see the full audit report.
Analog is the foundational, sovereign blockchain based on a Nominated Proof-of-Stake (NPoS). It is designed to address the challenges of cross-chain and multi-chain interactions in Web3, enabling developers and users to seamlessly leverage the benefits of any blockchain.
Overall, this is a Substrate project, with the scope consisting of Substrate pallets and additional node functionality in a separate crate. The pallets implement the core logic of the Timechain. The more complex part was the logic outside the pallets, as they implemented TSS (Threshold Signature Scheme) in their node.
In March 2024, Hacken completed a comprehensive Blockchain Protocol Audit of Analog’s Node, SDK, and Chain. The audit, which lasted a few months, focused on various aspects, including the system’s code quality, architecture quality, documentation, and overall security.
We have identified a total of 15 issues, categorized by severity:
All the issues were fixed, granting a maximum security score within the audited scope.
The audit report has been made public by Analog, in this post, we want to uncover the most interesting vulnerabilities, how they were discovered, what consequences, and mitigated.
The only high-severity issue was a vulnerability inside Runtime & Pallets that allowed the replay or DoS attack. Let’s review this vulnerability and the implemented fix in detail.
The submit_error
extrinsic within the tasks
pallet presented a significant security risk. This vulnerability originated from the absence of validation against the resubmission of identical error signatures. As a result, it opened up the potential for a replay attack, in which malicious actors repeatedly submit the same error signature. This repetitive submission undermined the system’s integrity and triggered a Denial of Service (DoS) by prematurely forcing tasks into a failure state.
Pre-audit, the signature verification process, did not utilize a nonce or any other method to guarantee the uniqueness of each submission. This absence of a uniqueness check allowed the same signature to be used repeatedly without being invalidated after its first use, as shown in the following code:
/// Submit Task Error
#[pallet::call_index(4)]
#[pallet::weight(T::WeightInfo::submit_error())]
pub fn submit_error(
origin: OriginFor<T>,
task_id: TaskId,
cycle: TaskCycle,
error: TaskError,
) -> DispatchResult {
ensure_signed(origin)?;
ensure!(Tasks::<T>::get(task_id).is_some(), Error::<T>::UnknownTask);
Self::validate_signature(
task_id,
cycle,
error.shard_id,
schnorr_evm::VerifyingKey::message_hash(error.msg.as_bytes()),
error.signature,
)?;
// ...
In the implementation, the signature is created based on public parameters such as the task ID, task cycle, error shard ID, and the error message, all visible when the extrinsic is called. Yet, this process does not incorporate a nonce or any other unique identifier to ensure the uniqueness of each transaction.
The absence of a nonce in the signature generation means that the signature remains valid for multiple transactions as long as the public parameters remain the same. In a blockchain environment, where transaction details are publicly visible on the chain, this aspect of transparency becomes a double-edged sword. While it upholds the blockchain’s principle of openness, it also means that any user can view the details of transactions, including those that call the submit_error
extrinsic.
As previously described, a malicious actor could exploit the replay attack vulnerability to repeatedly submit the same submit_error
extrinsic with a valid signature.
Each successful execution of the submit_error
extrinsic increments the TaskRetryCounter
for the specified task. This counter tracks the number of times an error has been submitted for a task.
In the runtime configuration, type MaxRetryCount = ConstU8<3>
specifies that the maximum retry count for a task was set to 3. This is a critical threshold value in the context of task execution.
When the TaskRetryCounter
reaches the maximum retry count of 3, as defined in the runtime configuration, the task’s state is automatically set to Failed
. This is enforced by the line TaskState::<T>::insert(task_id, TaskStatus::Failed { error: error.clone() });
in the submit_error
extrinsic.
Once the retry counter hits this limit, the task is marked as failed regardless of the actual validity or severity of the reported errors.
If this vulnerability had gone unnoticed, several potential consequences could have occurred:
To address the DoS vulnerability in the submit_error
extrinsic, Hacken auditors recommended incorporating the TaskRetryCounter
as part of the signature. This approach effectively uses the TaskRetryCounter
as a nonce, adding a unique element to each transaction and thereby mitigating the risk of replay attacks. Here’s a detailed recommendation:
Incorporate TaskRetryCounter into Signature:
TaskRetryCounter
for the task. Each time an error is reported and the submit_error
extrinsic is called, the TaskRetryCounter
is incremented. By including this incremented value in the signature, each submission becomes unique.TaskRetryCounter
part of the signature would differ. This effectively prevents the possibility of replaying the same transaction.The identified issue stems from the ROAST implementation in the tss crate, specifically the coordinator’s failure to verify that a node submitting a commitment is not concurrently in a pending state within an existing signing session. This lack of verification contradicts the fundamental logic of ROAST.
The root cause lies in the implementation of the on_commit
function for RoastCoordinator
, as depicted below:
fn on_commit(&mut self, peer: Identifier, commitment: SigningCommitments) {
self.commitments.insert(peer, commitment);
}
Upon receiving a ROAST request containing a commitment, the coordinator directly appends it to the map storing commitments for future sessions, neglecting to confirm whether the signer is currently involved in any ongoing signing session. As the coordinator proceeds to initiate a new session upon accumulating a sufficient number of commitments, the existing flaw permits a node already pending in one signing session to commit once again and engage in other sessions.
This deviation from the fundamental logic of the ROAST protocol is critical, as the protocol’s robustness relies on the premise that each signer is pending in at most one session. The protocol anticipates a finite and limited number of sessions to conclude. However, the identified flaw introduces a vulnerability, enabling a malicious node to secure a position in every session by consistently dispatching commitments to the coordinator. Subsequently, the malicious node can impede session termination by withholding its signature share.
This compromised scenario severely undermines the robustness of the ROAST protocol, providing an avenue for any malicious member to obstruct the process and obstruct the computation of a valid signature.
It is essential to underscore that the current risk level is relatively low, contingent upon the centralized nature of the chronicles, where all shard members are presumed non-malicious. However, as the project transitions to a decentralized model, this issue becomes more critical, representing a vulnerability susceptible to exploitation by any shard member. Such exploitation could compromise the performance and overall security of the system.
A variety of medium and low severity issues were identified, ranging from logical inconsistencies in the use of non-cryptographically secure crates to a lack of phase validation in task execution. All issues were addressed with specific recommendations, including:
Analog has resolved all the identified issues and implemented recommendations, enhancing its security posture as an innovative solution for omni-chain interoperability. We strongly believe that an audit by a credible and knowledgeable third party will enhance stakeholder confidence in Analog’s security measures and readiness for broader adoption in the Web3 ecosystem.
Analog’s proactive approach to addressing audit findings demonstrates its commitment to maintaining high-security standards and fostering a trustworthy environment for cross-chain application developers. The comprehensive audit by Hacken has thus played a crucial role in refining Analog’s security posture and ensuring its resilience against potential vulnerabilities.
Hacken’s audit of Analog revealed a well-architected and innovative system with high code quality and robust security measures. All of the identified issues were addressed by the Analog team. This audit not only strengthened the security of Analog’s platform but also highlighted areas for continuous improvement, ensuring the project’s long-term success and reliability.
Be the first to receive our latest company updates, Web3 security insights, and exclusive content curated for the blockchain enthusiasts.
Table of contents
Tell us about your project
3 min read
Case Studies
6 min read
Case Studies
7 min read
Case Studies