-
Notifications
You must be signed in to change notification settings - Fork 73
/
omnibus-cli.py
693 lines (507 loc) · 19.9 KB
/
omnibus-cli.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
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
#!/usr/bin/env python
##
# The OSINT Omnibus
# --
# InQuest, LLC. (2018)
# https://github.com/InQuest/omnibus
# https://www.inquest.net
# --
# Please see docs directory for license information.
##
import os
import sys
import cmd2
import json
import argparse
from lib import common
from lib import storage
from lib import asciiart
from lib.mongo import Mongo
from lib.cache import RedisCache
from lib.dispatch import Dispatch
from lib.common import info
from lib.common import mkdir
from lib.common import error
from lib.common import running
from lib.common import success
from lib.common import warning
from lib.common import bold_msg
from lib.common import pp_json
from lib.common import lookup_key
from lib.common import detect_type
from lib.common import read_file
from lib.common import get_option
from lib.models import create_artifact
help_dict = {
'general': [
'help', 'history', 'quit', 'cat', 'apikey', 'banner', 'set', 'clear', 'artifacts', 'general',
'redirect', 'sessions', 'modules'
],
'artifacts': [
'new', 'cat', 'open', 'source', 'artifacts', 'delete'
],
'modules': [
'blockchain', 'clearbit', 'censys', 'csirtg', 'csirtg', 'cymon',
'dnsresolve', 'geoip', 'fullcontact', 'hackedemails', 'he', 'hibp',
'ipinfo', 'ipvoid', 'isc', 'keybase', 'machine', 'nmap', 'passivetotal',
'pgp', 'rss', 'shodan', 'threatcrowd',
'twitter', 'urlvoid', 'virustotal', 'web', 'whois'],
'sessions': [
'session', 'ls', 'rm', 'wipe'
]
}
class Console(cmd2.Cmd):
def __init__(self):
cmd2.Cmd.__init__(self,
completekey='tab',
persistent_history_file=get_option('core', 'hist_file', config),
persistent_history_length=int(get_option('core', 'hist_size', config)))
self.allow_cli_args = False
self.default_to_shell = False
self.intro = 'Welcome to the Omnibus shell! Type "session" to get started or "help" to view all commands.'
self.allow_redirection = True
self.prompt = 'omnibus >> '
self.redirector = '>'
self.quit_on_sigint = False
del cmd2.Cmd.do_alias
del cmd2.Cmd.do_edit
del cmd2.Cmd.do_eof
del cmd2.Cmd.do_shell
del cmd2.Cmd.do_eos
del cmd2.Cmd.do_load
del cmd2.Cmd.do_py
del cmd2.Cmd.do_pyscript
del cmd2.Cmd.do_shortcuts
del cmd2.Cmd.do_unalias
del cmd2.Cmd.do__relative_load
self.db = Mongo(config)
self.dispatch = Dispatch(self.db)
self.session = None
if DEBUG:
self.do_set('debug true')
def sigint_handler(self, signum, frame):
"""Ensure Redis DB is cleared before exiting application"""
pipe_proc = self.pipe_proc
if pipe_proc is not None:
pipe_proc.terminate()
if self.session is not None:
self.session.flush()
raise KeyboardInterrupt('Caught keyboard interrupt; quitting ...')
def default(self, arg):
"""Override default function for custom error message"""
if arg.startswith('#'):
return
error('Unknown command')
return
def do_quit(self, _):
"""Exit Omnibus shell."""
self._should_quit = True
if self.session is not None:
running('Clearing artifact cache ...')
self.session.flush()
warning('Closing Omnibus shell ...')
return self._STOP_AND_EXIT
def do_clear(self, arg):
"""Clear the console"""
os.system('clear')
def do_modules(self, arg):
"""Show module list"""
bold_msg('[ Modules ]')
for cmd in help_dict['modules']:
print(cmd)
def do_artifacts(self, arg):
"""Show artifact information and available commands"""
bold_msg('[ Artifacts ]')
for cmd in help_dict['artifacts']:
print(cmd)
def do_general(self, arg):
"""Show general commands"""
bold_msg('[ General Commands ]')
for cmd in help_dict['general']:
print(cmd)
def do_sessions(self, arg):
"""Show session commands"""
bold_msg('[ Session Commands ]')
for cmd in help_dict['sessions']:
print(cmd)
def do_redirect(self, arg):
""" Show redirection command help """
info('Omnibus supports command redirection to output files using the ">" character. For example, "cat host zeroharbor.org > zh.json" will pipe the output of the cat command to ./zh.json on disk.')
def do_banner(self, arg):
"""Display random ascii art banner"""
print(asciiart.show_banner())
def do_session(self, arg):
"""Open a new session"""
self.session = RedisCache(config)
if self.session.db is None:
error('Failed to connect to Redis back-end. Please ensure the Redis service is running')
else:
success('Opened new session')
def do_ls(self, arg):
"""View current sessions artifacts"""
if self.session is None:
warning('No active session')
return
count = 0
keys = self.session.db.scan_iter()
for key in keys:
value = self.session.get(key)
print('[%s] %s' % (key, value))
count += 1
info('Active Artifacts: %d' % count)
def do_wipe(self, arg):
"""Clear currently active artifacts """
if self.session is not None:
info('Clearing active artifacts from cache ...')
self.session.flush()
success('Artifact cache cleared')
else:
warning('No active session; start a new session by running the "session" command')
def do_rm(self, arg):
"""Remove artifact from session by ID
Usage: rm <session id>"""
try:
arg = int(arg)
except:
error('Artifact ID must be an integer')
return
if self.session is not None:
if self.session.exists(arg):
self.session.delete(arg)
success('Removed artifact from cache (%s)' % arg)
else:
warning('Unable to find artifact by ID (%s)' % arg)
else:
warning('No active session; start a new session by running the "session" command')
def do_new(self, arg):
"""Create a new artifact
Artifacts are created by their name. An IP address artifacts name would be the IP address itself,
an FQDN artifacts name is the domain name, and so on.
Usage: new <artifact name> """
artifact = create_artifact(arg)
if not self.db.exists(artifact.type, {'name': artifact.name}):
doc_id = self.db.insert_one(artifact.type, artifact)
if doc_id is not None:
success('Created new artifact (%s - %s)' % (artifact.name, artifact.type))
if self.session is None:
self.session = RedisCache(config)
self.session.set(1, artifact.name)
success('Opened new session')
print('Artifact ID: 1')
else:
count = 0
for key in self.session.db.scan_iter():
count += 1
_id = count + 1
self.session.set(_id, artifact.name)
print('Artifact ID: %s' % _id)
def do_delete(self, arg):
"""Remove artifact from database by name or ID
Usage: delete <name>
delete <session id>"""
is_key, value = lookup_key(self.session, arg)
if is_key and value is None:
error('Unable to find artifact key in session (%s)' % arg)
return
elif is_key and value is not None:
arg = value
else:
pass
artifact_type = detect_type(arg)
self.db.delete_one(artifact_type, {'name': arg})
def do_cat(self, arg):
"""View artifact details or list API keys
Usage: cat apikeys
cat <artifact name>"""
if arg == 'apikeys':
data = json.load(open(common.API_CONF, 'rb'))
print json.dumps(data, indent=2)
else:
is_key, value = lookup_key(self.session, arg)
if is_key and value is None:
error('Unable to find artifact key in session (%s)' % arg)
return
elif is_key and value is not None:
arg = value
else:
pass
artifact_type = detect_type(arg)
result = self.db.find(artifact_type, {'name': arg}, one=True)
if len(result) == 0:
info('No entry found for artifact (%s)' % arg)
else:
print json.dumps(result, indent=2, separators=(',', ':'))
def do_open(self, arg):
"""Load text file list of artifacts
Command will detect each line items artifact type, create the artifact,
and add it to the current session if there is one.
Usage: open <path/to/file.txt> """
if not os.path.exists(arg):
warning('Cannot find file on disk (%s)' % arg)
return
artifacts = read_file(arg, True)
for artifact in artifacts:
new_artifact = create_artifact(artifact)
if not self.db.exists(new_artifact.type, {'name': new_artifact.name}):
doc_id = self.db.insert_one(new_artifact.type, new_artifact)
if doc_id is not None:
success('Created new artifact (%s - %s)' % (artifact.name, artifact.type))
if self.session is None:
self.session = RedisCache(config)
self.session.set(1, arg)
success('Opened new session')
print('Artifact ID: 1')
else:
count = 0
for key in self.session.db.scan_iter():
count += 1
_id = count + 1
self.session.set(_id, arg)
print('Artifact ID: %s' % _id)
success('Finished loading artifact list')
def do_report(self, arg):
"""Save artifact report as JSON file
Usage: report <artifact name>
report <session id>"""
is_key, value = lookup_key(self.session, arg)
if is_key and value is None:
error('Unable to find artifact key in session (%s)' % arg)
return
elif is_key and value is not None:
arg = value
else:
pass
_type = detect_type(arg)
result = self.db.find(_type, {'name': arg}, one=True)
if len(result) == 0:
warning('No entry found for artifact (%s)' % arg)
else:
report = storage.JSON(data=result, file_path=output_dir)
report.save()
if os.path.exists(report.file_path):
success('Saved artifact report (%s)' % report.file_path)
else:
error('Failed to properly save report')
def do_machine(self, arg):
"""Run all modules available for an artifacts type
Usage: machine <artifact name>
machine <session id>"""
result = self.dispatch.machine(self.session, arg)
pp_json(result)
# def do_abusech(self, arg):
# """Search Abuse.ch for artifact details """
# pass
def do_blockchain(self, arg):
"""Search Blockchain.info for BTC address"""
result = self.dispatch.submit(self.session, 'blockchain', arg)
pp_json(result)
def do_clearbit(self, arg):
"""Search Clearbit for email address """
result = self.dispatch.submit(self.session, 'clearbit', arg)
pp_json(result)
def do_censys(self, arg):
"""Search Censys for IPv4 address """
result = self.dispatch.submit(self.session, 'censys', arg)
pp_json(result)
def do_csirtg(self, arg):
"""Search CSIRTG for hash information"""
result = self.dispatch.submit(self.session, 'csirtg', arg)
pp_json(result)
def do_cybercure(self, arg):
"""Check if IP intelligence exists at cybercure.ai"""
result = self.dispatch.submit(self.session, 'cybercure', arg)
pp_json(result)
def do_cymon(self, arg):
"""Search Cymon for host """
result = self.dispatch.submit(self.session, 'cymon', arg)
pp_json(result)
# def do_dnsbrute(self, arg):
# """Enumerate DNS subdomains of FQDN """
# pass
def do_dnsresolve(self, arg):
"""Retrieve DNS records for host """
result = self.dispatch.submit(self.session, 'dnsresolve', arg)
pp_json(result)
def do_geoip(self, arg):
"""Retrieve Geolocation details for host """
result = self.dispatch.submit(self.session, 'geoip', arg)
pp_json(result)
def do_fullcontact(self, arg):
"""Search FullContact for email address """
result = self.dispatch.submit(self.session, 'fullcontact', arg)
pp_json(result)
# def do_gist(self, arg):
# """Search Github Gist's for artifact as string """
# pass
# def do_gitlab(self, arg):
# """Check Gitlab for active username """
# pass
def do_github(self, arg):
"""Check GitHub for active username"""
result = self.dispatch.submit(self.session, 'github', arg)
pp_json(result)
def do_hackedemails(self, arg):
"""Check hacked-emails.com for email address"""
result = self.dispatch.submit(self.session, 'hackedemails', arg)
pp_json(result)
def do_he(self, arg):
"""Search Hurricane Electric for host"""
result = self.dispatch.submit(self.session, 'he', arg)
pp_json(result)
def do_hibp(self, arg):
"""Check HaveIBeenPwned for email address"""
result = self.dispatch.submit(self.session, 'hibp', arg)
pp_json(result)
def do_ipinfo(self, arg):
"""Retrieve ipinfo resutls for host"""
result = self.dispatch.submit(self.session, 'ipinfo', arg)
pp_json(result)
def do_ipvoid(self, arg):
"""Search IPVoid for host"""
result = self.dispatch.submit(self.session, 'ipvoid', arg)
pp_json(result)
def do_isc(self, arg):
"""Search SANS ISC for host"""
result = self.dispatch.submit(self.session, 'sans', arg)
pp_json(result)
def do_keybase(self, arg):
"""Search Keybase for active username"""
result = self.dispatch.submit(self.session, 'keybase', arg)
pp_json(result)
def do_monitor(self, arg):
"""Setup active monitors for RSS Feeds, Pastebin, Gist, and other services"""
pass
def do_mdl(self, arg):
"""Search Malware Domain List for host"""
pass
def do_nmap(self, arg):
"""Run NMap discovery scan against host"""
result = self.dispatch.submit(self.session, 'nmap', arg)
pp_json(result)
def do_otx(self, arg):
"""Search AlienVault OTX for host or hash artifacts"""
result = self.dispatch.submit(self.session, 'otx', arg)
pp_json(result)
def do_passivetotal(self, arg):
"""Search PassiveTotal for host"""
result = self.dispatch.submit(self.session, 'passivetotal', arg)
pp_json(result)
def do_pastebin(self, arg):
"""Search Pastebin for artifact as string"""
pass
def do_pgp(self, arg):
"""Search PGP records for email address or user"""
result = self.dispatch.submit(self.session, 'pgp', arg)
pp_json(result)
# def do_projecthp(self, arg):
# """Search Project Honeypot for host"""
# pass
# def do_reddit(self, arg):
# """Search Reddit for active username"""
# pass
def do_rss(self, arg):
"""Read latest from RSS feed
Usage: rss <feed url>"""
result = self.dispatch.submit(self.session, 'rss', arg, True)
pp_json(result)
# def do_securitynews(self, arg):
# """Get current cybersecurity headlines from Google News"""
# result = self.dispatch.submit(self.session, 'securitynews', arg, True)
# pp_json(result)
def do_shodan(self, arg):
"""Query Shodan for host"""
result = self.dispatch.submit(self.session, 'shodan', arg)
pp_json(result)
def do_source(self, arg):
"""Add source to given artifact or most recently added artifact if not specified
Usage: source # adds to last created artifact
source <artifact name|session id> # adds to specific artifact
"""
if arg == '':
last = self.session.receive('artifacts')
_type = detect_type(last)
else:
_type = detect_type(arg)
is_key, value = lookup_key(self.session, arg)
if is_key and value is None:
error('Unable to find artifact key in session (%s)' % arg)
return
elif is_key and value is not None:
arg = value
else:
pass
if self.db.exists(_type, {'name': last}):
self.db.update_one(_type, {'name': last}, {'source': arg})
success('Added source to artifact entry (%s: %s)' % (last, arg))
else:
warning('Failed to find last artifact in MongoDB. Run "new <artifact name>" before using the source command')
def do_threatcrowd(self, arg):
"""Search ThreatCrowd for host"""
result = self.dispatch.submit(self.session, 'threatcrowd', arg)
pp_json(result)
# def do_totalhash(self, arg):
# """Search TotalHash for host"""
# pass
def do_twitter(self, arg):
"""Get Twitter info for username"""
result = self.dispatch.submit(self.session, 'twitter', arg)
pp_json(result)
def do_urlvoid(self, arg):
"""Search URLVoid for domain name"""
result = self.dispatch.submit(self.session, 'urlvoid', arg)
pp_json(result)
# def do_usersearch(self, arg):
# """Search Usersearch.com for active usernames"""
# pass
def do_virustotal(self, arg):
"""Search VirusTotal for IPv4, FQDN, or Hash"""
result = self.dispatch.submit(self.session, 'virustotal', arg)
pp_json(result)
def do_vxvault(self, arg):
"""Search VXVault for IPv4 or FQDN"""
pass
def do_web(self, arg):
"""Fingerprint webserver"""
pass
def do_whois(self, arg):
"""Perform WHOIS lookup on host"""
result = self.dispatch.submit(self.session, 'whois', arg)
pp_json(result)
def do_whoismind(self, arg):
"""Search Whois Mind for domains associated to an email address"""
result = self.dispatch.submit(self.session, 'whoismind', arg)
pp_json(result)
if __name__ == '__main__':
global config
global api_keys
global output_dir
global DEBUG
os.system('clear')
print(asciiart.banners[1])
parser = argparse.ArgumentParser(description='Omnibus - https://github.com/InQuest/omnibus')
ob_group = parser.add_argument_group('cli options')
ob_group.add_argument('-o', '--output',
help='report output directory',
action='store',
default='%s/reports' % os.path.dirname(os.path.realpath(__file__)),
required=False)
ob_group.add_argument('-d', '--debug',
help='enable full traceback on exceptions',
action='store_true',
default=False,
required=False)
args = parser.parse_args()
config = '%s/etc/omnibus.conf' % os.path.dirname(os.path.realpath(__file__))
output_dir = args.output
DEBUG = args.debug
info('Using configuration file (%s) ...' % config)
info('Debug: %s' % DEBUG)
if os.path.exists(output_dir):
if not os.path.isdir(output_dir):
error('Specified report output location is not a directory; exiting ...')
sys.exit(1)
else:
info('Creating report output directory (%s) ...' % output_dir)
mkdir(output_dir)
console = Console()
console.cmdloop()