## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' require 'net/http' require 'uri' class MetasploitModule < Msf::Post include Msf::Post::File def initialize(info={}) super( update_info( info, 'Name' => 'Multi Gather Malware Verifier', 'Description' => %q{ This module will check a file for malware on VirusTotal based on the checksum. }, 'License' => MSF_LICENSE, 'Author' => [ 'sinn3r'], 'Platform' => [ 'osx', 'win', 'linux' ], 'SessionTypes' => [ "shell", "meterpreter" ] )) register_options( [ OptString.new('APIKEY', [true, "VirusTotal API key", '501caf66349cc7357eb4398ac3298fdd03dec01a3e2f3ad576525aa7b57a1987']), OptString.new('REMOTEFILE', [true, "A file to check from the remote machine"]) ], self.class) end def rhost session.session_host end def get_report(api_key, checksum) # # We have to use Net::HTTP instead of HttpClient because of the following error: # The supplied module name is ambiguous: undefined method `register_autofilter_ports' # url = URI.parse("https://www.virustotal.com/vtapi/v2/file/report") req = Net::HTTP::Post.new(url.path, initheader={'Host'=>'www.virustotal.com'}) req.set_form_data({'apikey'=>api_key, 'resource'=>checksum}) http = Net::HTTP::new(url.host, url.port) http.use_ssl = true res = http.start {|http| http.request(req)} unless res print_error("#{rhost} - Connection timed out") return '' end case res.code when 204 print_error("#{rhost} - You have reached the request limit, please wait for one minute to try again") return '' when 403 print_error("#{rhost} - No privilege to execute this request probably due to an invalye API key") return '' end body = '' begin body = JSON.parse(res.body) rescue JSON::ParserError print_error("#{rhost} - Unable to parse the response") return body end body end def show_report(res, filename) md5 = res['md5'] || '' sha1 = res['sha1'] || '' sha256 = res['sha256'] || '' print_status("#{rhost} - MD5: #{md5}") unless md5.blank? print_status("#{rhost} - SHA1: #{sha1}") unless sha1.blank? print_status("#{rhost} - SHA256: #{sha256}") unless sha256.blank? tbl = Rex::Ui::Text::Table.new( 'Header' => "Analysis Report: #{filename} (#{res['positives']} / #{res['total']}): #{res['sha256']}", 'Indent' => 1, 'Columns' => ['Antivirus', 'Detected', 'Version', 'Result', 'Update'] ) res['scans'].each do |result| product = result[0] detected = result[1]['detected'].to_s version = result[1]['version'] || '' sig_name = result[1]['result'] || '' timestamp = result[1]['update'] || '' tbl << [product, detected, version, sig_name, timestamp] end report_note({ :host => session, :type => 'malware.sample', :data => tbl.to_csv }) print_status tbl.to_s end def run filename = datastore['REMOTEFILE'] api_key = datastore['APIKEY'] unless file?(filename) print_error("#{rhost} - File not found: #{filename}") return end checksum = file_remote_digestsha1(filename) print_status("#{rhost} - Checking: #{filename}...") report = get_report(api_key, checksum) return if report.blank? print_status("#{rhost} - VirusTotal message: #{report['verbose_msg']}") if report['response_code'] == 1 show_report(report, File.basename(filename)) end end end