2014-01-08 08:34:43 +00:00
|
|
|
require 'spec_helper'
|
|
|
|
|
2015-10-06 20:42:31 +00:00
|
|
|
load Metasploit::Framework.root.join('tools/exploit/virustotal.rb').to_path
|
2014-01-08 08:34:43 +00:00
|
|
|
|
|
|
|
require 'msfenv'
|
|
|
|
require 'msf/base'
|
|
|
|
require 'digest/sha2'
|
|
|
|
|
2015-10-16 20:57:04 +00:00
|
|
|
RSpec.describe VirusTotalUtility do
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context "Classes" do
|
2014-01-08 08:34:43 +00:00
|
|
|
let(:api_key) do
|
|
|
|
'FAKE_API_KEY'
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:filename) do
|
|
|
|
'MALWARE.EXE'
|
|
|
|
end
|
|
|
|
|
|
|
|
let(:malware_data) do
|
|
|
|
'DATA'
|
|
|
|
end
|
|
|
|
|
2014-01-12 21:54:56 +00:00
|
|
|
describe VirusTotalUtility::ToolConfig do
|
2014-01-08 19:22:38 +00:00
|
|
|
context "Class methods" do
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
let(:tool_config) do
|
2014-01-12 21:54:56 +00:00
|
|
|
VirusTotalUtility::ToolConfig.new
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
context ".Initializer" do
|
|
|
|
it "should init the config file path as Metasploit's default config path" do
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(tool_config.instance_variable_get(:@config_file)).to eq(Msf::Config.config_file)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
it "should init the group name as 'VirusTotal'" do
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(tool_config.instance_variable_get(:@group_name)).to eq('VirusTotal')
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
end
|
|
|
|
|
2014-01-12 21:54:56 +00:00
|
|
|
describe VirusTotalUtility::VirusTotal do
|
2014-01-08 19:22:38 +00:00
|
|
|
context "Class methods" do
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
let(:malware_sha256) do
|
|
|
|
Digest::SHA256.hexdigest(malware_data)
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
let(:sample) do
|
|
|
|
{
|
|
|
|
'filename' => filename,
|
|
|
|
'data' => malware_data,
|
|
|
|
'sha256' => malware_sha256
|
|
|
|
}
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
let(:boundary) do
|
|
|
|
'THEREAREMANYLIKEITBUTTHISISMYDATA'
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
let(:scan_sample_opts) do
|
|
|
|
opts = {
|
|
|
|
'boundary' => boundary,
|
|
|
|
'api_key' => api_key,
|
|
|
|
'filename' => filename,
|
|
|
|
'data' => malware_data
|
|
|
|
}
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
return opts
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
let(:retrieve_report_opts) do
|
|
|
|
opts = {
|
|
|
|
'uri' => '/vtapi/v2/file/report',
|
|
|
|
'method' => 'POST',
|
|
|
|
'vhost' => 'www.virustotal.com',
|
|
|
|
'vars_post' => {
|
|
|
|
'apikey' => api_key,
|
|
|
|
'resource' => malware_sha256
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return opts
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
let(:vt) do
|
|
|
|
file = double(File, read: malware_data)
|
2015-10-21 14:45:17 +00:00
|
|
|
allow(File).to receive(:open).with(filename, 'rb') {|&block| block.yield file}
|
2014-01-12 21:54:56 +00:00
|
|
|
VirusTotalUtility::VirusTotal.new({'api_key'=>api_key, 'sample'=>filename})
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context ".Initializer" do
|
|
|
|
it "should have an API key" do
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(vt.instance_variable_get(:@api_key)).to eq(api_key)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
it "should have a checksum for the malware sample" do
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(vt.instance_variable_get(:@sample_info)['sha256']).to eq(malware_sha256)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context "._load_sample" do
|
|
|
|
it "should contain sample info including data, filename, and sha256" do
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(vt.send(:_load_sample, filename)).to eq(sample)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context ".scan_sample" do
|
|
|
|
it "should return with data" do
|
2015-10-20 18:45:31 +00:00
|
|
|
expect(vt).to receive(:_execute_request).and_return('')
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(vt.scan_sample).to eq('')
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context ".retrieve_report" do
|
|
|
|
it "should return with data" do
|
2015-10-20 18:45:31 +00:00
|
|
|
expect(vt).to receive(:_execute_request).and_return('')
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(vt.retrieve_report).to eq('')
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context "._execute_request" do
|
|
|
|
it "should return status code 204" do
|
|
|
|
res = double(Rex::Proto::Http::Response)
|
2015-10-20 18:45:31 +00:00
|
|
|
expect(res).to receive(:code).and_return(204)
|
|
|
|
expect(vt).to receive(:send_request_cgi).with(scan_sample_opts).and_return(res)
|
2014-01-08 19:22:38 +00:00
|
|
|
expect { vt.send(:_execute_request, scan_sample_opts) }.to raise_error(RuntimeError)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "should return status code 403" do
|
|
|
|
res = double(Rex::Proto::Http::Response)
|
2015-10-20 18:45:31 +00:00
|
|
|
expect(res).to receive(:code).and_return(403)
|
|
|
|
expect(vt).to receive(:send_request_cgi).with(scan_sample_opts).and_return(res)
|
2014-01-08 19:22:38 +00:00
|
|
|
expect { vt.send(:_execute_request, scan_sample_opts) }.to raise_error(RuntimeError)
|
|
|
|
end
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context "._create_upload_data" do
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
let(:form_opts) do
|
|
|
|
{
|
|
|
|
'boundary' => boundary,
|
|
|
|
'api_key' => api_key,
|
|
|
|
'filename' => filename,
|
|
|
|
'data' => malware_data
|
|
|
|
}
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2015-12-31 22:56:13 +00:00
|
|
|
before(:example) do
|
2014-01-08 19:22:38 +00:00
|
|
|
@upload_data = vt.send(:_create_upload_data, form_opts)
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
it "should create form-data with a boundary" do
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(@upload_data).to match(/#{boundary}/)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "should create form-data with the API key" do
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(@upload_data).to match(/#{api_key}/)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
it "should create form-data with the malware filename" do
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(@upload_data).to match(/#{filename}/)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "should create form-data with the malware data" do
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(@upload_data).to match(/#{malware_data}/)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2014-01-12 21:54:56 +00:00
|
|
|
describe VirusTotalUtility::Driver do
|
2014-01-08 19:22:38 +00:00
|
|
|
before do
|
|
|
|
$stdin = StringIO.new("Y\n")
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
after do
|
|
|
|
$stdin = STDIN
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
let(:driver) do
|
|
|
|
argv = "-k #{api_key} -f #{filename}".split
|
|
|
|
options = {
|
|
|
|
'samples' => filename,
|
|
|
|
'api_key' => api_key,
|
|
|
|
'delay' => 60
|
|
|
|
}
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2015-10-20 18:45:31 +00:00
|
|
|
expect(VirusTotalUtility::OptsConsole).to receive(:parse).with(anything).and_return(options)
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2015-10-21 14:45:17 +00:00
|
|
|
tool_config = instance_double(
|
|
|
|
VirusTotalUtility::ToolConfig,
|
|
|
|
has_privacy_waiver?: true,
|
|
|
|
load_api_key: api_key,
|
|
|
|
save_api_key: nil,
|
|
|
|
save_privacy_waiver: nil
|
|
|
|
)
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2015-10-20 18:45:31 +00:00
|
|
|
expect(VirusTotalUtility::ToolConfig).to receive(:new).and_return(tool_config)
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
d = nil
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2015-10-21 14:45:17 +00:00
|
|
|
get_stdout {
|
2014-01-12 21:54:56 +00:00
|
|
|
d = VirusTotalUtility::Driver.new
|
2014-01-08 19:22:38 +00:00
|
|
|
}
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
d
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context ".Class methods" do
|
2014-01-08 08:34:43 +00:00
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context ".initialize" do
|
|
|
|
it "should return a Driver object" do
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(driver.class).to eq(VirusTotalUtility::Driver)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
end
|
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context ".ask_privacy" do
|
|
|
|
it "should have a link of VirusTotal's terms of service" do
|
|
|
|
tos = 'https://www.virustotal.com/en/about/terms-of-service'
|
|
|
|
out = get_stdout { driver.ack_privacy }
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(out).to match(/#{tos}/)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
end
|
|
|
|
|
2014-01-08 19:22:38 +00:00
|
|
|
context ".generate_report" do
|
|
|
|
it "should show a report" do
|
|
|
|
res = {
|
|
|
|
"scans" => {
|
|
|
|
"Bkav" => { "detected" => false, "version" => "1.3.0.4613", "result" => nil, "update" => "20140107" }
|
2014-01-12 21:45:16 +00:00
|
|
|
},
|
|
|
|
"response_code" => 1
|
2014-01-08 19:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out = get_stdout { driver.generate_report(res, filename) }
|
2015-10-20 19:37:18 +00:00
|
|
|
expect(out).to match(/#{res['scans']['Bkav']['version']}/)
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|
|
|
|
end
|
2014-01-08 08:34:43 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2014-01-08 19:22:38 +00:00
|
|
|
end
|