Skip to content

Commit

Permalink
Revamp Shodan analyzer (#328)
Browse files Browse the repository at this point in the history
  • Loading branch information
amr-cossi authored and nadouani committed Dec 4, 2018
1 parent ac108c3 commit 2166975
Show file tree
Hide file tree
Showing 18 changed files with 292 additions and 35 deletions.
23 changes: 23 additions & 0 deletions analyzers/Shodan/Shodan_DNSResolve.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "Shodan_DNSResolve",
"version": "1.0",
"author": "Sebastien Larinier @Sebdraven",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers/Shodan",
"license": "AGPL-V3",
"description": "Retrieve domain resolutions on Shodan.",
"dataTypeList": ["domain"],
"command": "Shodan/shodan_analyzer.py",
"baseConfig": "Shodan",
"config": {
"service": "dns_resolve"
},
"configurationItems": [
{
"name": "key",
"description": "Define the API Key",
"type": "string",
"multi": false,
"required": true
}
]
}
8 changes: 0 additions & 8 deletions analyzers/Shodan/Shodan_Host.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@
"type": "string",
"multi": false,
"required": true
},
{
"name": "polling_interval",
"description": "Define the polling interval",
"type": "number",
"multi": false,
"required": false,
"defaultValue": 60
}
]
}
23 changes: 23 additions & 0 deletions analyzers/Shodan/Shodan_Host_History.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "Shodan_Host_History",
"version": "1.0",
"author": "Sebastien Larinier @Sebdraven",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers/Shodan",
"license": "AGPL-V3",
"description": "Retrieve Shodan history scan results for an IP address.",
"dataTypeList": ["ip"],
"command": "Shodan/shodan_analyzer.py",
"baseConfig": "Shodan",
"config": {
"service": "host_history"
},
"configurationItems": [
{
"name": "key",
"description": "Define the API Key",
"type": "string",
"multi": false,
"required": true
}
]
}
23 changes: 23 additions & 0 deletions analyzers/Shodan/Shodan_InfoDomain.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "Shodan_InfoDomain",
"version": "1.0",
"author": "Sebastien Larinier @Sebdraven",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers/Shodan",
"license": "AGPL-V3",
"description": "Retrieve key Shodan information on a domain.",
"dataTypeList": ["domain"],
"command": "Shodan/shodan_analyzer.py",
"baseConfig": "Shodan",
"config": {
"service": "info_domain"
},
"configurationItems": [
{
"name": "key",
"description": "Define the API Key",
"type": "string",
"multi": false,
"required": true
}
]
}
23 changes: 23 additions & 0 deletions analyzers/Shodan/Shodan_ReverseDNS.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "Shodan_ReverseDNS",
"version": "1.0",
"author": "Sebastien Larinier @Sebdraven",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers/Shodan",
"license": "AGPL-V3",
"description": "Retrieve ip reverse DNS resolutions on Shodan.",
"dataTypeList": ["ip"],
"command": "Shodan/shodan_analyzer.py",
"baseConfig": "Shodan",
"config": {
"service": "reverse_dns"
},
"configurationItems": [
{
"name": "key",
"description": "Define the API Key",
"type": "string",
"multi": false,
"required": true
}
]
}
14 changes: 3 additions & 11 deletions analyzers/Shodan/Shodan_Search.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "Shodan_Search",
"version": "1.0",
"version": "2.0",
"author": "Sebastien Larinier @Sebdraven",
"url": "https://github.com/TheHive-Project/Cortex-Analyzers/Shodan",
"license": "AGPL-V3",
"description": "Retrieve key Shodan information on a domain.",
"dataTypeList": ["domain"],
"description": "Search query on Shodan",
"dataTypeList": ["other"],
"command": "Shodan/shodan_analyzer.py",
"baseConfig": "Shodan",
"config": {
Expand All @@ -18,14 +18,6 @@
"type": "string",
"multi": false,
"required": true
},
{
"name": "polling_interval",
"description": "Define the polling interval",
"type": "number",
"multi": false,
"required": false,
"defaultValue": 60
}
]
}
53 changes: 39 additions & 14 deletions analyzers/Shodan/shodan_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,43 @@ class ShodanAnalyzer(Analyzer):
def __init__(self):
Analyzer.__init__(self)
self.shodan_key = self.get_param('config.key', None, 'Missing Shodan API key')
self.service = self.getParam('config.service', None, 'Service parameter is missing')
self.shodan_client = None
self.polling_interval = self.get_param('config.polling_interval', 60)

def execute_shodan_service(self, data):
if self.service in ['host', 'host_history']:
results = {'host': self.shodan_client.host(data, history=True if self.service == 'host_history' else False)}
return results
elif self.service == 'dns_resolve':
results = {'records': self.shodan_client.dns_resolve(data)}
return results
elif self.service == 'reverse_dns':
results = {'records': self.shodan_client.reverse_dns(data)}
return results
elif self.service == 'search':
page = self.getParam('parameters.page', 1, None)
results = {'records': self.shodan_client.search(data, page)}
return results
elif self.service == 'info_domain':
results = {'info_domain': self.shodan_client.info_domains(data)}
return results
else:
self.error("Unknown service")

def summary(self, raw):
taxonomies = []
level = "info"
namespace = "Shodan"
predicate = "Location"
if self.data_type == 'ip':
if self.service in ['host', 'host_history']:
if 'country_name' in raw['host']:
value = raw['host']['country_name']
taxonomies.append(self.build_taxonomy(level, namespace, predicate, value))
if 'org' in raw['host']:
taxonomies.append(self.build_taxonomy(level, namespace, 'Org', raw['host']['org']))
if 'asn' in raw['host']:
taxonomies.append(self.build_taxonomy(level, namespace, 'ASN', raw['host']['asn']))
elif self.data_type == 'domain':
elif self.service == 'info_domain':
if 'ips' in raw['infos_domain']:
value = "{}".format(len(raw['infos_domain']['ips']))
taxonomies.append(self.build_taxonomy(level, namespace, 'IPs', value))
Expand All @@ -38,24 +58,29 @@ def summary(self, raw):
if 'isp' in raw['infos_domain']:
value = "{}".format(len(raw['infos_domain']['isp']))
taxonomies.append(self.build_taxonomy(level, namespace, 'ISPs', value))

elif self.service == 'dns_resolve':
value = "{}".format(len(raw['records']))
taxonomies.append(self.build_taxonomy(level, namespace, 'DNS resolutions', value))
elif self.service == 'reverse_dns':
nb_domains = 0
for k in raw['records'].keys():
nb_domains += len(raw['records'][k])
value = "{}".format(len(nb_domains))
taxonomies.append(self.build_taxonomy(level, namespace, 'Reverse DNS resolutions', value))
elif self.service == 'search':
value = "{}".format(raw['records']['total'])
taxonomies.append(self.build_taxonomy(level, namespace, 'Hosts', value))
return {'taxonomies': taxonomies}

def run(self):
Analyzer.run(self)

try:
self.shodan_client = ShodanAPIPublic(self.shodan_key)
if self.data_type == 'ip':
ip = self.get_param('data', None, 'Data is missing')
results = {'reverse_dns': {'hostnames': self.shodan_client.reverse_dns(ip)[ip]},
'host': self.shodan_client.host(ip)}
self.report(results)
if self.data_type == 'domain':
domain = self.get_param('data', None, 'Data is missing')
result = {'dns_resolve': self.shodan_client.dns_resolve(domain),
'infos_domain': self.shodan_client.info_domains(domain)}
self.report(result)
data = self.get_param('data', None, 'Data is missing')
results = self.execute_shodan_service(data)
self.report(results)

except APIError as e:
self.error(str(e))
except Exception as e:
Expand Down
8 changes: 6 additions & 2 deletions analyzers/Shodan/shodan_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ class ShodanAPIPublic(Shodan):
def __init__(self, api_key):
Shodan.__init__(self, api_key)

def host(self, ips):
def host(self, ips, history=False, minify=False):

host = Shodan.host(self, ips)
host = Shodan.host(self, ips, history=history, minify=minify)
if host:
return host

Expand All @@ -30,6 +30,10 @@ def info_domains(self, domain):
'isp': isp, 'asn': asn, 'orgs': orgs
}

def search(self, query, page=1):
results = Shodan.search(self, query, page=page)
return results

def dns_resolve(self, domain):
payload = {'hostnames': [domain], 'key': self.api_key}
r = requests.get(urljoin(self.base_url, 'dns/resolve'), params=payload)
Expand Down
22 changes: 22 additions & 0 deletions thehive-templates/Shodan_DNSResolve_1_0/long.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div class="panel panel-info" ng-if="success">
<div class="panel-heading">
DNS resolutions of <strong>{{artifact.data | fang}}</strong>
</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dd ng-repeat="(domain, ip) in content.records">
{{domain}}: {{ip}}
</dd>
</dl>

</div>
</div>

<div class="panel panel-danger" ng-if="!success">
<div class="panel-heading">
<strong>{{artifact.data | fang}}</strong>
</div>
<div class="panel-body">
{{content.errorMessage}}
</div>
</div>
48 changes: 48 additions & 0 deletions thehive-templates/Shodan_Host_History_1_0/long.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<div class="panel panel-info" ng-if="success">
<div class="panel-heading">
Host Information of <strong>{{artifact.data | fang}}</strong>
</div>
<div class="panel-body">
<dl class="dl-horizontal">
<dt>ASN</dt>
<dd>{{content.host.asn || '-'}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>ISP</dt>
<dd>{{content.host.isp || '-'}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Hostnames</dt>
<dd>{{content.host.hostnames.join(', ') || '-'}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Country</dt>
<dd>{{content.host.country_name || '-'}} (<strong>{{content.host.country_code || '-'}}</strong>)</dd>
</dl>
<dl class="dl-horizontal">
<dt>Ports</dt>
<dd>{{content.host.ports.join(', ') || '-'}}</dd>
</dl>
<dl class="dl-horizontal">
<dt>Last update</dt>
<dd>{{content.host.last_update || '-'}}</dd>
</dl>

<dl class="dl-horizontal">
<dt>Geolocation</dt>
<dd>
<strong>Latitude:</strong> {{content.host.latitude || '-'}}, <strong>Longitude:</strong> {{content.host.longitude || '-'}} <br>
<a ng-href="http://google.com/maps/place/{{content.host.latitude}},{{content.host.longitude}}" target="_blank">Open in Google Maps</a>
</dd>
</dl>
</div>
</div>

<div class="panel panel-danger" ng-if="!success">
<div class="panel-heading">
<strong>{{artifact.data | fang}}</strong>
</div>
<div class="panel-body">
{{content.errorMessage}}
</div>
</div>
3 changes: 3 additions & 0 deletions thehive-templates/Shodan_Host_History_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>
3 changes: 3 additions & 0 deletions thehive-templates/Shodan_InfoDomain_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>
22 changes: 22 additions & 0 deletions thehive-templates/Shodan_ReverseDNS_1_0/long.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div class="panel panel-info" ng-if="success">
<div class="panel-heading">
DNS resolutions of <strong>{{artifact.data | fang}}</strong>
</div>
<div class="panel-body">
<dl class="dl-horizontal" ng-repeat="(ip, data) in content.records">
<dd ng-repeat="domain in data">
{{ip}}: {{domain}}
</dd>
</dl>

</div>
</div>

<div class="panel panel-danger" ng-if="!success">
<div class="panel-heading">
<strong>{{artifact.data | fang}}</strong>
</div>
<div class="panel-body">
{{content.errorMessage}}
</div>
</div>
3 changes: 3 additions & 0 deletions thehive-templates/Shodan_ReverseDNS_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>
Loading

0 comments on commit 2166975

Please sign in to comment.