CVE-2017-20180: Insufficient Verification of Data Authenticity (CWE-345)
value: A vulnerability classified as critical has been found in Zerocoin libzerocoin. Affected is the function CoinSpend::CoinSpend of the file CoinSpend.cpp of the component Proof Handler. The manipulation leads to insufficient verification of data authenticity. Continious delivery with rolling releases is used by this product. Therefore, no version details of affected nor updated releases are available. The patch is identified as ce103a09ec079d0a0ed95475992348bed6e860de. It is recommended to apply a patch to fix this issue. VDB-222318 is the identifier assigned to this vulnerability.
CWE description: The product does not sufficiently verify the origin or authenticity of data, in a way that causes it to accept invalid data.
CVSS v3 base score: 7.5
Published: 2023-03-06T11:15Z
Repository: https://github.com/Zerocoin/libzerocoin
Language: C++
Files changed: CoinSpend.cpp, CoinSpend.h
Commit date: 2017-11-15 14:31:13 -0700
Commit message: Patch serial # exploit.

The serial # range needs to be checked or else the serial number is able to be incremented to a value that will create a valid proof, thus allowing multiple spends of a single mint.

Diff: diff --git a/CoinSpend.cpp b/CoinSpend.cpp
index 7187e85..ae1b31a 100644
--- a/CoinSpend.cpp
+++ b/CoinSpend.cpp
@@ -27,10 +27,15 @@ CoinSpend::CoinSpend(const Params* p, const PrivateCoin& coin,
 	// the coin and Accumulator provided.
 	if (!(witness.VerifyWitness(a, coin.getPublicCoin()))) {
 		throw ZerocoinException("Accumulator witness does not verify");
 	}
 
+	// The serial # needs to be within the specified range our else it can be incremented by the modulus and create another valid proof
+	if (!HasValidSerial()) {
+		throw ZerocoinException("Invalid serial # range");
+	}
+
 	// 1: Generate two separate commitments to the public coin (C), each under
 	// a different set of public parameters. We do this because the RSA accumulator
 	// has specific requirements for the commitment parameters that are not
 	// compatible with the group we use for the serial number proof.
 	// Specifically, our serial number proof requires the order of the commitment group
@@ -77,6 +82,11 @@ const uint256 CoinSpend::signatureHash(const SpendMetaData &m) const {
 	CHashWriter h(0,0);
 	h << m << serialCommitmentToCoinValue << accCommitmentToCoinValue << commitmentPoK << accumulatorPoK;
 	return h.GetHash();
 }
 
+bool CoinSpend::HasValidSerial() const
+{
+	return coinSerialNumber > 0 && coinSerialNumber < params->coinCommitmentGroup.groupOrder;
+}
+
 } /* namespace libzerocoin */
diff --git a/CoinSpend.h b/CoinSpend.h
index 51a4664..5bd52fa 100644
--- a/CoinSpend.h
+++ b/CoinSpend.h
@@ -74,10 +74,11 @@ public:
 	 *
 	 * @return the denomination
 	 */
 	const CoinDenomination getDenomination();
 
+	bool HasValidSerial() const;
 	bool Verify(const Accumulator& a, const SpendMetaData &metaData) const;
 
 	IMPLEMENT_SERIALIZE
 	(
 	    READWRITE(denomination);

Fixed code: 	// The serial # needs to be within the specified range our else it can be incremented by the modulus and create another valid proof
	if (!HasValidSerial()) {
		throw ZerocoinException("Invalid serial # range");
	}

bool CoinSpend::HasValidSerial() const
{
	return coinSerialNumber > 0 && coinSerialNumber < params->coinCommitmentGroup.groupOrder;
}

	bool HasValidSerial() const;