/
shodancmd.py
146 lines (138 loc) · 6.91 KB
/
shodancmd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#! /usr/bin/env python
import sys
import json
import shodan
from harpoon.commands.base import Command
from dateutil.parser import parse
class CommandShodan(Command):
"""
# Shodan
**Queries information from shodan.io API***
* Get information on an IP : `harpoon shodan ip IP`
* Get summary (only ports 22, 80 and 443) of historical data on an ip : `harpoon shodan ip -H -s IP`
* Get raw json of historical data : `harpoon shodan ip -H -v IP`
* Search in the database: `harpoon shodan search SEARCH`
"""
name = "shodan"
description = "Requests Shodan API"
config = {'Shodan': ['key']}
def add_arguments(self, parser):
subparsers = parser.add_subparsers(help='Subcommand')
parser_a = subparsers.add_parser('ip', help='Get information on an IP address')
parser_a.add_argument('IP', help='IP to be searched')
parser_a.add_argument('--history', '-H', action='store_true',
help='Also display historical information')
parser_a.add_argument('-v', '--verbose', action='store_true',
help="Verbose mode (display raw json)")
parser_a.add_argument('-s', '--summary', action='store_true',
help="Only display information for ports 22, 80 and 443")
parser_a.set_defaults(subcommand='ip')
parser_b = subparsers.add_parser('search', help='Search in shodan')
parser_b.add_argument('QUERY', help='Query')
parser_b.set_defaults(subcommand='search')
parser_c = subparsers.add_parser('ssh', help='Write ssh history from Shodan historical data')
parser_c.add_argument('IP', help='IP address')
parser_c.set_defaults(subcommand='ssh')
self.parser = parser
def run(self, conf, args, plugins):
if 'subcommand' in args:
if 'Shodan' not in conf and 'key' not in conf['Shodan']:
print('Bad configuration for Shodan, quitting...')
sys.exit(1)
api = shodan.Shodan(conf['Shodan']['key'])
if args.subcommand == 'ip':
try:
res = api.host(args.IP, history=args.history)
except shodan.exception.APIError:
print("IP not found in Shodan")
else:
if args.verbose:
print(json.dumps(res, sort_keys=True, indent=4))
else:
if args.summary:
for d in res['data']:
if d['port'] == 22:
print("%s - port 22 ssh - %s" % (
d['timestamp'][:19],
d['data'].split("\n")[0]
)
)
elif d['port'] == 80:
print("%s - port 80 http - Server \"%s\"" % (
d['timestamp'][:19],
d['http']['server']
)
)
elif d['port'] == 443:
if 'cert' in d['ssl']:
print("%s - port 443 https - Cert \"%s\" \"%s\" %s - Server \"%s\"" % (
d['timestamp'][:19],
d['ssl']['cert']['subject']['CN'],
d['ssl']['cert']['issuer']['CN'],
d['ssl']['cert']['fingerprint']['sha1'],
d['http']['server']
)
)
else:
print("%s - port 443 https - Cert Unknown- Server \"%s\"" % (
d['timestamp'][:19],
d['http']['server']
)
)
else:
for d in res['data']:
print(d['timestamp'])
print(d['_shodan']['module'])
print("%s/%i" % (d['transport'], d['port']))
print(d['data'])
if 'html' in d:
print(d['html'][:2000])
if 'http' in d:
print(json.dumps(d['http'])[:3000])
print('')
elif args.subcommand == 'search':
res = api.search(args.QUERY)
print('%i results' % res['total'])
for r in res['matches']:
print('[+] %s (%s): port %s/%i -> %s\n' % (
r['ip_str'],
r['org'],
r['transport'],
r['port'],
r['data'][:1000]
)
)
elif args.subcommand == 'ssh':
data = {}
try:
res = api.host(args.IP, history=True)
except shodan.exception.APIError:
print("IP not found in Shodan")
else:
for event in res['data']:
if event['_shodan']['module'] == 'ssh':
if 'ssh' in event:
fingerprint = event['ssh']['fingerprint']
date = parse(event['timestamp'])
if fingerprint not in data:
data[fingerprint] = {
'first': date,
'last': date,
'fingerprint': fingerprint
}
else:
if data[fingerprint]['first'] > date:
data[fingerprint]['first'] = date
if data[fingerprint]['last'] < date:
data[fingerprint]['last'] = date
for val in sorted(data.values(), key=lambda x:x['first']):
print('%s - %s -> %s' % (
val['fingerprint'],
val['first'].strftime('%Y-%m-%d'),
val['last'].strftime('%Y-%m-%d')
)
)
else:
self.parser.print_help()
else:
self.parser.print_help()