Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp Shodan analyzer #328

Merged
merged 1 commit into from
Dec 4, 2018
Merged
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
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