Skip to content

Integrating Graylog With TheHive

If you couldn't tell by now, we love Graylog. We may have mentioned them a time or two :)

We also love TheHive. It makes tracking incidents easy, it's reliable, and it's another well-managed open source project that we support and contribute to. TheHive also integrates with Cortex, which allows us to run any number of the available Cortex analyzers (OTX, MISP, GreyNoise, Shodan, etc) against observables linked to our cases.

Part of our job at Recon relies on both of these tools.

We bring data into Graylog, and as a result, we need to be able to bring alerts from that data into TheHive.

Previous to writing this tool, our analysts were receiving alerts from Graylog and having to manually create cases, manually extract observables, and manually enter all of this data into TheHive. Less than ideal, right? Lots of room for error, potentially missing key artifacts, wasting time that could be much better spent actually triaging the alert.

Graylog supports alerting via webhooks, and TheHive has a very well-documented API. Knowing this, we decided to bridge the gap and create a tool that allowed us to send alerts from Graylog straight to TheHive.

The benefit to bringing these into TheHive as alerts (versus cases), means that our analysts can assess the alerts before deciding whether or not they even need to be kept and turned into a case. We didn't want to blast every alert (and potentially a false positive) into a full blown case right off the bat.

The tool is aptly named "graylog2thehive", and can be found here.

It's a lightweight python Flask application that listens for incoming webhooks. It can run on the same server as TheHive, or somewhere else, as long as Graylog can reach the configured URL/IP and port.

It uses TheHive4py, which means integration with your instance of TheHive only needs the URL and an API key, which you provide in config.py and/or the systemd service.

config.py:

class Config(object):
    API_KEY=os.environ.get('HIVE_SECRET_KEY')
    HIVE_URL='https://myhive.mycompany.com'
    LOG_FILE='/var/log/graylog2thehive.log'
    GRAYLOG_URL='https://graylog.mycompany.com'

As for integration on the Graylog side, it's even easier. Create a new HTTP notification, and simply tie the notification to your desired Event Definitions.

Screenshot-2020-01-31-13.41.48

 

Additionally, depending on what data you are bringing into your Graylog instance, you can customize what data you want to add as observables attached to each alert.

By default, it only adds src_ip and dst_ip as ip type observables (where src_ip and dst_ip are field names from Graylog), but you can add as many as you want here. This entirely depends on how you are normalizing your fields in Graylog, but here are some examples:

if key == "src_ip" or key == "dst_ip":
    artifacts.append(AlertArtifact(dataType='ip', tlp=3, tags=[key], data=message_flattened[key]))
elif key == "md5" or key == "sha256" or key == "imphash":
    artifacts.append(AlertArtifact(dataType='hash', tags=[key], data=message_flattened[key]))
elif key == "url":
    artifacts.append(AlertArtifact(dataType='url', tags=[key], data=message_flattened[key]))
elif key == "useragent":
    artifacts.append(AlertArtifact(dataType='user-agent', tags=[key], data=message_flattened[key]))
elif key == "process_name" or key == "process_parent":
    artifacts.append(AlertArtifact(dataType='filename', tags=[key], data=message_flattened[key]))
elif key == "filename":
    artifacts.append(AlertArtifact(dataType='filename', tags=[key], data=message_flattened[key]))
elif key == "service_name":
    artifacts.append(AlertArtifact(dataType='other', tags=[key], data=message_flattened[key]))
elif key == "dst_hostname":
    artifacts.append(AlertArtifact(dataType='fqdn', tags=[key], data=message_flattened[key]))
elif key == "src_hostname" or key == "hostname":
    artifacts.append(AlertArtifact(dataType='other', tlp=3, tags=[key], data=message_flattened[key]))

If you'll notice, you can set the TLP for each of these as well. Once you begin running Cortex analyzers against your observables, this becomes important. You don't necessarily want to send your sensitive data to all of your configured analyzers. You likely only want to send your more sensitive data to a subset of analyzers. This way, you can ensure that your Max TLP in Cortex aligns with the TLP you are assigning to your observables.

Screenshot-2020-01-31-13.29.41

Step by step instructions can be found in the README, so you can be up and running in no time!

WANT HANDS ON EXPERIENCE?

Check us out at Black Hat USA 2020 here and here, or join us for OpenSOC in the Blue Team Village at DEF CON 28 this year!

BUT WAIT! THERE'S MORE!

If you want to learn more about Graylog, their documentation is fantastic.

If you want to learn more about TheHive, their documentation is also fantastic.

We thrive on giving back to a community that has provided us with so much of what we use and rely on. Everything we've open sourced can be found in our GitHub, @ReconInfoSec.