Last active
August 2, 2024 02:33
-
-
Save bontchev/8a53787a37862c3dc11d1dddff143c3e to your computer and use it in GitHub Desktop.
Converts MD5 to SHA256 hashes using VirusTotal
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 | |
# -*- coding: utf-8 -*- | |
from __future__ import print_function | |
from re import compile | |
from sys import stdout, stderr | |
from argparse import ArgumentParser | |
try: | |
from virustotal import VirusTotal | |
except ImportError: | |
print('Could not import module "virustotal"; try "pip install virustotal".') | |
try: | |
from ratelimiter import RateLimiter | |
except ImportError: | |
print('Could not import module "ratelimiter"; try "pip install ratelimiter".') | |
__description__ = 'Converts MD5 to SHA256 hashes using VirusTotal.' | |
__license__ = 'GPL' | |
__uri__ = 'https://gist.github.com/bontchev/8a53787a37862c3dc11d1dddff143c3e' | |
__VERSION__ = '1.0.0' | |
__author__ = 'Vesselin Bontchev' | |
__email__ = '[email protected]' | |
api_key = 'Your VirusTotal API key here' | |
def process_hash(hash, output_file, use_sha1, verbose, rate_limiter, v): | |
hex_digits_md5 = compile('[0-9a-fA-F]{32}') | |
if not hex_digits_md5.match(hash): | |
print('Bad MD5 hash: "{}".'.format(hash), file=stdout) | |
return True | |
if verbose: | |
print('Processing {}...'.format(hash)) | |
with rate_limiter: | |
try: | |
report = v.get(hash) | |
if report is None: | |
print('{}'.format(hash), file=output_file) | |
return False | |
report.join() | |
assert report.done == True | |
new_hash = report.sha1 if use_sha1 else report.sha256 | |
print('{}\t{}'.format(hash.upper(), new_hash.upper()), file=output_file) | |
output_file.flush() | |
except Exception as e: | |
print('Error: {}'.format(e), file=stderr) | |
return True | |
return False | |
def process_file(hash_file, output_file, use_sha1, verbose, rate_limiter, v): | |
error_level = False | |
try: | |
with open(hash_file, 'r') as f: | |
lines = f.readlines() | |
for line in lines: | |
line = line.strip() | |
if not line: | |
continue | |
if line[0] == '#': | |
continue | |
line = line.split()[0] | |
if process_hash(line, output_file, use_sha1, verbose, rate_limiter, v): | |
error_level = True | |
except Exception as e: | |
print('Error: {}.'.format(e), file=stderr) | |
return True | |
return error_level | |
def get_options(): | |
parser = ArgumentParser(description=__description__) | |
parser.add_argument('-v', '--version', action='version', | |
version='%(prog)s version {}'.format(__VERSION__)) | |
parser.add_argument('-a', '--apikey', default=api_key, | |
help='VirusTotal API key') | |
parser.add_argument('-b', '--verbose', action='store_true', | |
help='Display the hashes as they are processed') | |
parser.add_argument('-r', '--rate', type=int, default=4, | |
help='Requests per minute (default: 4)') | |
parser.add_argument('-s', '--sha1', action='store_true', | |
help='Use SHA1 instead of SHA256') | |
parser.add_argument('-o', '--output', dest='outputfile', default=None, | |
help='Output file name (default: stdout)') | |
parser.add_argument('fileOrMD5', nargs='+', help='@File or MD5 hash') | |
return parser.parse_args() | |
def main(): | |
args = get_options() | |
hex_digits_api = compile('[0-9a-fA-F]{64}') | |
if not hex_digits_api.match(args.apikey): | |
print('API key not set correctly.', file=stderr) | |
exit(1) | |
output_file = stdout | |
error_level = 0 | |
if args.outputfile is not None: | |
output_file = open(args.outputfile, 'w') | |
rate_limiter = RateLimiter(max_calls=args.rate, period=60) | |
v = VirusTotal(args.apikey) | |
for hash in args.fileOrMD5: | |
if hash[0] == '@': | |
if process_file(hash[1:], output_file, args.sha1, args.verbose, rate_limiter, v): | |
error_level = 1 | |
elif process_hash(hash, output_file, args.sha1, args.verbose, rate_limiter, v): | |
error_level = 1 | |
if args.outputfile is not None: | |
output_file.close() | |
exit(error_level) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
md5tosha.py - Converting MD5 hashes to SHA hashes using VirusTotal
Introduction
Don't you hate it when a malware report includes as indicators of compromise
(IoCs) MD5 hashes of the malware instead of SHA256 hashes? MD5 is an obsolete
and insecure hash function and it should no longer be used. Besides, many
honeypots store the files uploaded by the attackers with names derived from
their SHA hashes, so it's easier to check if your honeypot has already seen
this malware if you had its SHA hash.
Sadly, it is not possible to compute the SHA hash from the MD5 hash. However,
VirusTotal computes and stores different kinds of hashes for the malware
uploaded to it and, if the sample of the malware has been uploaded there,
it is possible to obtain any of its hashes by knowing some other hash of it.
Doing this manually is a rather irritating and time-consumming process, especially
for large batches of hashes, so I have written this small Python script in
order to automate it.
Dependencies
The script depends on two external Python modules:
virustotal
andratelimiter
. Both can be installed withpip
:In addition, you need a VirusTotal API key. One can be obtaied by registering
a free account threre. Once obtained, the key can be put in the script (in the
variable
api_key
), or it can be specified at run-time from the command lineby using the
-a
option. Please keep in mind that the API keys of the freeaccounts are limited to 4 queries per minute.
Also note that the script can find out the SHA hash only if the malware with
the corresponding MD5 hash has been uploaded to VirusTotal.
Usage
The script takes as arguments one or more MD5 hashes or file names. If a file
name is specified, it must be a text file and must be specified with a
@
character prepended to its name.
The format of the file is the following:
The script takes the following command-line options:
-h
,--help
Displays usage information-v
,--version
Displays the version number of the script-a APIKEY
,--apikey APIKEY
Specifies a VirusTotal API key-b
,--verbose
Displays tostdout
each MD5 hash as it is being processed-r RATE
,--rate RATE
Specifies the rate at which the queries should be sentto VirusTotal. Use this option if you have a paid account (and an API key from
it) that allows more frequent queries
-s
,--sha1
Display the SHA1 hash of the file instead of the default SHA256-o OUTPUTFILE
,--output OUTPUTFILE
Stores the results into the specifiedoutput file
If VirusTotal has a sample of the file whose MD5 hash is being queried, the
script will output a line containing the MD5 hash and the SHA hash, separated
with a tab. If VirusTotal does not have a sample, the script will output only
the MD5 hash on the line.