@@ -81,28 +81,177 @@ def close_connection(self):
81
81
"""Closes the connection to the local database file"""
82
82
self .db .close ()
83
83
84
+ class Sha256Hash (object ):
85
+ """Md5 hash handler class"""
86
+ def __init__ (self ):
87
+ self .osint_options = {}
88
+ self .api_db = Database ()
89
+ self .virustotal_api_key = self .api_db .get_api_key ("virustotal" )
90
+ if self .virustotal_api_key :
91
+ self .osint_options .update ({
92
+ "virustotal: malicious check" : self .virustotal_is_malicious ,
93
+ "virustotal: malware type" : self .virustotal_malware_type })
94
+
95
+ def is_sha256 (self , _input : str ):
96
+ """Validates if _input is an md5 hash"""
97
+ if validators .hashes .sha256 (_input ):
98
+ return True
99
+ return False
100
+
101
+ def virustotal_is_malicious (self , _hash :str ):
102
+ """Checks virustotal to see if sha256 has positive detections"""
103
+ try :
104
+ data = make_vt_api_request (
105
+ "https://www.virustotal.com/vtapi/v2/file/report" ,
106
+ self .virustotal_api_key ,
107
+ {"resource" : _hash }
108
+ )
109
+ if data :
110
+ if data .json ().get ("response_code" ) == 0 :
111
+ return ["no report available" ]
112
+ return ["hash malicious: {} detections" .format (data .json ().get ("positives" ))]
113
+ else :
114
+ return ["no data available" ]
115
+ except Exception as e :
116
+ return ["virustotal api error: " , e ]
117
+
118
+ def virustotal_malware_type (self , _hash :str ):
119
+ """Checks virustotal to return malware types detected by scans"""
120
+ try :
121
+ data = make_vt_api_request (
122
+ "https://www.virustotal.com/vtapi/v2/file/report" ,
123
+ self .virustotal_api_key ,
124
+ {"resource" : _hash }
125
+ )
126
+ if data :
127
+ if data .json ().get ("response_code" ) == 1 :
128
+ return ["{}: {}" .format (i , data .json ().get ("scans" ).get (i ).get ("result" ))
129
+ for i in data .json ().get ("scans" )
130
+ if data .json ().get ("scans" ).get (i ).get ("result" )]
131
+ return ["no report available" ]
132
+ else :
133
+ return ["no data available" ]
134
+ except Exception as e :
135
+ return ["virustotal api error: " , e ]
136
+
84
137
class Md5Hash (object ):
85
138
"""Md5 hash handler class"""
86
139
def __init__ (self ):
87
140
self .osint_options = {}
141
+ self .api_db = Database ()
142
+ self .virustotal_api_key = self .api_db .get_api_key ("virustotal" )
143
+ if self .virustotal_api_key :
144
+ self .osint_options .update ({
145
+ "virustotal: malicious check" : self .virustotal_is_malicious ,
146
+ "virustotal: malware type" : self .virustotal_malware_type })
88
147
89
148
def is_md5 (self , _input : str ):
90
149
"""Validates if _input is an md5 hash"""
91
150
if validators .hashes .md5 (_input ):
92
151
return True
93
152
return False
94
153
154
+ def virustotal_is_malicious (self , _hash :str ):
155
+ """Checks virustotal to see if MD5 has positive detections"""
156
+ try :
157
+ data = make_vt_api_request (
158
+ "https://www.virustotal.com/vtapi/v2/file/report" ,
159
+ self .virustotal_api_key ,
160
+ {"resource" : _hash }
161
+ )
162
+ if data :
163
+ if data .json ().get ("response_code" ) == 0 :
164
+ return ["no report available" ]
165
+ return ["hash malicious: {} detections" .format (data .json ().get ("positives" ))]
166
+ else :
167
+ return ["no data available" ]
168
+ except Exception as e :
169
+ return ["virustotal api error: " , e ]
170
+
171
+ def virustotal_malware_type (self , _hash :str ):
172
+ """Checks virustotal to return malware types detected by scans"""
173
+ try :
174
+ data = make_vt_api_request (
175
+ "https://www.virustotal.com/vtapi/v2/file/report" ,
176
+ self .virustotal_api_key ,
177
+ {"resource" : _hash }
178
+ )
179
+ if data :
180
+ if data .json ().get ("response_code" ) == 1 :
181
+ return ["{}: {}" .format (i , data .json ().get ("scans" ).get (i ).get ("result" ))
182
+ for i in data .json ().get ("scans" )
183
+ if data .json ().get ("scans" ).get (i ).get ("result" )]
184
+ return ["no report available" ]
185
+ else :
186
+ return ["no data available" ]
187
+ except Exception as e :
188
+ return ["virustotal api error: " , e ]
189
+
190
+
95
191
class Url (object ):
96
192
"""Url handler class"""
97
193
def __init__ (self ):
98
- self .osint_options = {}
194
+ self .osint_options = {
195
+ "dns: extract hostname" : self .url_to_hostname
196
+ }
197
+ self .api_db = Database ()
198
+ self .virustotal_api_key = self .api_db .get_api_key ("virustotal" )
199
+ if self .virustotal_api_key :
200
+ self .osint_options .update ({
201
+ "virustotal: malicious check" : self .is_malicious ,
202
+ "virustotal: reported detections" : self .reported_detections })
99
203
100
204
def is_url (self , _input : str ):
101
205
"""Validates if _input is a url"""
102
206
if validators .url (_input ):
103
207
return True
104
208
return False
105
209
210
+ def is_malicious (self , url : str ):
211
+ """Checks if url is malicious"""
212
+ try :
213
+ data = make_vt_api_request (
214
+ "https://www.virustotal.com/vtapi/v2/url/report" ,
215
+ self .virustotal_api_key ,
216
+ {"resource" : url }
217
+ )
218
+ if data :
219
+ if data .json ().get ("response_code" ) == 1 :
220
+ return ["url malicious: {} detections" .format (data .json ().get ("positives" ))]
221
+ return ["no report available" ]
222
+ else :
223
+ return ["no data available" ]
224
+ except Exception as e :
225
+ return ["virustotal api error: " , e ]
226
+
227
+ def reported_detections (self , url : str ):
228
+ """Checks virustotal to determine which sites are reporting the url"""
229
+ try :
230
+ data = make_vt_api_request (
231
+ "https://www.virustotal.com/vtapi/v2/url/report" ,
232
+ self .virustotal_api_key ,
233
+ {"resource" : url }
234
+ )
235
+ if data :
236
+ if data .json ().get ("response_code" ) == 1 :
237
+ return ["{}: {}" .format (i , data .json ().get ("scans" ).get (i ).get ("result" ))
238
+ for i in data .json ().get ("scans" )
239
+ if (data .json ().get ("scans" ).get (i ).get ("result" ) == "malicious site" ) or
240
+ (data .json ().get ("scans" ).get (i ).get ("result" ) == "malware site" )]
241
+ return ["no report available" ]
242
+ else :
243
+ return ["no data available" ]
244
+ except Exception as e :
245
+ return ["virustotal api error: " , e ] # todo: change to return ["error: " + e]
246
+
247
+ def url_to_hostname (self , url : str ):
248
+ """Extracts hostname from url"""
249
+ try :
250
+ return [urlparse (url ).netloc ]
251
+ except Exception as e :
252
+ return ["error: " + e ]
253
+
254
+
106
255
class IPAdress (object ):
107
256
"""Ip address handler class"""
108
257
def __init__ (self ):
@@ -288,13 +437,13 @@ def ip_to_vt_detected_urls(self, ip:str):
288
437
else :
289
438
return ["no data available" ]
290
439
except Exception as e :
291
- return ["virustotal api error: " , e ]
440
+ return ["virustotal api error: " , e ] # todo: return ["virustotal api error: " + e]
441
+
292
442
293
443
class EmailAddress (object ):
294
444
"""Email address handler class"""
295
445
def __init__ (self ):
296
446
self .osint_options = {
297
- "haveibeenpwnd" : self .hibp_lookup ,
298
447
"extract domain" : self .domain_extract
299
448
}
300
449
@@ -311,6 +460,7 @@ def domain_extract(self, email: str):
311
460
"""Returns domain from supplied email"""
312
461
return [email .split ("@" )[1 ]]
313
462
463
+
314
464
class Domain (object ):
315
465
"""Domain handler class"""
316
466
def __init__ (self ):
@@ -325,6 +475,13 @@ def __init__(self):
325
475
if shodan_api_key :
326
476
self .shodan_api = shodan .Shodan (shodan_api_key )
327
477
self .osint_options .update ({"shodan: hostnames" : self .to_shodan_hostnames })
478
+ self .virustotal_api_key = self .api_db .get_api_key ("virustotal" )
479
+ if self .virustotal_api_key :
480
+ self .osint_options .update ({
481
+ "virustotal: downloaded samples" : self .domain_to_vt_downloaded_samples ,
482
+ "virustotal: detected urls" : self .domain_to_vt_detected_urls ,
483
+ "virustotal: subdomains" : self .domain_to_vt_subdomains
484
+ })
328
485
329
486
def is_valid_domain (self , _input : str ):
330
487
"""Checks if _input is a domain"""
@@ -344,7 +501,7 @@ def to_mx_records(self, domain: str):
344
501
try :
345
502
return [x .exchange for x in dns .resolver .query (domain , 'MX' )]
346
503
except Exception as e :
347
- raise e
504
+ raise e # todo: return ["virustotal api error: " + e]
348
505
349
506
def to_txt_records (self , domain : str ):
350
507
"""Returns dns txt record for domain"""
@@ -373,7 +530,50 @@ def to_shodan_hostnames(self, domain: str):
373
530
else :
374
531
return ["no data available" ]
375
532
except Exception as e :
376
- return ["shodan api error: " , e ]
533
+ return ["shodan api error: " + e ]
534
+
535
+ def domain_to_vt_detected_urls (self , domain :str ):
536
+ """Searches virustotal to search for detected communicating samples"""
537
+ try :
538
+ data = make_vt_api_request (
539
+ "https://www.virustotal.com/vtapi/v2/domain/report" ,
540
+ self .virustotal_api_key ,
541
+ {"domain" :domain })
542
+ if data :
543
+ return [record .get ("url" ) for record in data .json ()["detected_urls" ]]
544
+ else :
545
+ return ["no data available" ]
546
+ except Exception as e :
547
+ return ["virustotal api error: " + e ]
548
+
549
+ def domain_to_vt_downloaded_samples (self , domain :str ):
550
+ """Searches virustotal to search for detected communicating samples"""
551
+ try :
552
+ data = make_vt_api_request (
553
+ "https://www.virustotal.com/vtapi/v2/domain/report" ,
554
+ self .virustotal_api_key ,
555
+ {"domain" :domain })
556
+ if data :
557
+ return [record .get ("sha256" ) for record in data .json ()["detected_downloaded_samples" ]]
558
+ else :
559
+ return ["no data available" ]
560
+ except Exception as e :
561
+ return ["virustotal api error: " + e ]
562
+
563
+ def domain_to_vt_subdomains (self , domain : str ):
564
+ """Searches virustotal for subdomains"""
565
+ try :
566
+ data = make_vt_api_request (
567
+ "https://www.virustotal.com/vtapi/v2/domain/report" ,
568
+ self .virustotal_api_key ,
569
+ {"domain" :domain })
570
+ if data :
571
+ return data .json ().get ("subdomains" )
572
+ else :
573
+ return ["no data available" ]
574
+ except Exception as e :
575
+ return ["virustotal api error: " + e ]
576
+
377
577
378
578
class InputValidator (object ):
379
579
"""Handler to validate user inputs"""
@@ -383,6 +583,7 @@ def __init__(self):
383
583
self .domain = Domain ()
384
584
self .url = Url ()
385
585
self .md5 = Md5Hash ()
586
+ self .sha256 = Sha256Hash ()
386
587
387
588
def run (self , _function , ** kwargs ):
388
589
"""Runs function and associated keyword arguments"""
@@ -403,6 +604,8 @@ def validate(self, _input: str):
403
604
return [True , "input: url" , [option for option in self .url .osint_options .keys ()]]
404
605
elif self .md5 .is_md5 (_input ):
405
606
return [True , "input: md5" , [option for option in self .md5 .osint_options .keys ()]]
607
+ elif self .sha256 .is_sha256 (_input ):
608
+ return [True , "input: sha256" , [option for option in self .sha256 .osint_options .keys ()]]
406
609
return [False , []]
407
610
408
611
def execute_transform (self , _input : str , transform : str ):
@@ -413,6 +616,12 @@ def execute_transform(self, _input: str, transform: str):
413
616
return self .run (self .email .osint_options .get (transform ), email = _input )
414
617
elif self .domain .is_valid_domain (_input ):
415
618
return self .run (self .domain .osint_options .get (transform ), domain = _input )
619
+ elif self .md5 .is_md5 (_input ):
620
+ return self .run (self .md5 .osint_options .get (transform ), _hash = _input )
621
+ elif self .url .is_url (_input ):
622
+ return self .run (self .url .osint_options .get (transform ), url = _input )
623
+ elif self .sha256 .is_sha256 (_input ):
624
+ return self .run (self .sha256 .osint_options .get (transform ), _hash = _input )
416
625
417
626
def load_icon ():
418
627
"""loads and returns program icon from base64 string"""
0 commit comments