-
Notifications
You must be signed in to change notification settings - Fork 385
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'BSI-CERT-Bund-feature/censys_analyzer' into develop
- Loading branch information
Showing
5 changed files
with
277 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,12 @@ | ||
{ | ||
"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 hashes 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,142 @@ | ||
<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"> | ||
<div class="ip-results" ng-if="content.ip"> | ||
<dl class="dl-horizontal"> | ||
<dt class="text-bold">IP</dt> | ||
<dd>{{content.ip.ip}}</dd> | ||
</dl> | ||
<dl class="dl-horizontal"> | ||
<dt class="text-bold">Last update</dt> | ||
<dd>{{content.ip.updated_at}}</dd> | ||
</dl> | ||
<dl class="dl-horizontal"> | ||
<dt class="text-bold">Location</dt> | ||
<dd> | ||
{{content.ip.location.continent}} - {{content.ip.location.country}} - {{content.ip.location.province}} - {{content.ip.location.city}} | ||
</dd> | ||
</dl> | ||
<dl class="dl-horizontal"> | ||
<dt class="text-bold">AS</dt> | ||
<dd>{{content.ip.autonomous_system.asn}}: {{content.ip.autonomous_system.name}}</dd> | ||
</dl> | ||
<div ng-repeat="protocol in content.ip.ports"> | ||
<dl class="dl-horizontal"> | ||
<dt>Info on port {{protocol}}</dt> | ||
<dd> | ||
<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}}"> | ||
<pre style="overflow-x:scroll;">{{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> | ||
</dd> | ||
</dl> | ||
</div> | ||
</div> | ||
<div class="hash-results" ng-if="content.cert"> | ||
<dl class="dl-horizontal"> | ||
<dt>Metadata</dt> | ||
<dd> | ||
<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> | ||
</dd> | ||
</dl> | ||
<dl class="dl-horizontal"> | ||
<dt>Added to CT</dt> | ||
<dd> | ||
<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> | ||
</dd> | ||
</dl> | ||
<dl class="dl-horizontal"> | ||
<dt>Issuer</dt> | ||
<dd> | ||
<p ng-repeat="cn in content.cert.parsed.issuer.common_name">{{cn}}</p> | ||
</dd> | ||
</dl> | ||
<dl class="dl-horizontal"> | ||
<dt>Validity</dt> | ||
<dd> | ||
<p>Valid since: {{content.cert.parsed.validity.start}}</p> | ||
<p>Valid until: {{content.cert.parsed.validity.end}}</p> | ||
</dd> | ||
</dl> | ||
<dl class="dl-horizontal"> | ||
<dt>Full certificate data</dt> | ||
<dd> | ||
<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 style="overflow-x:scroll;">{{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> | ||
</dd> | ||
</tr> | ||
</div> | ||
<div class="website-results" ng-if="content.website"> | ||
|
||
<dl class="dl-horizontal"> | ||
<dt>Domain</dt> | ||
<dd>{{content.website.domain}}</dd> | ||
</dl> | ||
<dl class="dl-horizontal"> | ||
<dt>Last update</dt> | ||
<dd>{{content.website.updated_at}}</dd> | ||
</dl> | ||
<dl class="dl-horizontal"> | ||
<dt>Location</dt> | ||
<dd> | ||
{{content.website.location.continent}} - {{content.website.location.country}} - {{content.website.location.province}} - {{content.website.location.city}} | ||
</dd> | ||
</dl> | ||
<dl class="dl-horizontal"> | ||
<dt>AS</dt> | ||
<dd>{{content.website.autonomous_system.asn}}: {{content.website.autonomous_system.name}}</dd> | ||
</dl> | ||
<dl class="dl-horizontal" ng-repeat="protocol in content.website.ports"> | ||
<dt>Info on port {{protocol}}</dt> | ||
<dd> | ||
<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 style="overflow-x:scroll;">{{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> | ||
</dd> | ||
</dl> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<!-- General error --> | ||
<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> |
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> |