Posted by:

David Greenwood

David Greenwood, Chief of Signal

If you are reading this blog post via a 3rd party source it is very likely that many parts of it will not render correctly. Please view the post on signalscorps.com for the full interactive viewing experience.

In this post I will show you how to use STIX 2.1 SDOs, SCOs, and SROs to standardised the way vulnerabilities are represented.

Recapping what I have covered in the previous two posts, cve2stix considers;

  • NVD CPE records
  • NVD CVE records (including CPE configurations)

One of the main design elements of the cve2stix design was the ability to integrate data with other products. STIX 2.1 was what I settled on, becoming an increasingly popular standard in the world of cyber threat intelligence.

I will not cover STIX 2.1 concepts in the post. If you are new to STIX, check out my tutorial on the subject from last year.

CVEs as STIX 2.1 Objects

Vulnerability SDOs

STIX 2.1 contains a Vulnerability SDO, here is the specification for it. In short, it is designed for modelling vulnerabilities, so I will use it for just that.

Using the response from the CVE API (see the schema) I can map the data to the STIX 2.1 Vulnerability SDO;

{
    "type": "vulnerability",
    "spec_version": "2.1",
    "id": "vulnerability--<GENERATED BY STIX2 LIBRARY>",
    "created_by_ref": "identity--<CVE2STIX IDENTITY ID>",
    "created": "<vulnerabilities.cve.published>",
    "modified": "<vulnerabilities.cve..lastModified>",
    "name": "CVE: <vulnerabilities.cve.id>",
    "description": "<vulnerabilities.cve.descriptions.description_data.value> (if multiple, where lan = en, else first result)",
    "external_references": [
        {
        	"source_name": "cve",
          	"external_id": "<vulnerabilities.cve.id>"
        },
        {
          	"source_name": "cwe",
          	"external_id": "<vulnerabilities.cve.weaknesses.description.value[n]>"
        },
        { 
          	"source_name": "<vulnerabilities.cve.references.source.[n]>",
          	"url": "<vulnerabilities.cve.references.url.[n]>",
          	"tags": [
             	"<vulnerabilities.cve.references.tags.[n]>"
         	]
        },
        {
         	"source_name": "cve2stix",
         	"url": "https://github.com/signalscorps/cve2stix"
        }
    ],
    "revoked": "TRUE IF vulnStatus IS REVOKED",
    "extensions": {
        "extension-definition--b2b5f2cd-49e6-4091-a0e0-c0bb71543e23": {
            "extension_type": "property-extension",
            "<ENTIRE NVD API PAYLOAD FOR CVE RECORD>"
        }
    }
}

The CVE metadata and references in the response are mapped directly to the default properties of the STIX 2.1 Vulnerability Object.

Sometime CVEs are revoked for a variety of reasons. When a CVE is revoked, the vulnStatus becomes Revoked. In which case a revoked property is included in the Vulnerability SDO with its value set to true.

To ensure no data loss, the entire NVD API payload is captured in a custom STIX 2.1 extension (extension-definition--b2b5f2cd-49e6-4091-a0e0-c0bb71543e23).

The extensions definitions configurations object is the critical part of the CVE for matching to the CVE to affected software.

These configurations are indicators of potential malicious activities. Whilst I include the raw CPE configurations data in the CVE Object (which defines what CPE is actually vulnerable), to better represent this data cve2stix also uses the STIX 2.1 Indicator SDO.

Indicator SDOs

STIX 2.1 Indicator Objects contain STIX Patterns that can be used to describe the CPE configuration logic defined in the CVE.

The STIX 2.1 Specification contains a Software SCO that can be used to construct these patterns, here is the specification for it.

The problem here is CPEs cover applications (software), operating systems (also software), and hardware (not software).

As a trade-off to ensure consistency I decided to model all CPEs as Software SCOs regardless of whether they are software or not.

The pattern object is always constructed from STIX Software SCOs CPE property (software.cpe).

For example, if the CVE contained a simple node configuration with the following CPE URI cpe:2.3:o:tesla:model_3_firmware:-:*:*:*:*:*:*:* the pattern would read;

    "pattern": "[software.cpe = 'cpe:2.3:o:tesla:model_3_firmware:-:*:*:*:*:*:*:*']",

The logic to create the pattern is based on the node configurations inside the CVE (the operators used AND, OR, and parenthesis). This was explained in part 2.

Here is the structure of the Indicator SDO and how cve2stix populates it;

  {
    "type": "indicator",
    "spec_version": "2.1",
    "id": "indicator--<GENERATED BY STIX2 LIBRARY>",
    "created_by_ref": "identity--<CVE2STIX IDENTITY ID>",
    "created": "<vulnerabilities.cve.published>",
    "modified": "<vulnerabilities.cve.lastModifiedDate>",
    "indicator_types": ["compromised"],
    "name": "<CPE VENDOR / PRODUCT / VERSION>",
    "description": "vulnerabilities.cve.description.description_data.value> (if multiple, where lan = en, else first result)",
    "pattern": "<CPE PATTERN>",
    "valid_from": "<vulnerabilities.cve.publishedDate>",
    "extensions": {
        "extension-definition--b463c449-d022-48b7-b464-3e9c7ec5cf16": {
            "extension_type": "property-extension",
            "all_cpe23uris": [
                "<ALL CPE URIS IN PATTERN>"
            ],
            "vulnerable_cpe23uris": [
                "<ALL CPE URIS IN PATTERN THAT ARE VULNERABLE>"
            ]
        }
    }
  }

In the case of multiple CPEs present in the match pattern, the name considers all CPEs joined with the same operator as the node.

If you remember back to the last post, all the CPE’s referenced in the pattern might not be vulnerable. As such, an extension is used to show which of the CPE URI’s in the pattern are vulnerable should a match be observed.

Note, a CVE can have zero or more Indicator SDOs linked to it depending on the CPE configurations (an easy way to think of this is that the number of nodes in a CVE will equal the number of Indicator SDOs cve2stix creates).

Let me demonstrate using the examples of varying complexity from the last post…

Simple Relationships

CVE-2022-29098 offers a good example of simple relationships.

In this case, the 6 configurations variations that lead to matches.

                "configurations": [
                    {
                        "nodes": [
                            {
                                "operator": "OR",
                                "negate": false,
                                "cpeMatch": [
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:a:dell:powerscale_onefs:9.0.0:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "30687628-5C7F-4BB5-B990-93703294FDF0"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:a:dell:powerscale_onefs:9.1.0:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "68291D44-DBE1-4923-A848-04E64288DC23"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:a:dell:powerscale_onefs:9.1.1:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "DCC55FA4-AD91-4DA6-B60E-A4E34DDAE95A"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:a:dell:powerscale_onefs:9.2.0:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "B948CD53-3D17-4230-9B77-FCE8E0E548B9"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:a:dell:powerscale_onefs:9.2.1:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "5AB99A1A-8DD3-4DDE-B70C-0E91D1D3B682"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:a:dell:powerscale_onefs:9.3.0:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "61F14753-D64C-4E8B-AA94-07E014848B4D"
                                    }
                                ]
                            }
                        ]
                    }
                ],
  1. Dell PowerScale OneFS version 9.0.0 ("matchCriteriaId": "30687628-5C7F-4BB5-B990-93703294FDF0") OR,
  2. Dell PowerScale OneFS version 9.1.0 ("matchCriteriaId": "68291D44-DBE1-4923-A848-04E64288DC23") OR,
  3. Dell PowerScale OneFS version 9.1.1 ("matchCriteriaId": "DCC55FA4-AD91-4DA6-B60E-A4E34DDAE95A") OR,
  4. Dell PowerScale OneFS (version 9.2.0) ("matchCriteriaId": "B948CD53-3D17-4230-9B77-FCE8E0E548B9") OR,
  5. Dell PowerScale OneFS (version 9.2.1) ("matchCriteriaId": "5AB99A1A-8DD3-4DDE-B70C-0E91D1D3B682") OR,
  6. Dell PowerScale OneFS (version 9.3.0) ("matchCriteriaId": "61F14753-D64C-4E8B-AA94-07E014848B4D")

Note, all matchCriteriaId responses contain only one CPE URI.

In this example there would be one Indicator SDO created (because only one nodes) with a pattern as follow;

"pattern": "[ software.cpe='cpe:2.3:a:dell:powerscale_onefs:9.0.0:*:*:*:*:*:*:*' ] OR [ software.cpe='cpe:2.3:a:dell:powerscale_onefs:9.1.0:*:*:*:*:*:*:*' ] OR [ software.cpe='cpe:2.3:a:dell:powerscale_onefs:9.1.1:*:*:*:*:*:*:*' ] OR [ software.cpe='cpe:2.3:a:dell:powerscale_onefs:9.2.0:*:*:*:*:*:*:*' ] OR [ software.cpe='cpe:2.3:a:dell:powerscale_onefs:9.2.1:*:*:*:*:*:*:*' ] OR [ software.cpe='cpe:2.3:a:dell:powerscale_onefs:9.3.0:*:*:*:*:*:*:*' ]"

Running On/With Relationships

Let me demonstrate how more complex Relationships are modelled using the example CVE-2022-27948.

In total there are 24 possible product combinations that are vulnerable in this CVE (see the last post for an explanation).

                "configurations": [
                    {
                        "operator": "AND",
                        "nodes": [
                            {
                                "operator": "OR",
                                "negate": false,
                                "cpeMatch": [
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:o:tesla:model_3_firmware:*:*:*:*:*:*:*:*",
                                        "versionEndIncluding": "2022-03-26",
                                        "matchCriteriaId": "86619D7A-ACB6-489C-9C29-37C6018E5B4B"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:o:tesla:model_s_firmware:*:*:*:*:*:*:*:*",
                                        "versionEndIncluding": "2022-03-26",
                                        "matchCriteriaId": "FD68704D-C711-491F-B278-B02C6866738C"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:o:tesla:model_x_firmware:*:*:*:*:*:*:*:*",
                                        "versionEndIncluding": "2022-03-26",
                                        "matchCriteriaId": "C3517683-8493-4D0D-9792-5C9034B1F0B3"
                                    }
                                ]
                            },
                            {
                                "operator": "OR",
                                "negate": false,
                                "cpeMatch": [
                                    {
                                        "vulnerable": false,
                                        "criteria": "cpe:2.3:h:tesla:model_3:-:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "825A79FD-C872-4564-9782-83BEEADDF5D9"
                                    },
                                    {
                                        "vulnerable": false,
                                        "criteria": "cpe:2.3:h:tesla:model_s:-:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "8D28E699-B843-4641-9BA6-406D88231E7C"
                                    },
                                    {
                                        "vulnerable": false,
                                        "criteria": "cpe:2.3:h:tesla:model_x:-:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "C550FF8A-58ED-4265-B33F-10AFDEA95519"
                                    }
                                ]
                            }
                        ]
                    }
                ],

There are have one nodes again in this example, so two Indicator SDOs will be created.

This time there are two cpeMatches. Note how in the Simple Relationships pattern all CPE key values were wrapped in square brackets ([]). Each CPE inside a cpeMatch is wrapped in square brackets.

So here I get pattern that will look like;

    "pattern": "[ software.cpe = 'A' OR software.cpe = 'B' OR software.cpe = 'N'] AND [ software.cpe = '1' OR software.cpe = '2' OR software.cpe = '0']",

Note how the AND joins the two square brackets, that’s because the top level operator in the CVE response shown above is an AND.

For CVE-2022-27948 I get a pattern that reads;

    "pattern": "[ software.cpe = 'cpe:2.3:o:tesla:model_3_firmware:-:*:*:*:*:*:*:*' OR software.cpe = 'cpe:2.3:o:tesla:model_3_firmware:11.0:*:*:*:*:*:*:*' OR software.cpe = 'cpe:2.3:o:tesla:model_3_firmware:2022-03-26:*:*:*:*:*:*:*' OR software.cpe = 'cpe:2.3:o:tesla:model_s_firmware:-:*:*:*:*:*:*:*' OR software.cpe = 'cpe:2.3:o:tesla:model_s_firmware:2022-03-26:*:*:*:*:*:*:*' OR software.cpe = 'cpe:2.3:o:tesla:model_x_firmware:-:*:*:*:*:*:*:*' OR software.cpe = 'cpe:2.3:o:tesla:model_x_firmware:2020-11-23:*:*:*:*:*:*:*' OR software.cpe = 'cpe:2.3:o:tesla:model_x_firmware:2022-03-26:*:*:*:*:*:*:*'] AND [ software.cpe = 'cpe:2.3:h:tesla:model_3:-:*:*:*:*:*:*:*' OR software.cpe = 'cpe:2.3:h:tesla:model_s:-:*:*:*:*:*:*:*' OR software.cpe = 'cpe:2.3:h:tesla:model_x:-:*:*:*:*:*:*:*']",

If you’re wondering where some of the CPEs in this pattern of come from, in this case I needed to get all the CPEs in all the matchCriteriaIds (using the CPE Match Criteria API) to check the list of CPEs that are associated with them.

Advanced Relationships

I will use CVE-2019-18939 to demonstrate another more complex configuration.

                "configurations": [
                    {
                        "nodes": [
                            {
                                "operator": "AND",
                                "negate": false,
                                "cpeMatch": [
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:a:hm-print_project:hm-print:1.2a:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "286DA904-5631-4AAF-86DE-97C23982D2C5"
                                    },
                                    {
                                        "vulnerable": false,
                                        "criteria": "cpe:2.3:h:eq-3:homematic_ccu2:-:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "9C2CF19C-7EDE-4E3C-A736-E6736FF03FDC"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:o:eq-3:homematic_ccu2_firmware:2.47.20:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "38BE17DA-7C5E-427E-B824-151EB27CFF26"
                                    }
                                ]
                            }
                        ]
                    },
                    {
                        "nodes": [
                            {
                                "operator": "AND",
                                "negate": false,
                                "cpeMatch": [
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:a:hm-print_project:hm-print:1.2:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "F5D8290F-3541-4452-99CB-0766CDC59073"
                                    },
                                    {
                                        "vulnerable": false,
                                        "criteria": "cpe:2.3:h:eq-3:homematic_ccu3:-:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "33113AD0-F378-49B2-BCFC-C57B52FD3A04"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:o:eq-3:homematic_ccu3_firmware:3.47.18:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "285F4E29-E299-4F83-9F7E-BB19933AD654"
                                    }
                                ]
                            }
                        ]
                    },
                    {
                        "nodes": [
                            {
                                "operator": "AND",
                                "negate": false,
                                "cpeMatch": [
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:a:hm-print_project:hm-print:1.2a:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "286DA904-5631-4AAF-86DE-97C23982D2C5"
                                    },
                                    {
                                        "vulnerable": false,
                                        "criteria": "cpe:2.3:h:eq-3:homematic_ccu3:-:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "33113AD0-F378-49B2-BCFC-C57B52FD3A04"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:o:eq-3:homematic_ccu3_firmware:3.47.18:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "285F4E29-E299-4F83-9F7E-BB19933AD654"
                                    }
                                ]
                            }
                        ]
                    },
                    {
                        "nodes": [
                            {
                                "operator": "AND",
                                "negate": false,
                                "cpeMatch": [
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:a:hm-print_project:hm-print:1.2:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "F5D8290F-3541-4452-99CB-0766CDC59073"
                                    },
                                    {
                                        "vulnerable": false,
                                        "criteria": "cpe:2.3:h:eq-3:homematic_ccu2:-:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "9C2CF19C-7EDE-4E3C-A736-E6736FF03FDC"
                                    },
                                    {
                                        "vulnerable": true,
                                        "criteria": "cpe:2.3:o:eq-3:homematic_ccu2_firmware:2.47.20:*:*:*:*:*:*:*",
                                        "matchCriteriaId": "38BE17DA-7C5E-427E-B824-151EB27CFF26"
                                    }
                                ]
                            }
                        ]
                    }
                ],

In this CVE there are four nodes, so this time cve2stix will create four Indicator SDOs.

Each pattern is fairly simplistic to create. Each of the nodes only has one cpeMatch. The operator in each cpeMatch is an AND. All matchCriteriaId responses contain only one CPE URI.

This gives four patterns for each Indicator SDO as follows;

Pattern one (node one);

    "pattern": "[ software.cpe = 'cpe:2.3:a:hm-print_project:hm-print:1.2a:*:*:*:*:*:*:*' AND software.cpe = 'cpe:2.3:h:eq-3:homematic_ccu2:-:*:*:*:*:*:*:*' AND software.cpe = 'cpe:2.3:o:eq-3:homematic_ccu2_firmware:2.47.20:*:*:*:*:*:*:*']",

Pattern two (node two);

    "pattern": "[ software.cpe = 'cpe:2.3:a:hm-print_project:hm-print:1.2:*:*:*:*:*:*:*' AND software.cpe = 'cpe:2.3:h:eq-3:homematic_ccu3:-:*:*:*:*:*:*:*' AND software.cpe = 'cpe:2.3:o:eq-3:homematic_ccu3_firmware:3.47.18:*:*:*:*:*:*:*']",

Pattern three (node three);

    "pattern": "[ software.cpe = 'cpe:2.3:a:hm-print_project:hm-print:1.2a:*:*:*:*:*:*:*' AND software.cpe = 'cpe:2.3:h:eq-3:homematic_ccu3:-:*:*:*:*:*:*:*' AND software.cpe = 'cpe:2.3:o:eq-3:homematic_ccu3_firmware:3.47.18:*:*:*:*:*:*:*']",

Pattern four (node four);

    "pattern": "[ software.cpe = 'cpe:2.3:a:hm-print_project:hm-print:1.2:*:*:*:*:*:*:*' AND software.cpe = 'cpe:2.3:h:eq-3:homematic_ccu2:-:*:*:*:*:*:*:*' AND software.cpe = 'cpe:2.3:o:eq-3:homematic_ccu2_firmware:2.47.20:*:*:*:*:*:*:*']",

Relationship SROs

Now that the CVE is modelled as a STIX Vulnerability and STIX Indicator Objects the relationship between them needs to be defined.

cve2stix uses STIX Relationship SROs to do this. They are structured like so;

{
    "type": "relationship",
    "spec_version": "2.1",
    "id": "relationship--<GENERATED BY STIX2 LIBRARY>",
    "created_by_ref": "identity--<CVE2STIX IDENTITY ID>",
    "created": "<vulnerabilities.cve.published>",
    "modified": "<vulnerabilities.cve.lastModifiedDate>",
    "relationship_type": "indicates",
    "source_ref": "indicator--<INDICATOR STIX OBJECT>",
    "target_ref": "vulnerability--<VULNERABILITY STIX OBJECT>"
}

Note, one Relationship SRO is created for every Indicator SDO created for the CVE, but they all point to the same Vulnerability SDO.

Software SCOs

I’ve already shown the Software SCOs used in the STIX pattern. For each Software SCO referenced in a pattern, an SCO will already exist (because cve2stix backfills CPEs from the CPE API on first run).

Software SCOs have a default cpe, languages, vendor, and version Properties that are all found in CPE URIs used to generate them.

cve2stix creates Software SCOs for CPEs as follows;

{
    "type": "software",
    "spec_version": "2.1",
    "id": "software--<GENERATED BY STIX2 LIBRARY>",
    "name": "<products.cpe.titles.title> (if multiple, where lan = en, else first result)",
    "cpe": "<products.cpe.cpeName>",  
    "version": "<products.cpe.cpeName[version_section]>",
    "vendor": "<products.cpe.cpeName[vendor_section]>",
    "languages": "<products.cpe.cpeName[vendor_language]>",
    "revoked": "<products.cpe.deprecated>",
    "extensions": {
        "extension-definition--6c453e0f-9895-498f-a273-2e2dda473377": {
            "extension_type": "property-extension",
            "<ENTIRE NVD API PAYLOAD FOR CPE RECORD>"
        }
    }
}

When a CVE is created and an Indicator pattern references a Software SCO, the two are joined with a Relationship SRO as follows;

{
    "type": "relationship",
    "spec_version": "2.1",
    "id": "relationship--<GENERATED BY STIX2 LIBRARY>",
    "created_by_ref": "identity--<CVE2STIX IDENTITY ID>",
    "created": "<vulnerabilities.cve.published>",
    "modified": "<vulnerabilities.cve.lastModifiedDate>",
    "relationship_type": "pattern-contains",
    "source_ref": "indicator--<INDICATOR STIX OBJECT>",
    "target_ref": "software--<SOFTWARE STIX OBJECT>"
}

Grouping SDOs

Finally a STIX Grouping Object is used to group all the Objects related to the CVE together;

{
    "type": "grouping",
    "spec_version": "2.1",
    "id": "grouping--<GENERATED BY STIX2 LIBRARY>",
    "created_by_ref": "identity--<CVE2STIX IDENTITY ID>",
    "created": "<vulnerabilities.cve.published>",
    "modified": "<vulnerabilities.cve.lastModifiedDate>",
    "name": "CVE: <vulnerabilities.cve.id>",
    "description": "<vulnerabilities.cve.descriptions.description_data.value> (if multiple, where lan = en, else first result)",
    "object_refs": [
        "<IDENTITY, VULNERABILITY, INDICATOR, SOFTWARE, RELATIONSHIP IDs>"
    ]
}

Next up: Enriching NVD CPEs

To provide even more context to those looking at CVEs, cve2stix enriches CVEs using CAPEC and ATT&CK external knowledge-bases.

In the next post I will explain where the data for these enrichments comes from and how it is handled by cve2stix.




Discuss this post


Signals Corps Slack

Never miss an update


Sign up to receive new articles in your inbox as they published.