├── .gitignore ├── README.md └── vtscan ├── README.md └── vtscan.py /.gitignore: -------------------------------------------------------------------------------- 1 | .pyc 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Various scripts helpful in sorting collections of malware samples. 2 | -------------------------------------------------------------------------------- /vtscan/README.md: -------------------------------------------------------------------------------- 1 | VT-scan 2 | -- 3 | Checks list of hashes for malware (using Virus Total). 4 | -------------------------------------------------------------------------------- /vtscan/vtscan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.7 2 | "Checks list of hashes for malware names (using Virus Total)" 3 | 4 | __author__ = 'hasherezade (hasherezade.net)' 5 | __license__ = "GPL" 6 | __VERSION__ = "1.0" 7 | 8 | import sys,os 9 | import re 10 | import time 11 | import zlib 12 | import argparse 13 | import urllib,urllib2 14 | import hashlib 15 | 16 | DEFAULT_MALNAMES = 'cryptowall,crypwall,bunitu,proxy,zeus,zbot,ramnit' 17 | 18 | host = "www.virustotal.com" 19 | url2 = "https://" + host + "/en/search/?query=" 20 | method = 'GET' 21 | 22 | agent = 'Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.2.1' 23 | accept = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' 24 | language = 'en-US,en;q=0.5' 25 | encoding = 'gzip, deflate' 26 | content_type = "application/x-www-form-urlencoded" 27 | 28 | g_DisableColors = False 29 | 30 | #--- 31 | #terminal colors: 32 | # 33 | GREY = '\033[90m' 34 | RED = '\033[91m' 35 | GREEN = '\033[92m' 36 | YELLOW = '\033[93m' 37 | BLUE = '\033[94m' 38 | PURPLE = '\033[95m' 39 | LIGHTBLUE = '\033[96m' 40 | BG_RED = '\033[6;30;41m' 41 | BG_GREY = '\033[6;37;40m' 42 | 43 | COLOR_END = '\033[0m' 44 | BOLD = "\033[1m" 45 | 46 | def color_signed_msg(color, sign, msg): 47 | if not color or not sign: 48 | print msg 49 | return 50 | if not is_linux() or g_DisableColors is True: 51 | print '[' + sign + '] ' + msg 52 | return 53 | print BOLD + color +'[' + sign + '] ' + COLOR_END + msg 54 | 55 | def color_msg(color,msg): 56 | if not color or not is_linux() or g_DisableColors is True: 57 | print msg 58 | return 59 | print color + msg + COLOR_END 60 | 61 | def color_bold_msg(color, msg): 62 | if not color or not is_linux() or g_DisableColors is True: 63 | print msg 64 | return 65 | print BOLD + color + msg + COLOR_END 66 | 67 | def info(msg): 68 | color_signed_msg(BLUE, '*', msg) 69 | 70 | def good(msg): 71 | color_signed_msg(GREEN, '+', msg) 72 | 73 | def warn(msg): 74 | color_signed_msg(YELLOW, '!', msg) 75 | 76 | def err( msg): 77 | color_signed_msg(RED, '-', msg) 78 | 79 | def is_linux(): 80 | from sys import platform as _platform 81 | if "linux" in _platform : 82 | return True 83 | return False 84 | #--- 85 | 86 | class TimeoutException(Exception): 87 | pass 88 | 89 | def decompress_data(data): 90 | data=zlib.decompress(data, 16+zlib.MAX_WBITS) 91 | return data 92 | 93 | def make_req(host, url, mhash): 94 | data='' 95 | url += mhash 96 | print "\n---\n"+ url 97 | request = urllib2.Request(url, data, {'Host': host, 98 | 'Content-Type': content_type, 99 | 'User-Agent' : agent, 100 | 'Accept' : accept, 101 | 'Accept-Language' : language, 102 | 'Accept-Encoding' : encoding 103 | }) 104 | request.get_method = lambda: method 105 | try: 106 | resp = urllib2.urlopen(request) 107 | except urllib2.HTTPError as e1: 108 | print "Error" 109 | raise e1 110 | except urllib2.URLError, e: 111 | print "Error" 112 | if 'timeout' in e.reason: 113 | raise TimeoutException() 114 | 115 | rcode = resp.getcode() 116 | if rcode == 200: 117 | resp_content = resp.read() 118 | if resp.info().getheader('Content-Encoding') == 'gzip': 119 | resp_content = decompress_data(resp_content) 120 | return resp_content 121 | print "Response code: %d" % rcode 122 | return None 123 | 124 | def fetch_md5s(line): 125 | pattern = re.compile(r'\b[0-9a-fA-F]{32}\b') 126 | fhash = re.findall(pattern, line) 127 | return fhash 128 | 129 | def fetch_sha1(line): 130 | pattern = re.compile(r'\b[0-9a-fA-F]{40}\b') 131 | fhash = re.findall(pattern, line) 132 | return fhash 133 | 134 | def fetch_sha256(line): 135 | pattern = re.compile(r'\b[0-9a-f]{64}\b') 136 | fhash = re.findall(pattern, line) 137 | return fhash 138 | 139 | def get_hashes(fname): 140 | hashes = set() 141 | with open(fname, 'r') as f: 142 | for line in f.readlines(): 143 | md5s = fetch_md5s(line) 144 | for h in md5s: 145 | hashes.add(h) 146 | sha1s = fetch_sha1(line) 147 | for h in sha1s: 148 | hashes.add(h) 149 | sha256s = fetch_sha256(line) 150 | for h in sha256s: 151 | hashes.add(h) 152 | return hashes 153 | 154 | def calc_hashes(dir_name): 155 | dir_content = set(os.listdir(dir_name)) 156 | hash_to_name = dict() 157 | for fname in dir_content: 158 | fullname = os.path.join(dir_name, fname) 159 | if not os.path.isfile(fullname): 160 | continue 161 | data = open(fullname, 'rb').read() 162 | filehash = hashlib.sha256(data).hexdigest() 163 | print filehash + " : " + fname 164 | hash_to_name[filehash] = fname 165 | return hash_to_name 166 | 167 | def get_between_patterns(data, pattern1, pattern2): 168 | pattern1 = pattern1.lower() 169 | pattern2 = pattern2.lower() 170 | data = data.lower() 171 | 172 | if not pattern1 in data: 173 | return None 174 | indx1 = data.index(pattern1) + len(pattern1) 175 | data = data[indx1:] 176 | if not pattern2 in data: 177 | return None 178 | indx2 = data.index(pattern2) 179 | data = data[:indx2].strip() 180 | return data 181 | 182 | def check_keywords(data, keywords, mhash): 183 | data = data.lower() 184 | for keyword in keywords: 185 | keyword = keyword.lower().strip() 186 | if keyword in data: 187 | return keyword 188 | return None 189 | 190 | def check_all_keywords(data, keywords, mhash): 191 | found_keywords = list() 192 | data = data.lower() 193 | for keyword in keywords: 194 | keyword = keyword.lower().strip() 195 | if keyword in data: 196 | found_keywords.append(keyword) 197 | if len(found_keywords) == 0: 198 | return None 199 | return found_keywords 200 | 201 | def check_id(data, vendor): 202 | data = get_between_patterns(data, vendor, '') 203 | if not data: 204 | return None 205 | detectedp = '