-
Notifications
You must be signed in to change notification settings - Fork 122
/
Copy pathseitan.py
361 lines (293 loc) · 10.5 KB
/
seitan.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
#!/usr/bin/env python3
"""
seitan.py 0.1 - OSINT Tool Built on Shodan.io API Search
Copyright (c) 2017 Marco Ivaldi <[email protected]>
"The Other Way to Pen-Test" --HD Moore & Valsmith
Seitan is a Python script that uses the Shodan.io API
search to collect open source intelligence on targets.
The following attacks are currently supported:
ipaddr: view all available information for an IP address
domain: search services related to a domain or host name
In order to use this tool you will need a valid API key
from https://account.shodan.io/, the shodan-python
library, and the netaddr module:
$ pip3 install shodan
$ pip3 install netaddr
Example usage:
$ ./seitan.py ipaddr -f targets.txt # ipaddr scan
$ ./seitan.py domain -t test.com # domain scan
$ ./seitan.py domain -t test.com -S # domain scan (ssl)
TODO:
Implement additional search methods (e.g. Amazon S3)
Expand information displayed for SSL/TLS services
Add download/parse/convert capabilities
Add on-demand scanning functionality (Shodan.scan)
Get the latest version at:
https://github.com/0xdea/tactical-exploitation/
"""
VERSION = "0.1"
BANNER = """
seitan.py {0} - OSINT Tool Built on Shodan.io API Search
Copyright (c) 2017 Marco Ivaldi <[email protected]>
""".format(VERSION)
# fill in with your own api key from https://account.shodan.io/
SHODAN_API_KEY = ""
import sys
import argparse
import time
import netaddr
import shodan
def init(api_key):
"""
Initialize the Shodan API
"""
# load api key and print credits
api = shodan.Shodan(SHODAN_API_KEY)
info(api)
return api
def info(api):
"""
Print currently available credits
"""
info = api.info()
print("Active plan:\t{}".format(info["plan"]))
print("Query credits:\t{}".format(info["query_credits"]))
print("Scan credits:\t{}\n".format(info["scan_credits"]))
def search(api, search_str, limit):
"""
Search with Shodan API
"""
try:
res = api.search(search_str, limit=limit)
for banner in res["matches"]:
# header
print("[", end="")
if "ip_str" in banner and banner["ip_str"]:
print(banner["ip_str"], end=", ")
if "hostnames" in banner and banner["hostnames"]:
for hostname in banner["hostnames"]:
print(hostname, end=", ")
if "os" in banner and banner["os"]:
print(banner["os"], end=", ")
if "port" in banner and banner["port"]:
print(banner["port"], end=", ")
if "timestamp" in banner and banner["timestamp"]:
date = banner["timestamp"][:10]
print(date, end="")
print("]\n")
# service information
if "ssl" in banner and banner["ssl"]["cert"]["subject"]:
b = banner["ssl"]["cert"]["subject"]
for field in b:
print("{}: {}".format(field, b[field]))
print()
if "data" in banner and banner["data"]:
print(banner["data"].rstrip(), end="\n\n")
except shodan.APIError as err:
print("\t// error: {}\n".format(err))
return 0
return res["total"]
def ipaddr(args):
"""
View all available information for an IP address (based on Shodan CLI tool)
"""
api = init(SHODAN_API_KEY)
targets = get_targets(args)
history = args.history
minify = args.minify
found_glob = 0
for entry in targets:
# try to resolve cidr
try:
net = netaddr.IPNetwork(entry)
except (KeyboardInterrupt, SystemExit):
sys.exit(1)
except Exception as err:
print("// error: {}\n".format(err))
continue
print("*** Scanning target IP network range {} ***".format(entry))
found = 0
# scan the target cidr
for ip in net:
ip = str(ip)
print("\n{}".format(ip))
try:
res = api.host(ip, history=history, minify=minify)
# general information
if len(res["hostnames"]):
print("\t{:25s}{}"
.format("Hostnames:", ";".join(res["hostnames"])))
if "org" in res and res["org"]:
print("\t{:25s}{}".format("Organization:", res["org"]))
if "city" in res and res["city"]:
print("\t{:25s}{}".format("City:", res["city"]))
if "country_name" in res and res["country_name"]:
print("\t{:25s}{}".format("Country:", res["country_name"]))
if "os" in res and res["os"]:
print("\t{:25s}{}".format("Operating System:", res["os"]))
# vulnerability information
if "vulns" in res and len(res["vulns"]):
vulns = []
for vuln in res["vulns"]:
if vuln.startswith("!"): continue
vulns.append(vuln)
if len(vulns):
print("\t{:25s}".format("Vulnerabilities:"), end="")
for vuln in vulns:
print(vuln + "\t", end="")
print()
# service information
print("\t{:25s}{}"
.format("Number of open ports:", len(res["ports"])))
if not minify:
print("\tPorts:")
for banner in sorted(
res["data"],
key=lambda k: (k["port"], k["timestamp"])
):
product = ""
if "product" in banner and banner["product"]:
product = banner["product"]
version = ""
if "version" in banner and banner["version"]:
version = "({})".format(banner["version"])
print("\t{:>7d}".format(banner["port"]), end=" ")
if "timestamp" in banner and banner["timestamp"]:
date = banner["timestamp"][:10]
print("\t{}".format(date), end="")
print("\t{} {}".format(product, version), end="")
print()
except shodan.APIError as err:
print("\t// error: {}".format(err))
else:
found += 1
# throttle requests to work around shodan request limit
time.sleep(1)
found_glob += found
print("\n*** {} interesting addresses found on {} ***\n"
.format(found, entry))
print("*** {} interesting addresses found globally ***\n"
.format(found_glob))
info(api)
def domain(args):
"""
Search services related to a domain or host name
"""
api = init(SHODAN_API_KEY)
targets = get_targets(args)
limit = args.l
ssl = args.ssl
# scan the target domains
for dom in targets:
# ssl cert based search
if ssl:
search_str = "ssl:{}".format(dom)
print("*** Scanning target domain {} (ssl cert based) ***\n"
.format(dom))
# hostname based search
else:
search_str = "hostname:{}".format(dom)
print("*** Scanning target domain {} (hostname based) ***\n"
.format(dom))
# enforce hard limit of 1000 results (100 pages)
if limit > 1000:
limit = 1000
print("// warning: enforcing a hard limit of 1000 results\n")
# perform the actual search
count = search(api, search_str, limit)
# throttle requests to work around shodan request limit
time.sleep(1)
if limit > count:
limit = count
print("*** Displayed {} results out of {} for domain {} ***\n"
.format(limit, count, dom))
info(api)
def get_targets(args):
"""
Get targets from command line or file
"""
if args.t: return [args.t]
return [t.rstrip() for t in args.f]
def get_args():
"""
Get command line arguments
"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(
title="commands",
help="choose action")
# ipaddr scan
parser_h = subparsers.add_parser(
"ipaddr",
help="ipaddr scan")
parser_h.set_defaults(func=ipaddr)
# ipaddr args
group_h_targets = parser_h.add_mutually_exclusive_group(required=True)
group_h_targets.add_argument(
"-t",
metavar="CIDR",
help="specify target network CIDR")
group_h_targets.add_argument(
"-f",
metavar="FILE",
type=argparse.FileType("r"),
help="specify file containing a list of CIDRs")
parser_h.add_argument(
"-H", "--history",
action="store_true",
help="include historical (non-current) services")
parser_h.add_argument(
"-M", "--minify",
action="store_true",
help="only return general information")
# domain scan
parser_d = subparsers.add_parser(
"domain",
help="domain scan")
parser_d.set_defaults(func=domain)
# domain args
group_d_targets = parser_d.add_mutually_exclusive_group(required=True)
group_d_targets.add_argument(
"-t",
metavar="DOMAIN",
help="specify target domain name")
group_d_targets.add_argument(
"-f",
metavar="FILE",
type=argparse.FileType("r"),
help="specify file containing a list of domain names")
parser_d.add_argument(
"-l",
metavar="LIMIT",
type=int,
default=100,
help="maximum number of results to return (default: 100)")
parser_d.add_argument(
"-S", "--ssl",
action="store_true",
help="search SSL Common Name instead of hostname")
if len(sys.argv) == 1:
parser.print_help()
sys.exit(0)
return parser.parse_args()
def main():
"""
Main function
"""
print(BANNER)
if sys.version_info[0] != 3:
print("// error: this script requires python 3")
sys.exit(1)
if not SHODAN_API_KEY:
print("// error: please fill in SHODAN_API_KEY")
sys.exit(1)
args = get_args()
try:
args.func(args)
except (KeyboardInterrupt, SystemExit):
sys.exit(1)
except Exception as err:
print("// error: {}".format(err))
sys.exit(1)
if __name__ == "__main__":
main()