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

In SLP Validate (npm package slp-validate) before version 1.2.2, 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 1.2.2.

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/slp-validate.js
commit_details: repo_url: https://github.com/simpleledger/slp-validate.js
author: James Cramer
author_date: 2020-07-29 11:53:29-04:00
msg: critical update for NFT child genesis validation
repository: repo_url: https://github.com/simpleledger/slp-validate.js
repo_name: simpleledger/slp-validate.js
description: SLP validator with pre-broadcast validation and burn protection.
repo_language: TypeScript
owner: simpleledger
file_changes: filename: validation.ts
change_type: ModificationType.MODIFY
diff: @@ -346,12 +346,13 @@ export class ValidatorType1 {
             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
-                const inputTxid = txn.inputs[0].previousTxHash;
-                const inputTxHex = await this.retrieveRawTransaction(inputTxid);
-                const inputTx: Transaction = Transaction.parseFromBuffer(inputTxHex);
+                const inputPrevTxid = txn.inputs[0].previousTxHash;
+                const inputPrevOut = txn.inputs[0].previousTxOutIndex;
+                const inputPrevTxHex = await this.retrieveRawTransaction(inputPrevTxid);
+                const inputPrevTx: Transaction = Transaction.parseFromBuffer(inputPrevTxHex);
                 let inputSlpMsg;
                 try {
-                    inputSlpMsg = Slp.parseSlpOutputScript(inputTx.outputs[0].scriptPubKey);
+                    inputSlpMsg = Slp.parseSlpOutputScript(inputPrevTx.outputs[0].scriptPubKey);
                 } catch (_) {}
                 if (!inputSlpMsg || inputSlpMsg.versionType !== 0x81) {
                     validation.validity = false;
@@ -360,23 +361,38 @@ export class ValidatorType1 {
                     return validation.validity!;
                 }
                 // Check that the there is a burned output >0 in the parent txn SLP message
-                if (inputSlpMsg.transactionType === SlpTransactionType.SEND &&
-                    !inputSlpMsg.sendOutputs![1].gt(0)) {
-                    validation.validity = false;
-                    validation.waiting = false;
-                    validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
-                    return validation.validity!;
-                } else if ((inputSlpMsg.transactionType === SlpTransactionType.GENESIS ||
-                            inputSlpMsg.transactionType === SlpTransactionType.MINT) &&
-                            !inputSlpMsg.genesisOrMintQuantity!.gt(0)) {
-                    validation.validity = false;
-                    validation.waiting = false;
-                    validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
-                    return validation.validity!;
+                if (inputSlpMsg.transactionType === SlpTransactionType.SEND) {
+                    if (inputPrevOut > inputSlpMsg.sendOutputs!.length - 1) {
+                        validation.validity = false;
+                        validation.waiting = false;
+                        validation.invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
+                        return validation.validity!;
+                    }
+                    if (! inputSlpMsg.sendOutputs![inputPrevOut].gt(0)) {
+                        validation.validity = false;
+                        validation.waiting = false;
+                        validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
+                        return validation.validity!;
+                    }
+                } else if (inputSlpMsg.transactionType === SlpTransactionType.GENESIS ||
+                            inputSlpMsg.transactionType === SlpTransactionType.MINT) {
+                    if (inputPrevOut !== 1) {
+                        validation.validity = false;
+                        validation.waiting = false;
+                        validation.invalidReason = "NFT1 child GENESIS does not have a valid NFT1 parent input.";
+                        return validation.validity!;
+                    }
+                    if (!inputSlpMsg.genesisOrMintQuantity!.gt(0)) {
+                        validation.validity = false;
+                        validation.waiting = false;
+                        validation.invalidReason = "NFT1 child's parent has SLP output that is not greater than zero.";
+                        return validation.validity!;
+                    }
                 }
+
                 // Continue to check the NFT1 parent DAG
                 const nft_parent_dag_validity = await this.isValidSlpTxid({
-                    txid: inputTxid,
+                    txid: inputPrevTxid,
                     tokenIdFilter: undefined,
                     tokenTypeFilter: 0x81
                 });

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

programming_language: JavaScript