-
Notifications
You must be signed in to change notification settings - Fork 542
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tsv output format for shodan host (#65)
- Loading branch information
John Matherly
committed
Aug 17, 2018
1 parent
743b445
commit a3e5855
Showing
3 changed files
with
126 additions
and
93 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
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
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,121 @@ | ||
# Helper methods for printing `host` information to the terminal. | ||
import click | ||
|
||
from shodan.helpers import get_ip | ||
|
||
|
||
def host_print_pretty(host, history=False): | ||
"""Show the host information in a user-friendly way and try to include | ||
as much relevant information as possible.""" | ||
# General info | ||
click.echo(click.style(get_ip(host), fg='green')) | ||
if len(host['hostnames']) > 0: | ||
click.echo(u'{:25s}{}'.format('Hostnames:', ';'.join(host['hostnames']))) | ||
|
||
if 'city' in host and host['city']: | ||
click.echo(u'{:25s}{}'.format('City:', host['city'])) | ||
|
||
if 'country_name' in host and host['country_name']: | ||
click.echo(u'{:25s}{}'.format('Country:', host['country_name'])) | ||
|
||
if 'os' in host and host['os']: | ||
click.echo(u'{:25s}{}'.format('Operating System:', host['os'])) | ||
|
||
if 'org' in host and host['org']: | ||
click.echo(u'{:25s}{}'.format('Organization:', host['org'])) | ||
|
||
if 'last_update' in host and host['last_update']: | ||
click.echo('{:25s}{}'.format('Updated:', host['last_update'])) | ||
|
||
click.echo('{:25s}{}'.format('Number of open ports:', len(host['ports']))) | ||
|
||
# Output the vulnerabilities the host has | ||
if 'vulns' in host and len(host['vulns']) > 0: | ||
vulns = [] | ||
for vuln in host['vulns']: | ||
if vuln.startswith('!'): | ||
continue | ||
if vuln.upper() == 'CVE-2014-0160': | ||
vulns.append(click.style('Heartbleed', fg='red')) | ||
else: | ||
vulns.append(click.style(vuln, fg='red')) | ||
|
||
if len(vulns) > 0: | ||
click.echo('{:25s}'.format('Vulnerabilities:'), nl=False) | ||
|
||
for vuln in vulns: | ||
click.echo(vuln + '\t', nl=False) | ||
|
||
click.echo('') | ||
|
||
click.echo('') | ||
|
||
# If the user doesn't have access to SSL/ Telnet results then we need | ||
# to pad the host['data'] property with empty banners so they still see | ||
# the port listed as open. (#63) | ||
if len(host['ports']) != len(host['data']): | ||
# Find the ports the user can't see the data for | ||
ports = host['ports'] | ||
for banner in host['data']: | ||
if banner['port'] in ports: | ||
ports.remove(banner['port']) | ||
|
||
# Add the placeholder banners | ||
for port in ports: | ||
banner = { | ||
'port': port, | ||
'transport': 'tcp', # All the filtered services use TCP | ||
'timestamp': host['data'][-1]['timestamp'], # Use the timestamp of the oldest banner | ||
'placeholder': True, # Don't store this banner when the file is saved | ||
} | ||
host['data'].append(banner) | ||
|
||
click.echo('Ports:') | ||
for banner in sorted(host['data'], key=lambda k: k['port']): | ||
product = '' | ||
version = '' | ||
if 'product' in banner and banner['product']: | ||
product = banner['product'] | ||
if 'version' in banner and banner['version']: | ||
version = '({})'.format(banner['version']) | ||
|
||
click.echo(click.style('{:>7d}'.format(banner['port']), fg='cyan'), nl=False) | ||
click.echo('/', nl=False) | ||
click.echo(click.style('{} '.format(banner['transport']), fg='yellow'), nl=False) | ||
click.echo('{} {}'.format(product, version), nl=False) | ||
|
||
if history: | ||
# Format the timestamp to only show the year-month-day | ||
date = banner['timestamp'][:10] | ||
click.echo(click.style('\t\t({})'.format(date), fg='white', dim=True), nl=False) | ||
click.echo('') | ||
|
||
# Show optional ssl info | ||
if 'ssl' in banner: | ||
if 'versions' in banner['ssl'] and banner['ssl']['versions']: | ||
click.echo('\t|-- SSL Versions: {}'.format(', '.join([version for version in sorted(banner['ssl']['versions']) if not version.startswith('-')]))) | ||
if 'dhparams' in banner['ssl'] and banner['ssl']['dhparams']: | ||
click.echo('\t|-- Diffie-Hellman Parameters:') | ||
click.echo('\t\t{:15s}{}\n\t\t{:15s}{}'.format('Bits:', banner['ssl']['dhparams']['bits'], 'Generator:', banner['ssl']['dhparams']['generator'])) | ||
if 'fingerprint' in banner['ssl']['dhparams']: | ||
click.echo('\t\t{:15s}{}'.format('Fingerprint:', banner['ssl']['dhparams']['fingerprint'])) | ||
|
||
|
||
def host_print_tsv(host, history=False): | ||
"""Show the host information in a succinct, grep-friendly manner.""" | ||
for banner in sorted(host['data'], key=lambda k: k['port']): | ||
click.echo(click.style('{:>7d}'.format(banner['port']), fg='cyan'), nl=False) | ||
click.echo('\t', nl=False) | ||
click.echo(click.style('{} '.format(banner['transport']), fg='yellow'), nl=False) | ||
|
||
if history: | ||
# Format the timestamp to only show the year-month-day | ||
date = banner['timestamp'][:10] | ||
click.echo(click.style('\t({})'.format(date), fg='white', dim=True), nl=False) | ||
click.echo('') | ||
|
||
|
||
HOST_PRINT = { | ||
'pretty': host_print_pretty, | ||
'tsv': host_print_tsv, | ||
} |