Advertisement
Guest User

Search VirusTotal. Added -f option

a guest
Jun 3rd, 2012
444
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.83 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. __description__ = 'Program to search VirusTotal reports with search terms (MD5, SHA1, SHA256) found in the argument file'
  4. __author__ = 'Didier Stevens'
  5. __version__ = '0.0.2'
  6. __date__ = '2012/05/23'
  7.  
  8. """
  9.  
  10. Source code put in public domain by Didier Stevens, no Copyright
  11. https://DidierStevens.com
  12. Use at your own risk
  13.  
  14. History:
  15.  2012/04/25: start
  16.  2012/04/27: added serialization of reports
  17.  2012/05/23: emergency fix pkl init bug
  18.  
  19. Todo:
  20. """
  21.  
  22. import optparse
  23. import urllib
  24. import urllib2
  25. import time
  26. import sys
  27. import pickle
  28. import hashlib
  29.  
  30. try:
  31.     import simplejson
  32. except:
  33.     print('Missing simplejson Python module, please check if it is installed.')
  34.     exit()
  35.  
  36. VIRUSTOTAL_API2_KEY = '37459da39f7e84a59f79f737ba075f6c7dd2c4dfd69ecd0e874d469542782e3d'
  37. VIRUSTOTAL_REPORT_URL = "https://www.virustotal.com/vtapi/v2/file/report"
  38.  
  39. PICKLE_FILE = 'virustotal-search.pkl'
  40.  
  41. def Serialize(object):
  42.     try:
  43.         fPickle = open(PICKLE_FILE, 'wb')
  44.     except:
  45.         return False
  46.     try:
  47.         pickle.dump(object, fPickle)
  48.     except:
  49.         return False
  50.     finally:
  51.         fPickle.close()
  52.     return True
  53.  
  54. def DeSerialize():
  55.     import os.path
  56.  
  57.     if os.path.isfile(PICKLE_FILE):
  58.         try:
  59.             fPickle = open(PICKLE_FILE, 'rb')
  60.         except:
  61.             return None
  62.         try:
  63.             object = pickle.load(fPickle)
  64.         except:
  65.             return None
  66.         finally:
  67.             fPickle.close()
  68.         return object
  69.     else:
  70.         return None
  71.  
  72. def Timestamp(epoch=None):
  73.     if epoch == None:
  74.         localTime = time.localtime()
  75.     else:
  76.         localTime = time.localtime(epoch)
  77.     return '%04d%02d%02d-%02d%02d%02d' % localTime[0:6]
  78.  
  79. class CSVLogger():
  80.     def __init__(self, prefix, headers, separator=';'):
  81.         self.separator = separator
  82.         self.filename = '%s-%s.csv' % (prefix, Timestamp())
  83.         self.f = open(self.filename, 'w')
  84.         self.f.write(self.separator.join(headers) + '\n')
  85.         self.f.close()
  86.  
  87.     def PrintAndLog(self, formats, parameters):
  88.         line = self.separator.join(formats) % parameters
  89.         print(line)
  90.         f = open(self.filename, 'a')
  91.         f.write(line + '\n')
  92.         f.close()
  93.  
  94. def VTHTTPReportRequest(searchTerm):
  95.     req = urllib2.Request(VIRUSTOTAL_REPORT_URL, urllib.urlencode({'resource': searchTerm, 'apikey': VIRUSTOTAL_API2_KEY}))
  96.     try:
  97.         if sys.hexversion >= 0x020601F0:
  98.             hRequest = urllib2.urlopen(req, timeout=15)
  99.         else:
  100.             hRequest = urllib2.urlopen(req)
  101.     except:
  102.         return None
  103.     try:
  104.         data = hRequest.read()
  105.     except:
  106.         return None
  107.     finally:
  108.         hRequest.close()
  109.     return data
  110.  
  111. def InsertIntoTuple(tupleIn, position, value):
  112.     listIn = list(tupleIn)
  113.     listIn.insert(position, value)
  114.     return tuple(listIn)
  115.  
  116. def GetReport(searchTerm, withComment, reports):
  117.     global oLogger
  118.  
  119.     if withComment:
  120.         index = searchTerm.find(' ')
  121.         if index == -1:
  122.             comment = ''
  123.         else:
  124.             comment = searchTerm[index+1:]
  125.             searchTerm = searchTerm[:index]
  126.     if searchTerm in reports:
  127.         issuedRequest = False
  128.         oResult = reports[searchTerm]
  129.     else:
  130.         jsonResponse = VTHTTPReportRequest(searchTerm)
  131.         issuedRequest = True
  132.         if jsonResponse == None:
  133.             formats = ('%s', '%s')
  134.             parameters = (searchTerm, 'Error')
  135.             if withComment:
  136.                 formats = InsertIntoTuple(formats, 1, '%s')
  137.                 parameters = InsertIntoTuple(parameters, 1, comment)
  138.             oLogger.PrintAndLog(formats, parameters)
  139.             return issuedRequest
  140.         else:
  141.             oResult = simplejson.loads(jsonResponse)
  142.             if oResult['response_code'] == 1:
  143.                 reports[searchTerm] = oResult
  144.     if oResult['response_code'] == 1:
  145.         scans = []
  146.         for scan in sorted(oResult['scans']):
  147.             if oResult['scans'][scan]['detected']:
  148.                 scans.append('#'.join((scan, oResult['scans'][scan]['result'], oResult['scans'][scan]['update'], oResult['scans'][scan]['version'])))
  149.         formats = ('%s', '%d', '%s', '%d', '%d', '%s', '%s')
  150.         parameters = (searchTerm, oResult['response_code'], oResult['scan_date'], oResult['positives'], oResult['total'], oResult['permalink'], ','.join(scans))
  151.         if withComment:
  152.             formats = InsertIntoTuple(formats, 1, '%s')
  153.             parameters = InsertIntoTuple(parameters, 1, comment)
  154.         oLogger.PrintAndLog(formats, parameters)
  155.     else:
  156.         print jsonResponse
  157.         formats = ('%s', '%d', '%s')
  158.         parameters = (searchTerm, oResult['response_code'], oResult['verbose_msg'])
  159.         if withComment:
  160.             formats = InsertIntoTuple(formats, 1, '%s')
  161.             parameters = InsertIntoTuple(parameters, 1, comment)
  162.         oLogger.PrintAndLog(formats, parameters)
  163.     return issuedRequest
  164.  
  165. def File2Strings(filename):
  166.     try:
  167.         f = open(filename, 'r')
  168.     except:
  169.         return None
  170.     try:
  171.         return map(lambda line:line.rstrip('\n'), f.readlines())
  172.     except:
  173.         return None
  174.     finally:
  175.         f.close()
  176.  
  177. def VirusTotalSearch(filename, options):
  178.     global oLogger
  179.  
  180.     if options.file:
  181.         searchTerms = hashfile(filename)
  182.         if searchTerms == None:
  183.             print('Error reading file %s' % filename)
  184.             return
  185.     else:
  186.         searchTerms = File2Strings(filename)
  187.         if searchTerms == None:
  188.             print('Error reading file %s' % filename)
  189.             return
  190.         elif searchTerms == []:
  191.             print('No searchterms in file %s' % filename)
  192.             return
  193.     headers = ('Search Term', 'Response', 'Scan Date', 'Detections', 'Total', 'Permalink', 'AVs')
  194.     if options.comment:
  195.         headers = InsertIntoTuple(headers, 1, 'Comment')
  196.     oLogger = CSVLogger('virustotal-search', headers)
  197.  
  198.     data = DeSerialize()
  199.     if data == None:
  200.         reports = {}
  201.     else:
  202.         reports = data['reports']
  203.  
  204.     while searchTerms != []:
  205.         issuedRequest = GetReport(searchTerms[0], options.comment, reports)
  206.         searchTerms = searchTerms[1:]
  207.         if issuedRequest and searchTerms != []:
  208.             time.sleep(options.delay)
  209.     Serialize({'reports': reports})
  210.  
  211. def hashfile(malfile):
  212.     hash = None
  213.     try:
  214.         hash = []
  215.         f = open(malfile,'rb')
  216.         m = hashlib.md5()
  217.         while True:
  218.             data = f.read(10240)
  219.             if len(data) == 0:
  220.                 break
  221.             m.update(data)
  222.         hash.append(m.hexdigest())
  223.     except:
  224.         None
  225.     return hash
  226.  
  227. def Main():
  228.     oParser = optparse.OptionParser(usage='usage: %prog [options] file\n' + __description__, version='%prog ' + __version__)
  229.     oParser.add_option('-d', '--delay', type=int, default=16, help='delay in seconds between queries (default 16s, VT rate limit is 4 queries per minute)')
  230.     oParser.add_option('-f', '--file', action='store_true', default=False, help='Specify a file to create a MD5sum from instead of specifying a file containing hashes.')
  231.     oParser.add_option('-c', '--comment', action='store_true', default=False, help='the search term is followed by a comment and separated by a space character')
  232.     (options, args) = oParser.parse_args()
  233.  
  234.     if len(args) != 1:
  235.         oParser.print_help()
  236.         print('')
  237.         print('  Source code put in the public domain by Didier Stevens, no Copyright')
  238.         print('  Use at your own risk')
  239.         print('  https://DidierStevens.com')
  240.         return
  241.     elif VIRUSTOTAL_API2_KEY == '':
  242.         print('You need to get a VirusTotal API key and add it to this program.\nTo get your API key, you need a VirusTotal account.')
  243.     else:
  244.         VirusTotalSearch(args[0], options)
  245.  
  246. if __name__ == '__main__':
  247.     Main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement