CVE-2021-4300 (published 2023-01-04T22:15Z)

A vulnerability has been found in ghostlander Halcyon and classified as critical. Affected by this vulnerability is the function CBlock::AddToBlockIndex of the file src/main.cpp of the component Block Verification. The manipulation leads to improper access controls. The attack can be launched remotely. Upgrading to version 1.1.1.0-hal is able to address this issue. The identifier of the patch is 0675b25ae9cc10b5fdc8ea3a32c642979762d45e. It is recommended to upgrade the affected component. The identifier VDB-217417 was assigned to this vulnerability.

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: NVD-CWE-Other
cwe_name: Other
description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/ghostlander/Halcyon
commit_details: repo_url: https://github.com/ghostlander/Halcyon
author: John Doering
author_date: 2021-09-15 07:49:34+00:00
msg: Block Verification Vulnerability Eliminated

Fixed a critical vulnerability allowing to accept a proof-of-work block
containing one or more coin stake transactions in addition to a valid
coin base. Output values of such coin stakes were out of control.
repository: repo_url: https://github.com/ghostlander/Halcyon
repo_name: ghostlander/Halcyon
description: The Halcyon Project
repo_language: C
owner: ghostlander
file_changes: filename: main.cpp
change_type: ModificationType.MODIFY
diff: @@ -2099,6 +2099,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u
 
 
 bool CBlock::CheckBlock() const {
+    uint i;
 
     // These are checks that are independent of context
     // that can be verified before saving an orphan block.
@@ -2107,17 +2108,6 @@ bool CBlock::CheckBlock() const {
     if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
         return DoS(100, error("CheckBlock() : size limits failed"));
 
-    if(IsProofOfWork()) {
-
-        /* Proof-of-work verification against target */
-        if(!CheckProofOfWork(GetHashPoW(), nBits))
-          return(DoS(50, error("CheckBlock() : proof-of-work verification failed")));
-
-        /* Proof-of-work block signature verification */
-        if(!CheckBlockSignature())
-          return(DoS(100, error("CheckBlock() : bad proof-of-work block signature")));
-    }
-
     // Check timestamp
     if (GetBlockTime() > FutureDrift(GetAdjustedTime()))
         return error("CheckBlock() : block timestamp too far in the future");
@@ -2125,16 +2115,17 @@ bool CBlock::CheckBlock() const {
     // First transaction must be coinbase, the rest must not be
     if (vtx.empty() || !vtx[0].IsCoinBase())
         return DoS(100, error("CheckBlock() : first tx is not coinbase"));
-    for (unsigned int i = 1; i < vtx.size(); i++)
-        if (vtx[i].IsCoinBase())
-            return DoS(100, error("CheckBlock() : more than one coinbase"));
+    for(i = 1; i < vtx.size(); i++) {
+        if(vtx[i].IsCoinBase())
+          return(DoS(100, error("CheckBlock() : more than one coin base")));
+    }
 
     // Check coinbase timestamp
     if (GetBlockTime() > FutureDrift((int64_t)vtx[0].nTime))
         return DoS(50, error("CheckBlock() : coinbase timestamp is too early"));
 
-    if (IsProofOfStake())
-    {
+    if(IsProofOfStake()) {
+
         // Coinbase output should be empty if proof-of-stake block
         if (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty())
             return DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block"));
@@ -2142,9 +2133,10 @@ bool CBlock::CheckBlock() const {
         // Second transaction must be coinstake, the rest must not be
         if (vtx.empty() || !vtx[1].IsCoinStake())
             return DoS(100, error("CheckBlock() : second tx is not coinstake"));
-        for (unsigned int i = 2; i < vtx.size(); i++)
-            if (vtx[i].IsCoinStake())
-                return DoS(100, error("CheckBlock() : more than one coinstake"));
+        for(i = 2; i < vtx.size(); i++) {
+            if(vtx[i].IsCoinStake())
+              return(DoS(100, error("CheckBlock() : more than one coin stake")));
+        }
 
         // Check coinstake timestamp
         if (!CheckCoinStakeTimestamp(GetBlockTime(), (int64_t)vtx[1].nTime))
@@ -2153,6 +2145,22 @@ bool CBlock::CheckBlock() const {
         // NovaCoin: check proof-of-stake block signature
         if(!CheckBlockSignature())
             return DoS(100, error("CheckBlock() : bad proof-of-stake block signature"));
+
+    } else {
+
+        /* No coin stakes in PoW blocks */
+        for(i = 1; i < vtx.size(); i++) {
+            if(vtx[i].IsCoinStake() && (vtx[i].nTime > 1626307200))
+              return(DoS(100, error("CheckBlock() : rogue coin stake")));
+        }
+
+        /* Proof-of-work verification against target */
+        if(!CheckProofOfWork(GetHashPoW(), nBits))
+          return(DoS(50, error("CheckBlock() : proof-of-work verification failed")));
+
+        /* Proof-of-work block signature verification */
+        if(!CheckBlockSignature())
+          return(DoS(100, error("CheckBlock() : bad proof-of-work block signature")));
     }
 
     // Check transactions

programming_language: C++