2014-01-09 00:26:40 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2014-01-09 00:26:40 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
require 'net/http'
|
|
|
|
require 'uri'
|
|
|
|
|
2016-03-07 19:19:55 +00:00
|
|
|
class Metasploit3 < Msf::Post
|
2014-01-09 00:26:40 +00:00
|
|
|
|
|
|
|
include Msf::Post::File
|
|
|
|
|
|
|
|
def initialize(info={})
|
|
|
|
super( update_info( info,
|
|
|
|
'Name' => 'Multi Gather Malware Verifier',
|
|
|
|
'Description' => %q{
|
2014-01-10 10:02:01 +00:00
|
|
|
This module will check a file for malware on VirusTotal based on the checksum.
|
2014-01-09 00:26:40 +00:00
|
|
|
},
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'Author' => [ 'sinn3r'],
|
|
|
|
'Platform' => [ 'osx', 'win', 'linux' ],
|
|
|
|
'SessionTypes' => [ "shell", "meterpreter" ]
|
|
|
|
))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
2014-01-10 23:59:51 +00:00
|
|
|
OptString.new('APIKEY', [true, "VirusTotal API key", '501caf66349cc7357eb4398ac3298fdd03dec01a3e2f3ad576525aa7b57a1987']),
|
2014-01-09 00:26:40 +00:00
|
|
|
OptString.new('REMOTEFILE', [true, "A file to check from the remote machine"])
|
|
|
|
|
|
|
|
], self.class)
|
|
|
|
end
|
|
|
|
|
2014-01-09 00:41:22 +00:00
|
|
|
def rhost
|
2014-01-11 00:27:03 +00:00
|
|
|
session.session_host
|
2014-01-09 00:41:22 +00:00
|
|
|
end
|
|
|
|
|
2014-01-09 00:26:40 +00:00
|
|
|
def get_report(api_key, checksum)
|
2014-01-09 00:41:22 +00:00
|
|
|
#
|
|
|
|
# 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'
|
|
|
|
#
|
2014-01-09 00:26:40 +00:00
|
|
|
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
|
2014-01-09 00:41:22 +00:00
|
|
|
print_error("#{rhost} - Connection timed out")
|
2014-01-09 00:26:40 +00:00
|
|
|
return ''
|
|
|
|
end
|
|
|
|
|
|
|
|
case res.code
|
|
|
|
when 204
|
2014-01-09 00:41:22 +00:00
|
|
|
print_error("#{rhost} - You have reached the request limit, please wait for one minute to try again")
|
|
|
|
return ''
|
2014-01-09 00:26:40 +00:00
|
|
|
when 403
|
2014-01-09 00:41:22 +00:00
|
|
|
print_error("#{rhost} - No privilege to execute this request probably due to an invalye API key")
|
|
|
|
return ''
|
2014-01-09 00:26:40 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
body = ''
|
|
|
|
begin
|
|
|
|
body = JSON.parse(res.body)
|
2014-01-09 00:41:22 +00:00
|
|
|
rescue JSON::ParserError
|
|
|
|
print_error("#{rhost} - Unable to parse the response")
|
|
|
|
return body
|
2014-01-09 00:26:40 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
body
|
|
|
|
end
|
|
|
|
|
|
|
|
def show_report(res, filename)
|
|
|
|
md5 = res['md5'] || ''
|
|
|
|
sha1 = res['sha1'] || ''
|
|
|
|
sha256 = res['sha256'] || ''
|
|
|
|
|
2014-01-09 00:41:22 +00:00
|
|
|
print_status("#{rhost} - MD5: #{md5}") unless md5.blank?
|
|
|
|
print_status("#{rhost} - SHA1: #{sha1}") unless sha1.blank?
|
|
|
|
print_status("#{rhost} - SHA256: #{sha256}") unless sha256.blank?
|
2014-01-09 00:26:40 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2014-01-10 07:49:07 +00:00
|
|
|
report_note({
|
|
|
|
:host => session,
|
|
|
|
:type => 'malware.sample',
|
|
|
|
:data => tbl.to_csv
|
|
|
|
})
|
2014-01-09 00:26:40 +00:00
|
|
|
print_status tbl.to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
def run
|
|
|
|
filename = datastore['REMOTEFILE']
|
|
|
|
api_key = datastore['APIKEY']
|
|
|
|
|
|
|
|
unless file?(filename)
|
2014-01-09 00:41:22 +00:00
|
|
|
print_error("#{rhost} - File not found: #{filename}")
|
2014-01-09 00:26:40 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
checksum = file_remote_digestsha1(filename)
|
2014-01-09 00:41:22 +00:00
|
|
|
print_status("#{rhost} - Checking: #{filename}...")
|
2014-01-09 00:26:40 +00:00
|
|
|
report = get_report(api_key, checksum)
|
2014-01-09 00:41:22 +00:00
|
|
|
|
|
|
|
return if report.blank?
|
|
|
|
|
|
|
|
print_status("#{rhost} - VirusTotal message: #{report['verbose_msg']}")
|
2014-01-09 00:26:40 +00:00
|
|
|
if report['response_code'] == 1
|
|
|
|
show_report(report, File.basename(filename))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|