-
Notifications
You must be signed in to change notification settings - Fork 384
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added first version of censys.io analyzer
- Loading branch information
Showing
5 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"name": "Censys", | ||
"author": "Nils Kuhnert, CERT-Bund", | ||
"license": "AGPL-V3", | ||
"url": "https://github.com/BSI-CERT-Bund/censys-analyzer", | ||
"version": "1.0", | ||
"baseConfig": "Censys", | ||
"config": {}, | ||
"description": "Check IPs, certificate hashs or domains against censys.io.", | ||
"dataTypeList": ["ip", "hash", "domain"], | ||
"command": "Censys/censys_analyzer.py" | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
#!/usr/bin/env python3 | ||
from cortexutils.analyzer import Analyzer | ||
from censys.certificates import CensysCertificates | ||
from censys.ipv4 import CensysIPv4 | ||
from censys.websites import CensysWebsites | ||
from censys.base import CensysNotFoundException, CensysRateLimitExceededException, CensysUnauthorizedException | ||
|
||
|
||
class CensysAnalyzer(Analyzer): | ||
def __init__(self): | ||
Analyzer.__init__(self) | ||
|
||
self.__uid = self.get_param( | ||
'config.uid', | ||
None, | ||
'No UID for Censys given. Please add it to the cortex configuration.' | ||
) | ||
self.__api_key = self.get_param( | ||
'config.key', | ||
None, | ||
'No API-Key for Censys given. Please add it to the cortex configuration.' | ||
) | ||
|
||
def search_hosts(self, ip): | ||
""" | ||
Searches for a host using its ipv4 address | ||
:param ip: ipv4 address as string | ||
:type ip: str | ||
:return: dict | ||
""" | ||
c = CensysIPv4(api_id=self.__uid, api_secret=self.__api_key) | ||
return c.view(ip) | ||
|
||
def search_certificate(self, hash): | ||
""" | ||
Searches for a specific certificate using its hash | ||
:param hash: certificate hash | ||
:type hash: str | ||
:return: dict | ||
""" | ||
c = CensysCertificates(api_id=self.__uid, api_secret=self.__api_key) | ||
return c.view(hash) | ||
|
||
def search_website(self, dom): | ||
""" | ||
Searches for a website using the domainname | ||
:param dom: domain | ||
:type dom: str | ||
:return: dict | ||
""" | ||
c = CensysWebsites(api_id=self.__uid, api_secret=self.__api_key) | ||
return c.view(dom) | ||
|
||
def run(self): | ||
try: | ||
if self.data_type == 'ip': | ||
self.report({ | ||
'ip': self.search_hosts(self.get_data()) | ||
}) | ||
elif self.data_type == 'hash': | ||
self.report({ | ||
'cert': self.search_certificate(self.get_data()) | ||
}) | ||
elif self.data_type == 'domain' or self.data_type == 'fqdn': | ||
self.report({ | ||
'website': self.search_website(self.get_data()) | ||
}) | ||
else: | ||
self.error('Data type not supported. Please use this analyzer with data types hash, ip or domain.') | ||
except CensysNotFoundException: | ||
self.error('{} not found.'.format(self.get_data())) | ||
except CensysUnauthorizedException: | ||
self.error('Censys raised NotAuthorizedException. Please check your credentials.') | ||
except CensysRateLimitExceededException: | ||
self.error('Rate limit exceeded.') | ||
|
||
def summary(self, raw): | ||
taxonomies = [] | ||
if 'ip' in raw: | ||
raw = raw['ip'] | ||
service_count = len(raw.get('protocols', [])) | ||
heartbleed = raw.get('443', {}).get('https', {}).get('heartbleed', {}).get('heartbleed_vulnerable', False) | ||
|
||
taxonomies.append(self.build_taxonomy('info', 'Censys', 'OpenServices', service_count)) | ||
if heartbleed: | ||
taxonomies.append(self.build_taxonomy('malicious', 'Censys', 'Heartbleed', 'vulnerable')) | ||
elif 'website' in raw: | ||
raw = raw['website'] | ||
service_count = len(raw.get('tags', [])) | ||
|
||
taxonomies.append(self.build_taxonomy('info', 'Censys', 'OpenServices', service_count)) | ||
elif 'cert' in raw: | ||
raw = raw['cert'] | ||
trusted_count = len(raw.get('validation', [])) | ||
validator_count = len(raw.get('validation', [])) | ||
|
||
for _, validator in raw.get('validation', []).items(): | ||
if validator.get('blacklisted', False) or \ | ||
validator.get('in_revocation_set', False) or \ | ||
(not validator.get('whitelisted', False) and not validator.get('valid', False)): | ||
trusted_count -= 1 | ||
if trusted_count < validator_count: | ||
taxonomies.append(self.build_taxonomy('suspicious', 'Censys', 'TrustedCount', '{}/{}'.format( | ||
trusted_count, validator_count | ||
))) | ||
else: | ||
taxonomies.append(self.build_taxonomy('info', 'Censys', 'TrustedCount', '{}/{}'.format( | ||
trusted_count, validator_count | ||
))) | ||
return { | ||
'taxonomies': taxonomies | ||
} | ||
|
||
|
||
if __name__ == '__main__': | ||
CensysAnalyzer().run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
cortexutils | ||
censys |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
<div class="panel panel-info" ng-if="success"> | ||
<div class="panel-heading"> | ||
Censys.io information for <strong>{{artifact.data}}</strong> | ||
</div> | ||
<div class="panel-body"> | ||
<dl class="dl-horizontal"> | ||
<dt>Results:</dt> | ||
<dd> | ||
<div id="ip-results" ng-if="content.ip"> | ||
<table class="table"> | ||
<thead> | ||
<tr> | ||
<th>Key</th> | ||
<th>Value</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>IP</td> | ||
<td>{{content.ip.ip}}</td> | ||
</tr> | ||
<tr> | ||
<td>Last update</td> | ||
<td>{{content.ip.updated_at}}</td> | ||
</tr> | ||
<tr> | ||
<td>Location</td> | ||
<td> | ||
{{content.ip.location.continent}} - | ||
{{content.ip.location.country}} - | ||
{{content.ip.location.province}} - | ||
{{content.ip.location.city}} | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>AS</td> | ||
<td>{{content.ip.autonomous_system.asn}}: {{content.ip.autonomous_system.name}}</td> | ||
</tr> | ||
<tr ng-repeat="protocol in content.ip.ports"> | ||
<td>Info on port {{protocol}}</td> | ||
<td> | ||
<button class="btn btn-primary" | ||
type="button" | ||
data-toggle="collapse" | ||
data-target="#collapse-{{protocol}}" | ||
aria-expanded="false" | ||
aria-controls="collapse-{{protocol}}"> | ||
Show/hide | ||
</button> | ||
<br /> | ||
<div class="collapse" id="collapse-{{protocol}}"> | ||
<div class="card card-body"> | ||
<pre>{{content.ip[protocol] | json}}</pre> | ||
<button class="btn btn-primary" | ||
type="button" | ||
data-toggle="collapse" | ||
data-target="#collapse-{{protocol}}" | ||
aria-expanded="false" | ||
aria-controls="collapse-{{protocol}}"> | ||
Show/hide | ||
</button> | ||
</div> | ||
</div> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> | ||
<div id="hash-results" ng-if="content.cert"> | ||
<table class="table"> | ||
<thead> | ||
<tr> | ||
<th>Key</th> | ||
<th>Value</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>Metadata</td> | ||
<td> | ||
<p>Source: {{content.cert.metadata.source}}</p> | ||
<p>Added at: {{content.cert.metadata.added_at}}</p> | ||
<p>Updated at: {{content.cert.metadata.updated_at}}</p> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>Added to CT</td> | ||
<td> | ||
<p>Comodo Mammoth<br />{{content.cert.ct.comodo_mammoth.added_to_ct_at}}</p> | ||
<p>Comodo Sabre<br />{{content.cert.ct.comodo_sabre.added_to_ct_at}}</p> | ||
<p>Google Pilot<br />{{content.cert.ct.google_pilot.added_to_ct_at}}</p> | ||
<p>Google Rocketeer<br />{{content.cert.ct.google_rocketeer.added_to_ct_at}}</p> | ||
<p>Symantec WS CT<br />{{content.cert.ct.symantec_ws_ct.added_to_ct_at}}</p> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>Issuer</td> | ||
<td> | ||
<p ng-repeat="cn in content.cert.parsed.issuer.common_name">{{cn}}</p> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>Validity</td> | ||
<td> | ||
<p>Valid since: {{content.cert.parsed.validity.start}}</p> | ||
<p>Valid until: {{content.cert.parsed.validity.end}}</p> | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>Full certificate data</td> | ||
<td> | ||
<button class="btn btn-primary" | ||
type="button" | ||
data-toggle="collapse" | ||
data-target="#collapse-certdata" | ||
aria-expanded="false" | ||
aria-controls="collapse-certdata"> | ||
Show/hide | ||
</button> | ||
<br /> | ||
<div class="collapse" id="collapse-certdata"> | ||
<div class="card card-body"> | ||
<pre>{{content.cert | json}}</pre> | ||
<button class="btn btn-primary" | ||
type="button" | ||
data-toggle="collapse" | ||
data-target="#collapse-certdata" | ||
aria-expanded="false" | ||
aria-controls="collapse-certdata"> | ||
Show/hide | ||
</button> | ||
</div> | ||
</div> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> | ||
<div id="website-results" ng-if="content.website"> | ||
<table class="table"> | ||
<thead> | ||
<tr> | ||
<th>Key</th> | ||
<th>Value</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>Domain</td> | ||
<td>{{content.website.domain}}</td> | ||
</tr> | ||
<tr> | ||
<td>Last update</td> | ||
<td>{{content.website.updated_at}}</td> | ||
</tr> | ||
<tr> | ||
<td>Location</td> | ||
<td> | ||
{{content.website.location.continent}} - | ||
{{content.website.location.country}} - | ||
{{content.website.location.province}} - | ||
{{content.website.location.city}} | ||
</td> | ||
</tr> | ||
<tr> | ||
<td>AS</td> | ||
<td>{{content.website.autonomous_system.asn}}: {{content.website.autonomous_system.name}}</td> | ||
</tr> | ||
<tr ng-repeat="protocol in content.website.ports"> | ||
<td>Info on port {{protocol}}</td> | ||
<td> | ||
<button class="btn btn-primary" | ||
type="button" | ||
data-toggle="collapse" | ||
data-target="#collapse-{{protocol}}" | ||
aria-expanded="false" | ||
aria-controls="collapse-{{protocol}}"> | ||
Show/hide | ||
</button> | ||
<br /> | ||
<div class="collapse" id="collapse-{{protocol}}"> | ||
<div class="card card-body"> | ||
<pre>{{content.website[protocol] | json}}</pre> | ||
<button class="btn btn-primary" | ||
type="button" | ||
data-toggle="collapse" | ||
data-target="#collapse-{{protocol}}" | ||
aria-expanded="false" | ||
aria-controls="collapse-{{protocol}}"> | ||
Show/hide | ||
</button> | ||
</div> | ||
</div> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> | ||
</dd> | ||
</dl> | ||
</div> | ||
</div> | ||
|
||
<!-- General error --> | ||
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |