Last active
November 15, 2021 05:02
-
-
Save jgamblin/da30c390e71994ffba910449d839af66 to your computer and use it in GitHub Desktop.
Scan a Folder and Upload VT Findings to Slack.
This file contains hidden or 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
#!/usr/bin/env python | |
import os | |
import sys | |
import json | |
import urllib | |
import urllib2 | |
import hashlib | |
import argparse | |
from slacker import Slacker | |
slack = Slacker('Your Slack Key') | |
VIRUSTOTAL_FILE_URL = 'https://www.virustotal.com/vtapi/v2/file/report' | |
API_KEY = 'Your Slack Key' | |
TPL_SECTION = "[*] ({0}):" | |
TPL_MATCH = "\t\_ Results: {0}/{1} {2}\n\t SHA256: {3}\n\t Scan Date: {4}" | |
TPL_SIGNATURES = "\t Signatures:\n\t\t{0}" | |
def color(text, color_code): | |
if sys.platform == "win32" and os.getenv("TERM") != "xterm": | |
return text | |
return '\x1b[%dm%s\x1b[0m' % (color_code, text) | |
def red(text): | |
return color(text, 31) | |
def yellow(text): | |
return color(text, 33) | |
class Hash(object): | |
def __init__(self, path): | |
self.path = path | |
self.md5 = '' | |
self.sha256 = '' | |
def get_chunks(self): | |
fd = open(self.path, 'rb') | |
while True: | |
chunk = fd.read(16 * 1024) | |
if not chunk: | |
break | |
yield chunk | |
fd.close() | |
def calculate(self): | |
md5 = hashlib.md5() | |
sha256 = hashlib.sha256() | |
for chunk in self.get_chunks(): | |
md5.update(chunk) | |
sha256.update(chunk) | |
self.md5 = md5.hexdigest() | |
self.sha256 = sha256.hexdigest() | |
class Scanner(object): | |
def __init__(self, key, path): | |
self.key = key | |
self.path = path | |
self.list = [] | |
def populate(self): | |
paths = [] | |
if os.path.isfile(self.path): | |
paths.append(self.path) | |
else: | |
for root, folders, files in os.walk(self.path): | |
for file_name in files: | |
# Skip hidden files, might need an option for this. | |
if file_name.startswith('.'): | |
continue | |
file_path = os.path.join(root, file_name) | |
if os.path.exists(file_path): | |
paths.append(file_path) | |
for path in paths: | |
hashes = Hash(path) | |
hashes.calculate() | |
self.list.append({ | |
'path' : path, | |
'md5' : hashes.md5, | |
'sha256' : hashes.sha256 | |
}) | |
def scan(self): | |
hashes = [] | |
for entry in self.list: | |
if entry['sha256'] not in hashes: | |
hashes.append(entry['sha256']) | |
data = urllib.urlencode({ | |
'resource' : ','.join(hashes), | |
'apikey' : self.key | |
}) | |
try: | |
request = urllib2.Request(VIRUSTOTAL_FILE_URL, data) | |
response = urllib2.urlopen(request) | |
report = json.loads(response.read()) | |
except Exception as e: | |
print(red("[!] ERROR: Cannot obtain results from VirusTotal: {0}\n".format(e))) | |
return | |
results = [] | |
if type(report) is dict: | |
results.append(report) | |
elif type(report) is list: | |
results = report | |
for entry in results: | |
sha256 = entry['resource'] | |
entry_paths = [] | |
for item in self.list: | |
if item['sha256'] == sha256: | |
if item['path'] not in entry_paths: | |
entry_paths.append(item['path']) | |
if entry['response_code'] == 0: | |
#print('NOT FOUND') | |
pass | |
else: | |
signatures = [] | |
for av, scan in entry['scans'].items(): | |
if scan['result']: | |
signatures.append(scan['result']) | |
if entry['positives'] > 2: | |
print(TPL_SECTION.format('\n '.join(entry_paths))), | |
print(TPL_MATCH.format( | |
entry['positives'], | |
entry['total'], | |
red('DETECTED'), | |
entry['resource'], | |
entry['scan_date'] | |
)) | |
if entry['positives'] > 0: | |
print(TPL_SIGNATURES.format('\n\t\t'.join(signatures))) | |
def run(self): | |
if not self.key: | |
print(red("[!] ERROR: You didn't specify a valid VirusTotal API key.\n")) | |
return | |
if not os.path.exists(self.path): | |
print(red("[!] ERROR: The target path {0} does not exist.\n".format(self.path))) | |
return | |
self.populate() | |
self.scan() | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser() | |
parser.add_argument('path', type=str, help='Path to the file or folder to lookup on VirusTotal') | |
parser.add_argument('--key', type=str, action='store', default=API_KEY, help='VirusTotal API key') | |
try: | |
args = parser.parse_args() | |
except IOError as e: | |
parser.error(e) | |
sys.exit() | |
with open('infectedfiles.txt', 'w') as log: | |
sys.stdout = log | |
scan = Scanner(args.key, args.path) | |
scan.run() | |
slack.files.upload('infectedfiles.txt',channels='#vt', filetype='text',initial_comment="These files are probably bad!") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment