Skip to content ↓ | Skip to navigation ↓

Tripwire has been getting more involved in connecting its products to threat intelligence services lately.

I described the reasons why we care about threat intelligence, particularly STIX and TAXII, in my article last month: Why We Should Care About STIX & TAXII. My colleagues also talked in more specifics about some of the partners Tripwire is working with in the post Threat Intelligence: Reduce the Gap.

If you are thinking about connecting your own products or services to threat intelligence feeds, one of the first questions you are apt to be asking yourself is: how hard is this going to be?

So, I thought I would share some code snippets we developed to connect to a TAXII server and show just how easy it is. Before you start to write code, the first step would be to get a TAXII server to connect to. Here are two very easy options for you:

  1. Hail a TAXII – This is a freely usable TAXII server that is publicly accessible on the internet, put up by our friends at Soltra. It has some open source threat intelligence on it, and that makes it a great place to connect to pull a TAXII feed from. It is not a test-bed though, so it may not be a great place to test non-compliant experimental code.
  2. Soltra Edge – Soltra, one of Tripwire’s partners, has a freely available TAXII server you can download as a virtual machine and easily stand-up. This would be the option I’d recommend, as you can have it on a virtualization server or on your local system using something like VMWare Workstation, and start to create TAXII feeds and test your code with it.

Now, here is under 70 lines of python code that will help you pull a TAXII feed and extract a particular element you are interested in out of the STIX content it contains. If you want to use some pre-built code, this is definitely not your only option. Soltra includes some TAXII client code with Soltra Edge as well, and you can also find various TAXII bindings and libraries as part of the TAXII project at

This code is commented extra-well, so you can follow-along with it even if you aren’t familiar with Python.


# These two modules handle the HTTP(S) protocol for us
import urllib2
from urlparse import urlparse
# We'll use ElementTree to parse the XML as it's a simple binding which ships with Python
import xml.etree.ElementTree as ET
# These are used to help with HTTP basic auth and generate random message IDs, respectively
import base64
import random

# The namespace of the TAXII document we'll send to the server
taxii_ns = ''

# This class parses the CybOX results from the server
class CybOXObject(object):

    # xml parameter should be an ElementTree.Element with the CybOX object inside it
    # url parameter is the feed location
    def __init__(self, xml, baseurl):
        # construct a new URL we can use to browse to this object in the Soltra dashboard
        p = urlparse(baseurl)
        self.url = '%s://%s/object/%s' % (p.scheme, p.netloc,  xml.get('id'))
        # Find all file hashes using the ElementTree "findall" method. Each
        # Simple_Hash_Value XML node can contain multiple hashes separated by a delimiter.
        self.hashes = []
        for hashval in xml.findall('.//{}Simple_Hash_Value'):
            self.hashes.extend(hashval.text.split(hashval.get('delimiter', '##comma##')))

# Internal function constructing an HTTP request to read unread threat data via TAXII
def _taxii_req(url, feedname, username, password):
    # Construct an XML document with 'Poll_Request' as the top-level node name.
    xmldata = ET.Element('Poll_Request', {'xmlns': taxii_ns,
                                          'message_id': str(random.randint(345271,9999999999)),
                                          'collection_name': feedname})

    # Fill in a few TAXII parameters
    pp = ET.SubElement(xmldata, 'Poll_Parameters', {'allow_asynch': 'false'})

    ET.SubElement(pp, 'Response_Type').text = 'FULL'
    ET.SubElement(pp, 'Content_Binding', {'binding_id': ''})

    # Create an HTTP request with our XML as the POST data
    req = urllib2.Request(url, ET.tostring(xmldata))

    # Add some headers to the request and return it
    if username is not None and password is not None:
        req.add_header('Authorization', 'Basic %s' % 
                       base64.encodestring('%s:%s' % 
                       (username, password)).replace('\n', ''))
    req.add_header('Content-Type', 'application/xml')
    req.add_header('User-Agent', 'TAXII Client Application')
    req.add_header('Accept', 'application/xml')
    req.add_header('X-TAXII-Accept', '')
    req.add_header('X-TAXII-Content-Type', '')
    req.add_header('X-TAXII-Protocol', '')
    return req

# Function to poll a TAXII feed. Returns a list of CybOXObject instances
def poll_feed(url, feedname, username, password):
    return [CybOXObject(x, url) for x in
            ET.fromstring(urllib2.urlopen(_taxii_req(url, feedname, username, password)).read())

# Example command-line usage
if __name__ == '__main__':
    import sys
    if len(sys.argv) < 3:         print "usage: url feedname [username] [password]"         sys.exit(-1)     url = sys.argv[1]     feedname = sys.argv[2]     username, password = None, None     if len(sys.argv) >= 5:
        username = sys.argv[3]
        password = sys.argv[4]

    for obj in poll_feed(url, feedname, username, password):
        if obj.hashes:
            print "%s: %s" % (obj.url, obj.hashes)

Source code examples are released under the BSD 2 Clause License found at

As you can see, this is actually pretty straight-forward. Browsing through the relevant standards, one might think that STIX and TAXII require months of study to successfully consume. But most of the complexity at the XML level lies in CybOX, the dictionary for which needs to be voluminous to handle all the different possible types of threat data. But if your organization is only interested in a few kinds of data, it’s easy enough to pick out and handle the elements you want (as the example above does with Simple_Hash_Value) and ignore the rest. In fact, STIX, which acts as a container for CybOX, is never addressed at all here; instead we just use the “.//” syntax in XPath to skip right past it to the actual content.

Likewise, while it’s possible to create complex feed scenarios with TAXII, starting out as a consuming client is virtually as simple as sending any other HTTP request; in the example above, we constructed a poll request in four lines of code, then added a few TAXII-specific HTTP headers. Users can extend this basic functionality to handle requesting indicators over a range of timestamps, etc., but for the purposes of discovering any threats which have come in since the last poll request (which Soltra supports by default), the code shown here is pretty much all you’ll need.

So, have you tried consuming external threat data using STIX and TAXII yet? If you were waiting because the setup seemed too complex, I guess you’ll need to find another excuse.


picThe Executive’s Guide to the Top 20 Critical Security Controls

Tripwire has compiled an e-book, titled The Executive’s Guide to the Top 20 Critical Security Controls: Key Takeaways and Improvement Opportunities, which is available for download [registration form required].

Title image courtesy of ShutterStock

SANS White Paper: Security Basics
  • Patrick Maroney


    Many thanks to your and your team for the sustained support for the STIX/CybOx/TAXII et all initiatives. The development and sharing of reference implementations is key to success and adoption.


  • Patrick Maroney

    In the post need to fix the indentation:

    if len(sys.argv) < 3: print "usage: url feedname [username] [password]" sys.exit(-1) url = sys.argv[1] feedname = sys.argv[2] username, password = None, None

    if __name__ == '__main__':
    import sys
    if len(sys.argv) < 3:
    print "usage: url feedname [username] [password]"
    url = sys.argv[1]
    feedname = sys.argv[2]
    username, password = None, None

  • Sotiris

    Hi Patrick,
    I would like to ask you for more information about STIX/CybOx/TAXII, is it easy for you?

  • Sarvagya Pant

    Thank you for your pointer towards consuming STIX. I have one question. Lets say I want to create a taxonomy that is fixed and would like to populate it with STIX documents. If so, how can I proceed and populate it. Since the STIX document can have any number of permutation, how should I proceed about it. Thanks for your effforts.

  • Scotty

    There is also this guy if you are looking to get started consuming STIX over TAXII – but need the data in a different output format once you have it (Bro, MISP, csv, Snort):