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 the TAXII Server API endpoints that TAXII Clients interact with.

I ended the last post showing the results of the query to a collections object endpoint, but lets start this post by jumping back ever-so-slightly to the object manifests endpoing…

Object manifests

Instead of getting the entire payload of objects in a collection you can instead request the Object Manifests using the Get Object Manifests endpoint.

This is particularly useful to retrieve metadata to decide whether it’s worth retrieving the actual objects.

For example;

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/manifest/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{
   "more":true,
   "next":"a5dddad9-cf65-4c76-a6e9-8308169dc753",
   "objects":[
        {
            "date_added":"2014-05-08T09:00:00.000000Z",
            "id":"relationship--2f9a9aa9-108a-4333-83e2-4fb25add0463",
            "media_type":"application/stix+json;version=2.1",
            "version":"2014-05-08T09:00:00.000Z"
        },
        {
            "date_added":"2016-11-01T03:04:05.000000Z",
            "id":"indicator--cd981c25-8042-4166-8945-51178443bdac",
            "media_type":"application/stix+json;version=2.1",
            "version":"2014-05-08T09:00:00.000Z"
        }
   ]
}

By default Objects returned via the Object/manifest endpoints are returned sorted in ascending order by the date_added property (that is, the time the object was added to the TAXII server). Meaning, the earliest object added to the server appears first.

It is also important to point out the difference between date_added and version properties in the returned response of the manifest endpoint.

date_added refers to the time the object was added to the TAXII server. Whereas version equals the modified property of the most recent STIX Object in the Collection.

Pagination

As noted last time I can page through the results, because on my server I have set the max_page_size=2.

If the more property is set to true in the response then the next property should also be populated so that the client can paginate through the remaining records using the next URL parameter along with the same original query options.

Important note, the next value is regenerated each time, it is not static. Try is by running the request above and seeing how the next value is different. This means you should not cache the next value for pagination at a later time.

Let me show you how…

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/manifest/?next=a5dddad9-cf65-4c76-a6e9-8308169dc753" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{
    "more":true,
    "next":"4adac81a-3d0e-4c1d-a0a6-fa1431569e64",
    "objects":[
        {
            "date_added":"2017-01-20T00:00:00.000000Z",
            "id":"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da",
            "media_type":"application/stix+json;version=2.1",
            "version":"2017-01-20T00:00:00.000Z"
        },
        {
            "date_added":"2017-01-27T13:49:59.997000Z",
            "id":"malware--c0931cc6-c75e-47e5-9036-78fabc95d4ec",
            "media_type":"application/stix+json;version=2.1",
            "version":"2017-01-27T13:49:53.997Z"
        }
    ]
}

And again…

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/manifest/?next=4adac81a-3d0e-4c1d-a0a6-fa1431569e64" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{
    "more":false,
    "objects":[
        {
            "date_added":"2017-12-31T13:49:53.935000Z",
            "id":"indicator--6770298f-0fd8-471a-ab8c-1c658a46574e",
            "media_type":"application/stix+json;version=2.1",
            "version":"2017-01-27T13:49:53.935Z"
        }
    ]
}

And we’ve reached the last page. There are 5 results on the server.

Lets increase the max_page_size to 100 again to show other ways to paginate through the results.

If I run the original request again…

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/manifest/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{
   "more":false,
   "objects":[
      {
         "date_added":"2014-05-08T09:00:00.000000Z",
         "id":"relationship--2f9a9aa9-108a-4333-83e2-4fb25add0463",
         "media_type":"application/stix+json;version=2.1",
         "version":"2014-05-08T09:00:00.000Z"
      },
      {
         "date_added":"2016-11-01T03:04:05.000000Z",
         "id":"indicator--cd981c25-8042-4166-8945-51178443bdac",
         "media_type":"application/stix+json;version=2.1",
         "version":"2014-05-08T09:00:00.000Z"
      },
      {
         "date_added":"2017-01-20T00:00:00.000000Z",
         "id":"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da",
         "media_type":"application/stix+json;version=2.1",
         "version":"2017-01-20T00:00:00.000Z"
      },
      {
         "date_added":"2017-01-27T13:49:59.997000Z",
         "id":"malware--c0931cc6-c75e-47e5-9036-78fabc95d4ec",
         "media_type":"application/stix+json;version=2.1",
         "version":"2017-01-27T13:49:53.997Z"
      },
      {
         "date_added":"2017-12-31T13:49:53.935000Z",
         "id":"indicator--6770298f-0fd8-471a-ab8c-1c658a46574e",
         "media_type":"application/stix+json;version=2.1",
         "version":"2017-01-27T13:49:53.935Z"
      }
   ]
}

You’ll see there are no more pages, all 5 object print withing the 100 limit.

If needed you can also limit the results manually on the object/manifest endpoint using the limit parameter, like so;

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/manifest/?limit=2" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{
   "more":true,
   "next":"adf37226-e8c0-46f5-9d36-7640ef126b71",
   "objects":[
      {
         "date_added":"2014-05-08T09:00:00.000000Z",
         "id":"relationship--2f9a9aa9-108a-4333-83e2-4fb25add0463",
         "media_type":"application/stix+json;version=2.1",
         "version":"2014-05-08T09:00:00.000Z"
      }
   ]
}

If you’re following along with me you’ll also notice in the header of response two properties; X-TAXII-Date-Added-First and X-TAXII-Date-Added-Last. These headers are returned by the objects/manifest endpoints. These headers contain the date/time value of when the first and last records of those returned on that page (NOT the collection). For example,

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/manifest/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
HTTP/1.1 200 OK
Server: Werkzeug/3.0.1 Python/3.11.6
Date: Thu, 2 Dec 2020 21:11:48 GMT
X-TAXII-Date-Added-First: 2014-05-08T09:00:00.000000Z
X-TAXII-Date-Added-Last: 2017-12-31T13:49:53.935000Z
Content-Type: application/taxii+json;version=2.1
Content-Length: 1008
Connection: close
{
   "more":false,
   "objects":[
      {
         "date_added":"2014-05-08T09:00:00.000000Z",
         "id":"relationship--2f9a9aa9-108a-4333-83e2-4fb25add0463",
         "media_type":"application/stix+json;version=2.1",
         "version":"2014-05-08T09:00:00.000Z"
      },
      ...

If the more property is set to true and the next property is empty (because this is not a required property for TAXII servers) then the client may paginate through the remaining records by using the added_after URL parameter with the date/time value from the X-TAXII-Date-Added-Last header along with the same original query options. For example;

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/manifest/?added_after=2017-12-31T13:49:53.935000Z" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{}

Returns no results, but I knew this (because there are only 5 records, and the more property for the last request was false)

Object Versions

To retrieve all object versions, you can use the object version endpoint, here I’m using the second object returned in the previous response;

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/indicator--cd981c25-8042-4166-8945-51178443bdac/versions/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{
    "more": false,
    "versions": [
        "2014-05-08T09:00:00.000Z"
    ]
}

Here I can see one object exists (objects are versions by modified date in the STIX object).

Let me add another version of this object to demonstrate what it would look like with multiple versions.

Lets add a new version of the Indicator STIX Object (91a7b528-80eb-42ed-a74d-c6fbd5a26116)…

Adding / Updating Objects

Adding objects can be done using a POST request to the API, this applies to adding new objects and updating existing objects (their is no PUT method in the TAXII specification).

Remember back to the first post… the authenticated user must have can_write permissions for the Collection.

The indicator object I want to update currently looks as follows;

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/indicator--cd981c25-8042-4166-8945-51178443bdac/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{
   "more":false,
   "objects":[
      {
         "created":"2014-05-08T09:00:00.000Z",
         "id":"indicator--cd981c25-8042-4166-8945-51178443bdac",
         "indicator_types":[
            "file-hash-watchlist"
         ],
         "modified":"2014-05-08T09:00:00.000Z",
         "name":"File hash for Poison Ivy variant",
         "pattern":"[file:hashes.'SHA-256' = 'ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c']",
         "pattern_type":"stix",
         "spec_version":"2.1",
         "type":"indicator",
         "valid_from":"2014-05-08T09:00:00.000000Z"
      }
   ]
}

I will update it by changing the name property from File hash for Poison Ivy variant -> AN UPDATED NAME.

{
   "objects":[
      {
         "created":"2014-05-08T09:00:00.000Z",
         "id":"indicator--cd981c25-8042-4166-8945-51178443bdac",
         "indicator_types":[
            "file-hash-watchlist"
         ],
         "modified":"2020-01-01T00:00:00.000Z",
         "name":"AN UPDATED NAME",
         "pattern":"[file:hashes.'SHA-256' = 'ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c']",
         "pattern_type":"stix",
         "spec_version":"2.1",
         "type":"indicator",
         "valid_from":"2014-05-08T09:00:00.000000Z"
      }
   ]
}

Here’s the request to do that…

curl -X POST "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1" \
    -H "Content-Type: application/taxii+json;version=2.1" \
    -d "{\"objects\":[{\"created\":\"2014-05-08T09:00:00.000Z\",\"id\":\"indicator--cd981c25-8042-4166-8945-51178443bdac\",\"indicator_types\":[\"file-hash-watchlist\"],\"modified\":\"2020-01-01T00:00:00.000Z\",\"name\":\"AN UPDATED NAME\",\"pattern\":\"[file:hashes.'SHA-256' = 'ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c']\",\"pattern_type\":\"stix\",\"spec_version\":\"2.1\",\"type\":\"indicator\",\"valid_from\":\"2014-05-08T09:00:00.000000Z\"}]}"
{
    "failure_count":0,
    "id":"753d0471-d014-4959-910e-7d6ccf836b85",
    "pending_count":0,
    "request_timestamp":"2020-01-02T13:55:48.399973Z",
    "status":"complete",
    "success_count":1,
    "successes":[
        {
            "id":"indicator--cd981c25-8042-4166-8945-51178443bdac",
            "version":"2020-01-01T00:00:00.000Z"
        }
    ],
    "total_count":1
}

Here I can see the one object in the request has been successfully added to the collection.

Note, when adding objects in bulk you can re-check the status of a POST job using the id returned after the original POST request. For example;

{
    "failure_count":0,
    "failures": [],
    "id":"753d0471-d014-4959-910e-7d6ccf836b85",
    "pending_count":0,
    "pendings": [],
    "request_timestamp":"2020-01-02T13:55:48.399973Z",
    "status":"complete",
    "success_count":1,
    "successes":[
        {
            "id":"indicator--cd981c25-8042-4166-8945-51178443bdac",
            "version":"2020-01-01T00:00:00.000Z"
        }
    ],
    "total_count":1
}

Here the response is the same, but it’s likely if you’re adding 1000’s of objects you’ll see objects moving from pendings to failures or successes.

Querying the versions endpoint again, I can now see both versions of the Indicator.

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/indicator--cd981c25-8042-4166-8945-51178443bdac/versions/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{
    "more": false,
    "versions": [
        "2020-01-01T00:00:00.000Z",
        "2014-05-08T09:00:00.000Z"]
}   

If you remember back to the STIX 2.1 tutorial, I showed how SCOs could not be versioned using the modified property (as they don’t contain one).

So lets look as adding two versions of the same SCO to the collection. Firstly, I’ll add;

{
    "objects": [
        {
            "type": "domain-name",
            "spec_version": "2.1",
            "id": "domain-name--dd686e37-6889-53bd-8ae1-b1a503452613",
            "value": "google.com"
        }
    ]
}
curl -X POST "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1" \
    -H "Content-Type: application/taxii+json;version=2.1" \
    -d "{\"objects\":[{\"type\":\"domain-name\",\"spec_version\":\"2.1\",\"id\":\"domain-name--dd686e37-6889-53bd-8ae1-b1a503452613\",\"value\":\"google.com\"}]}"
{
    "failure_count":0,
    "id":"48d2c362-521c-4c94-b33f-5b13f7179393",
    "pending_count":0,
    "request_timestamp":"2020-01-02T19:40:16.411884Z",
    "status":"complete",
    "success_count":1,
    "successes":[
        {
            "id":"domain-name--dd686e37-6889-53bd-8ae1-b1a503452613",
            "version":"2020-01-02T19:40:16.411884Z"
        }
    ],
    "total_count":1
}

Note how the TAXII server automatically assigns a version to the object. The version time matches the time of the import.

Regardless, it’s actually impossible to add another version of an SCO. The TAXII server always assumes an SCO is never updated. Let me show you by adding…

{
    "objects": [
        {
            "type": "domain-name",
            "spec_version": "2.1",
            "id": "domain-name--dd686e37-6889-53bd-8ae1-b1a503452613",
            "value": "google.com",
            "resolves_to_refs": [
                "ipv4-addr--dc63603e-e634-5357-b239-d4b562bc5445"
            ]
        }
    ]
}
curl -X POST "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1" \
    -H "Content-Type: application/taxii+json;version=2.1" \
    -d "{\"objects\":[{\"type\":\"domain-name\",\"spec_version\":\"2.1\",\"id\":\"domain-name--dd686e37-6889-53bd-8ae1-b1a503452613\",\"value\":\"google.com\",\"resolves_to_refs\":[\"ipv4-addr--dc63603e-e634-5357-b239-d4b562bc5445\"]}]}"
{
    "failure_count":1,
    "failures":[
        {
            "id":"domain-name--dd686e37-6889-53bd-8ae1-b1a503452613",
            "message":"Unable to process object",
            "version":"2020-01-02T19:43:28.513386Z"
        }
    ],
    "id":"ce85894d-733a-4cf1-85da-54dec1ae23c3",
    "pending_count":0,
    "request_timestamp":"2020-01-02T19:43:28.513386Z",
    "status":"complete",
    "success_count":0,
    "total_count":1
}

In which case, the original SCO would first need to be deleted, and thus only one version of an SCO can ever exist.

Deleting Objects

The TAXII specification also allows for the deletion of objects in a Collection.

Using the DELETE method (without any parameters) will delete the latest version (by highest modified time) of that object.

For example, lets delete the Indicator that was updated;

curl -X DELETE "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/indicator--cd981c25-8042-4166-8945-51178443bdac/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"

Note, this endpoint will return an empty 200 if the original request was successful.

Querying the versions endpoint again, I can now see only the first version of the Indicator still exists.

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/indicator--cd981c25-8042-4166-8945-51178443bdac/versions/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{
    "more": false,
    "versions": [
        "2014-05-08T09:00:00.000Z"
    ]
}

Running the delete command on the same Indicator again will delete the object entirely (as only one version remains).

curl -X DELETE "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/indicator--cd981c25-8042-4166-8945-51178443bdac/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"

And checking for it again using the versions endpoint…

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/indicator--cd981c25-8042-4166-8945-51178443bdac/versions/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"

Returns a 404.

{
    "description": "Object 'indicator--cd981c25-8042-4166-8945-51178443bdac' not found.",
    "http_status": "404",
    "title": "ProcessingError"
}

The delete endpoint also allows for filtering to delete specific versions or spec_versions of an Object, using the match[versions] and match[spec_versions] parameters.

I’ll demonstrate by adding some data to delete…

{
    "objects": [
        {
            "type": "attack-pattern",
            "spec_version": "2.1",
            "id": "attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5",
            "created_by_ref": "identity--d2916708-57b9-5636-8689-62f049e9f727",
            "created": "2020-01-01T11:21:07.478851Z",
            "modified": "2020-01-01T11:21:07.478851Z",
            "name": "Spear Phishing",
            "description": "Used for tutorial content",
            "object_marking_refs": [
                "marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da"
            ]
        },
        {
            "type": "attack-pattern",
            "spec_version": "2.1",
            "id": "attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5",
            "created_by_ref": "identity--d2916708-57b9-5636-8689-62f049e9f727",
            "created": "2020-01-01T11:21:07.478851Z",
            "modified": "2020-01-02T11:21:07.478851Z",
            "name": "Spear Phishing Updated ONCE",
            "description": "Used for tutorial content",
            "object_marking_refs": [
                "marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da"
            ]
        },
        {
            "type": "attack-pattern",
            "spec_version": "2.1",
            "id": "attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5",
            "created_by_ref": "identity--d2916708-57b9-5636-8689-62f049e9f727",
            "created": "2020-01-01T11:21:07.478851Z",
            "modified": "2020-01-03T11:21:07.478851Z",
            "name": "Spear Phishing Updated TWICE",
            "description": "Used for tutorial content",
            "object_marking_refs": [
                "marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da"
            ]
        }
    ]
}
curl -X POST "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1" \
    -H "Content-Type: application/taxii+json;version=2.1" \
    -d "{\"objects\":[{\"type\":\"attack-pattern\",\"spec_version\":\"2.1\",\"id\":\"attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5\",\"created_by_ref\":\"identity--d2916708-57b9-5636-8689-62f049e9f727\",\"created\":\"2020-01-01T11:21:07.478851Z\",\"modified\":\"2020-01-01T11:21:07.478851Z\",\"name\":\"Spear Phishing\",\"description\":\"Used for tutorial content\",\"object_marking_refs\":[\"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da\"]},{\"type\":\"attack-pattern\",\"spec_version\":\"2.1\",\"id\":\"attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5\",\"created_by_ref\":\"identity--d2916708-57b9-5636-8689-62f049e9f727\",\"created\":\"2020-01-01T11:21:07.478851Z\",\"modified\":\"2020-01-02T11:21:07.478851Z\",\"name\":\"Spear Phishing Updated ONCE\",\"description\":\"Used for tutorial content\",\"object_marking_refs\":[\"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da\"]},{\"type\":\"attack-pattern\",\"spec_version\":\"2.1\",\"id\":\"attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5\",\"created_by_ref\":\"identity--d2916708-57b9-5636-8689-62f049e9f727\",\"created\":\"2020-01-01T11:21:07.478851Z\",\"modified\":\"2020-01-03T11:21:07.478851Z\",\"name\":\"Spear Phishing Updated TWICE\",\"description\":\"Used for tutorial content\",\"object_marking_refs\":[\"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da\"]}]}"

Here we’re adding many versions of the same STIX Attack Pattern object.

{
    "failure_count":0,
    "id":"2d513dd0-06d1-42be-81b1-da34a48c29d7",
    "pending_count":0,
    "request_timestamp":"2023-12-19T19:54:04.444328Z",
    "status":"complete",
    "success_count":3,
    "successes":[
        {
            "id":"attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5",
            "version":"2020-01-01T11:21:07.478851Z"
        },
        {
            "id":"attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5",
            "version":"2020-01-02T11:21:07.478851Z"
        },
        {
            "id":"attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5",
            "version":"2020-01-03T11:21:07.478851Z"
        }
    ],
    "total_count":3
}

Now, if I was to run the delete command without any parameters in the request, only the latest version of the object (2020-01-03T11:21:07.478851Z) would be removed.

So instead, lets say I wanted to delete the 2nd version 2020-01-02T11:21:07.478851Z. I could do this using match[versions]=2020-01-02T11:21:07.478851Z.

curl -X DELETE "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5/?match\[versions\]=2020-01-02T11:21:07.478851Z" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"

Lets recheck the versions…

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5/versions/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{  
    "more": false,
    "versions": [
        "2020-01-03T11:21:07.478851Z",
        "2020-01-01T11:21:07.478851Z"
    ]
}  

I can delete all remaining versions in the same way…

curl -X DELETE "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5/?match\[versions\]=2020-01-01T11:21:07.478851Z,2020-01-03T11:21:07.478851Z" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"

Note, multiple values passed to fields that support match values can be passed with a comma ‘,’ which is treated as a logical OR. For example, ?match[versions]=2020-01-01T11:21:07.478851Z,2020-01-03T11:21:07.478851Z.

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5/versions/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{
    "description": "Object 'attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5' not found.",
    "http_status": "404",
    "title": "ProcessingError"
}

Filtering objects

I’ve shown some examples of filtering to achieve pagination, but all object/manifest endpoints can also be filtered to find specific objects

For example, the following filtering parameters can be used with the Get Objects endpoint;

Used for pagination;

  • added_after: e.g. 2015-01-01T00:00:00.000Z
  • limit: e.g. 100
  • next: e.g. 2

Used against object properties;

  • match[id]: e.g. indicator--29aba82c-5393-42a8-9edb-6a2cb1df070b,indicator--ef0b28e1-308c-4a30-8770-9b4851b260a5
  • match[type]: e.g. indicator,sighting
  • match[version]: e.g. first,2018-03-02T01:01:01.123Z,last
  • match[spec_version]: e.g. 2.0,2.1

For example;

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/?match\[type\]=indicator" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"

Returns 2 indicators…

{
   "more":false,
   "objects":[
      {
         "created":"2014-05-08T09:00:00.000Z",
         "id":"indicator--cd981c25-8042-4166-8945-51178443bdac",
         "indicator_types":[
            "file-hash-watchlist"
         ],
         "modified":"2014-05-08T09:00:00.000Z",
         "name":"File hash for Poison Ivy variant",
         "pattern":"[file:hashes.'SHA-256' = 'ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c']",
         "pattern_type":"stix",
         "spec_version":"2.1",
         "type":"indicator",
         "valid_from":"2014-05-08T09:00:00.000000Z"
      },
      {
         "created":"2016-11-03T12:30:59.000Z",
         "description":"Accessing this url will infect your machine with malware. This is the last updated indicator",
         "id":"indicator--6770298f-0fd8-471a-ab8c-1c658a46574e",
         "indicator_types":[
            "url-watchlist"
         ],
         "modified":"2017-01-27T13:49:53.935Z",
         "name":"Malicious site hosting downloader",
         "pattern":"[url:value = 'http://x4z9arb.cn/4712']",
         "pattern_type":"stix",
         "spec_version":"2.1",
         "type":"indicator",
         "valid_from":"2016-11-03T12:30:59.000Z"
      }
   ]
}

Remember earlier I added 3 versions of an Attack Pattern (and then deleted them)? Let me add them to the server again, so that I can demonstrate how the get objects endpoints deal with versioning.

curl -X POST "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1" \
    -H "Content-Type: application/taxii+json;version=2.1" \
    -d "{\"objects\":[{\"type\":\"attack-pattern\",\"spec_version\":\"2.1\",\"id\":\"attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5\",\"created_by_ref\":\"identity--d2916708-57b9-5636-8689-62f049e9f727\",\"created\":\"2020-01-01T11:21:07.478851Z\",\"modified\":\"2020-01-01T11:21:07.478851Z\",\"name\":\"Spear Phishing\",\"description\":\"Used for tutorial content\",\"object_marking_refs\":[\"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da\"]},{\"type\":\"attack-pattern\",\"spec_version\":\"2.1\",\"id\":\"attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5\",\"created_by_ref\":\"identity--d2916708-57b9-5636-8689-62f049e9f727\",\"created\":\"2020-01-01T11:21:07.478851Z\",\"modified\":\"2020-01-02T11:21:07.478851Z\",\"name\":\"Spear Phishing Updated ONCE\",\"description\":\"Used for tutorial content\",\"object_marking_refs\":[\"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da\"]},{\"type\":\"attack-pattern\",\"spec_version\":\"2.1\",\"id\":\"attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5\",\"created_by_ref\":\"identity--d2916708-57b9-5636-8689-62f049e9f727\",\"created\":\"2020-01-01T11:21:07.478851Z\",\"modified\":\"2020-01-03T11:21:07.478851Z\",\"name\":\"Spear Phishing Updated TWICE\",\"description\":\"Used for tutorial content\",\"object_marking_refs\":[\"marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da\"]}]}"
curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5/versions/" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"
{  
    "more": false,
    "versions": [
        "2020-01-03T11:21:07.478851Z",
        "2020-01-02T11:21:07.478851Z",
        "2020-01-01T11:21:07.478851Z"
    ]
}  

And now I’ll filter by type=attack-pattern;

curl -X GET "http://localhost:5000/trustgroup1/collections/91a7b528-80eb-42ed-a74d-c6fbd5a26116/objects/?match\[type\]=attack-pattern" \
    -H "Authorization: Basic YWRtaW46UGFzc3dvcmQw" \
    -H "Accept: application/taxii+json;version=2.1"

See how only the latest object version 2020-01-03T11:21:07.478851Z is returned.

{
   "more":false,
   "objects":[
      {
         "created":"2020-01-01T11:21:07.478851Z",
         "created_by_ref":"identity--d2916708-57b9-5636-8689-62f049e9f727",
         "description":"Used for tutorial content",
         "id":"attack-pattern--6b948b5a-3c09-5365-b48a-da95c3964cb5",
         "modified":"2020-01-03T11:21:07.478851Z",
         "name":"Spear Phishing Updated TWICE",
         "object_marking_refs":[
            "marking-definition--34098fce-860f-48ae-8e50-ebd3cc5e41da"
         ],
         "spec_version":"2.1",
         "type":"attack-pattern"
      }
   ]
}

Normally this is fine as typically it is only the latest object you want. However, this does highlight the importance of the version object endpoint, as there may be many historic versions of the objects being returned from the objects/manifest endpoints.

TAXII Clients

In this post I’ve shown you at a high-level how a TAXII client would interact with a TAXII server.

Instead of writing all these request by hand, I may as well use a client that already exists, in the same way that I didn’t write an entire TAXII server myself.

I’ll cover that in the next post.


TAXII 2.1 Certification (Virtual and In Person)

The content used in this post is a small subset of our full training material used in our TAXII 2.1 training.

If you want to join a select group of certified TAXII 2.1 professionals, subscribe to our newsletter below to be notified of new course dates.




Discuss this post


Signals Corps Slack

Never miss an update


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