CVE-2020-15130 (published 2020-07-30T15:15Z)

In SLPJS (npm package slpjs) before version 0.27.4, there is a vulnerability to false-positive validation outcomes for the NFT1 Child Genesis transaction type. A poorly implemented SLP wallet or opportunistic attacker could create a seemingly valid NFT1 child token without burning any of the NFT1 Group token type as is required by the NFT1 specification. This is fixed in version 0.27.4.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N)

Weakness information:
cwe_id: CWE-697
cwe_name: Incorrect Comparison
description: The product compares two entities in a security-relevant context, but the comparison is incorrect, which may lead to resultant weaknesses.
extended_description: 
            This Pillar covers several possibilities:
               
                  the comparison checks one factor incorrectly;
                  the comparison should consider multiple factors, but it does not check at least one of those factors at all;
                  the comparison checks the wrong factor.
               
         
         

Fix information:
repo_url: https://github.com/simpleledger/slpjs
commit_details: repo_url: https://github.com/simpleledger/slpjs
author: James Cramer
author_date: 2020-07-29 12:10:14-04:00
msg: critical update for NFT child genesis validation
repository: repo_url: https://github.com/simpleledger/slpjs
repo_name: simpleledger/slpjs
description: JavaScript library for Simple Ledger Protocol
repo_language: TypeScript
owner: simpleledger
file_changes: filename: localvalidator.ts
change_type: ModificationType.MODIFY
diff: @@ -210,9 +210,10 @@ export class LocalValidator implements SlpValidator {
             if (slpmsg.versionType === 0x41) {
                 // An NFT1 parent should be provided at input index 0,
                 // so we check this first before checking the whole parent DAG
-                let input_txid = txn.inputs[0].prevTxId.toString("hex");
-                let input_txhex = await this.retrieveRawTransaction(input_txid);
-                let input_tx: Bitcore.Transaction = new Bitcore.Transaction(input_txhex);
+                const input_txid = txn.inputs[0].prevTxId.toString("hex");
+                const input_prevout = txn.inputs[0].outputIndex;
+                const input_txhex = await this.retrieveRawTransaction(input_txid);
+                const input_tx: Bitcore.Transaction = new Bitcore.Transaction(input_txhex);
                 let input_slpmsg;
                 try {
                     input_slpmsg = this.slp.parseSlpOutputScript(input_tx.outputs[0]._scriptBuffer);
@@ -224,21 +225,34 @@ export class LocalValidator implements SlpValidator {
                     return this.cachedValidations[txid].validity!;
                 }
                 // Check that the there is a burned output >0 in the parent txn SLP message
-                if (input_slpmsg.transactionType === SlpTransactionType.SEND &&
-                    (!input_slpmsg.sendOutputs![1].isGreaterThan(0)))
-                {
-                    this.cachedValidations[txid].validity = false;
-                    this.cachedValidations[txid].waiting = false;
-                    this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
-                    return this.cachedValidations[txid].validity!;
-                } else if ((input_slpmsg.transactionType === SlpTransactionType.GENESIS ||
-                            input_slpmsg.transactionType === SlpTransactionType.MINT) &&
-                            !input_slpmsg.genesisOrMintQuantity!.isGreaterThan(0))
-                {
-                    this.cachedValidations[txid].validity = false;
-                    this.cachedValidations[txid].waiting = false;
-                    this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
-                    return this.cachedValidations[txid].validity!;
+                if (input_slpmsg.transactionType === SlpTransactionType.SEND) {
+                    if (input_prevout > input_slpmsg.sendOutputs!.length - 1) {
+                        this.cachedValidations[txid].validity = false;
+                        this.cachedValidations[txid].waiting = false;
+                        this.cachedValidations[txid].invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
+                        return this.cachedValidations[txid].validity!;
+                    } else if (! input_slpmsg.sendOutputs![input_prevout].isGreaterThan(0)) {
+                        this.cachedValidations[txid].validity = false;
+                        this.cachedValidations[txid].waiting = false;
+                        this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
+                        return this.cachedValidations[txid].validity!;
+                    } else {
+                        this.cachedValidations[txid].validity = true;
+                        this.cachedValidations[txid].waiting = false;
+                    }
+                } else if (input_slpmsg.transactionType === SlpTransactionType.GENESIS ||
+                            input_slpmsg.transactionType === SlpTransactionType.MINT) {
+                    if (input_prevout !== 1) {
+                        this.cachedValidations[txid].validity = false;
+                        this.cachedValidations[txid].waiting = false;
+                        this.cachedValidations[txid].invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
+                        return this.cachedValidations[txid].validity!;
+                    } else if (!input_slpmsg.genesisOrMintQuantity!.isGreaterThan(0)) {
+                        this.cachedValidations[txid].validity = false;
+                        this.cachedValidations[txid].waiting = false;
+                        this.cachedValidations[txid].invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
+                        return this.cachedValidations[txid].validity!;
+                    }
                 }
                 // Continue to check the NFT1 parent DAG
                 let nft_parent_dag_validity = await this.isValidSlpTxid(input_txid, undefined, 0x81);

programming_language: TypeScript
filename: package-lock.json
change_type: ModificationType.MODIFY
diff: @@ -5592,8 +5592,8 @@
       }
     },
     "slp-unit-test-data": {
-      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#a450146e112aee4db5a1b33dc978229fdcfb23a4",
-      "from": "git+https://github.com/simpleledger/slp-unit-test-data.git#a450146e112aee4db5a1b33dc978229fdcfb23a4",
+      "version": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",
+      "from": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",
       "dev": true
     },
     "socket.io": {

programming_language: JavaScript
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -44,7 +44,7 @@
     "mkdirp": "^0.5.5",
     "mocha": "^7.2.0",
     "nyc": "^15.1.0",
-    "slp-unit-test-data": "git+https://github.com/simpleledger/slp-unit-test-data.git#a450146e112aee4db5a1b33dc978229fdcfb23a4",
+    "slp-unit-test-data": "git+https://github.com/simpleledger/slp-unit-test-data.git#8c942eacfae12686dcf1f3366321445a4fba73e7",
     "source-map-support": "^0.5.19",
     "ts-node": "^7.0.1",
     "typescript": "^3.9.5",

programming_language: JavaScript