Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions analyzers/DHCPInfo/DHCPInfo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "DHCPInfo",
"author": "Jared Jennings <[email protected]>",
"license": "AGPL-V3",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers",
"version": "1.0",
"description": "Find DHCP scope name corresponding to an internal IP address",
"dataTypeList": ["ip"],
"command": "DHCPInfo/dhcp_info_analyzer.py",
"baseConfig": "DHCPInfo",
"max_tlp": 3,
"configurationItems": [
{
"name": "dhcp_info_directory",
"description": "Directory on the Cortex server where the DHCP scope info is to be found",
"type": "string",
"multi": false,
"required": true,
"defaultValue": "/path/to/csv-sources"
}
]
}
28 changes: 28 additions & 0 deletions analyzers/DHCPInfo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# DHCP scope analyzer

Find which subnet a local IP address belongs to.


## Rationale

When you have multiple subnets on an internal network, the particular
subnet someone is on may convey some information, like where someone
is in a building. If you have an IP observable containing a user's
workstation IP address, and an incident is going on, you want to pull
in that extra information without having to think about it. This
analyzer does that for you.


## Scope information

Put your DHCP scope information in CSV files (see examples in sources
directory). Set the configuration item `dhcp_info_directory` to the
location of the directory with the CSV files in it.

In particular, if you use Microsoft DHCP, you can directly use
exported DHCP scope information. Just save it to a CSV file like so:

```
Get-DhcpServerv4Scope -ComputerName mydc.example.com |
Export-CSV mydc.csv
```
85 changes: 85 additions & 0 deletions analyzers/DHCPInfo/dhcp_info_analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/usr/bin/env python3

import csv
import glob
import os.path
import ipaddress
from cortexutils.analyzer import Analyzer


def rows_of_csvs(csv_dir):
for csvfn in glob.glob(os.path.join(csv_dir, '*.csv')):
with open(csvfn, 'rt') as csvf:
first_line = next(csvf)
if not first_line.startswith('#TYPE'):
# oops, let's not skip that. start over
csvf.seek(0)
# at this point, type info or no, we should be on the line
# with the headers
d = csv.DictReader(csvf)
for i, row in enumerate(d, start=1):
row['_File'] = csvfn
row['_Row'] = i
yield row


def subnets(rows):
for d in rows:
# by using these titles here, we are assuming they are written
# at the top of columns in the CSV files we parsed
yield {
'net': ipaddress.ip_network(d['ScopeId'] + '/' + d['SubnetMask']),
'first_ip': ipaddress.ip_address(d['StartRange']),
'last_ip': ipaddress.ip_address(d['EndRange']),
'name': d['Name'],
}


def get_name_of_subnet(scopes, ip_str):
a = ipaddress.ip_address(ip_str)
for scope in scopes:
if a in scope['net']:
if (a >= scope['first_ip']) and (a <= scope['last_ip']):
return scope['name']
raise KeyError(ip_str)


class DHCPInfoAnalyzer(Analyzer):
def __init__(self):
super().__init__()
self.dhcp_info_directory = self.get_param(
'config.dhcp_info_directory', None,
'DHCP Info directory is missing')

def summary(self, raw):
taxonomies = []
if 'dhcp_scope_name' in raw:
taxonomies.append(self.build_taxonomy(
'info', 'DHCP', 'ScopeName', raw['dhcp_scope_name']))
return {'taxonomies': taxonomies}

def run(self):
super().run()
if self.data_type == 'ip':
all_rows = rows_of_csvs(self.dhcp_info_directory)
scopes = list(subnets(all_rows))
try:
data = self.get_data()
subnet_name = get_name_of_subnet(scopes, data)
self.report({
'dhcp_scope_name': subnet_name,
})
except KeyError:
self.report({
'not_found': ('address {} not found '
'in any of the {} known '
'DHCP scopes'.format(data, len(scopes))),
})
except Exception as e:
self.unexpectedError(repr(e))
else:
self.notSupported()


if __name__ == '__main__':
DHCPInfoAnalyzer().run()
3 changes: 3 additions & 0 deletions analyzers/DHCPInfo/sources/ad-example.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#TYPE Microsoft.Management.Infrastructure.CimInstance#root/Microsoft/Windows/DHCP/DhcpServerv4Scope
"ScopeId","SubnetMask","StartRange","EndRange","ActivatePolicies","Delay","Description","LeaseDuration","MaxBootpClients","Name","NapEnable","NapProfile","State","SuperscopeName","Type","PSComputerName"
"192.0.2.0","255.255.255.0","192.0.2.51","192.0.2.254","True","0","Office1","08:00:00","4294967295","Office Floor 1","False","","Active","","Dhcp",
2 changes: 2 additions & 0 deletions analyzers/DHCPInfo/sources/minimal-example.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"ScopeId","SubnetMask","StartRange","EndRange","Name"
"192.0.2.0","255.255.255.0","192.0.2.51","192.0.2.254","Office1"
26 changes: 26 additions & 0 deletions thehive-templates/DHCPInfo_1_0/long.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div class="panel panel-danger" ng-if="!success">
<div class="panel-heading">
<strong>{{(artifact.data || artifact.attachment.name) | fang}}</strong>
</div>
<div class="panel-body">
{{content.errorMessage}}
</div>
</div>

<div class="panel panel-info" ng-if="success">
<div class="panel-heading">
DHCP scope information
</div>
<div class="panel-body" ng-show="content.dhcp_scope_name">
<dl class="dl-horizontal">
<dt>Scope name</dt>
<dd>{{content.dhcp_scope_name}}</dd>
</dl>
</div>
<div class="panel-body panel-warning" ng-show="content.not_found">
<dl class="dl-horizontal">
<dt>Not found</dt>
<dd>{{content.not_found}}</dd>
</dl>
</div>
</div>
3 changes: 3 additions & 0 deletions thehive-templates/DHCPInfo_1_0/short.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<span class="label" ng-repeat="t in content.taxonomies" ng-class="{'info': 'label-info', 'safe': 'label-success', 'suspicious': 'label-warning', 'malicious':'label-danger'}[t.level]">
{{t.namespace}}:{{t.predicate}}={{t.value}}
</span>