Redo BES rspec

bug/bundler_fix
wchen-r7 2015-06-09 23:45:41 -05:00
parent 6eb25743e3
commit ed69e5f902
1 changed files with 209 additions and 228 deletions

View File

@ -1,53 +1,84 @@
require 'spec_helper'
#require 'spec_helper'
require 'msf/core'
describe Msf::Exploit::Remote::BrowserExploitServer do
# When unpacked, this gives us:
# {
# "BAP.1433806920.Client.blLGFIlwYrxfvcY" =>
# {
# "source" => "script",
# "os_name" => "Windows 8.1",
# "os_vendor" => "undefined",
# "os_device" => "undefined",
# "ua_name" => "Firefox",
# "ua_ver" => "35.0",
# "arch" => "x86",
# "java" => "1.7",
# "silverlight" => "false",
# "flash" => "14.0",
# "vuln_test" => "true",
# "proxy" => false,
# "language" => "en-US,en;q=0.5",
# "tried" => true,
# "activex" => [{"clsid"=>"{D27CDB6E-AE6D-11cf-96B8-444553540000}", "method"=>"LoadMovie"}]
# }}
let(:first_packed_profile) do
"\x81\xD9%BAP.1433806920.Client.blLGFIlwYrxfvcY\x8F\xA6source\xA6script\xA7os_name\xABWindows 8.1\xA9os_vendor\xA9undefined\xA9os_device\xA9undefined\xA7ua_name\xA7Firefox\xA6ua_ver\xA435.0\xA4arch\xA3x86\xA4java\xA31.7\xABsilverlight\xA5false\xA5flash\xA414.0\xA9vuln_test\xA4true\xA5proxy\xC2\xA8language\xC4\x0Een-US,en;q=0.5\xA5tried\xC3\xA7activex\x91\x82\xA5clsid\xD9&{D27CDB6E-AE6D-11cf-96B8-444553540000}\xA6method\xA9LoadMovie"
end
let(:default_note_type_prefix) do
MessagePack.unpack(first_packed_profile).keys.first.split('.')[0,3] * "."
end
let(:first_profile_tag) do
MessagePack.unpack(first_packed_profile).keys.first.split('.')[3]
end
let(:first_profile_info) do
MessagePack.unpack(first_packed_profile).values.first
end
let(:cli) do
sock = Rex::Socket::Tcp
allow(sock).to receive(:peerhost).and_return('0.0.0.0')
allow(sock).to receive(:peerport).and_return(4444)
sock
end
def create_fake_note(tag, data)
note = double('note')
allow(note).to receive(:ntype).and_return(tag)
allow(note).to receive(:data).and_return(data)
note
end
before(:each) do
allow_any_instance_of(described_class).to receive(:vprint_status)
@notes = [create_fake_note(first_profile_tag, first_packed_profile)]
end
subject(:server) do
mod = Msf::Exploit::Remote.allocate
mod.extend described_class
mod.send(:initialize, {})
mod.send(:initialize)
mod.send(:datastore=, {'NoteTypePrefix' => default_note_type_prefix})
mod
end
let(:service_double) do
service = double('service')
service.stub(:server_name=)
service.stub(:add_resource)
allow(service).to receive(:server_name=)
allow(service).to receive(:add_resource)
service
end
let(:expected_user_agent) do
'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)'
end
let(:profile_name) do
'random'
end
let(:expected_os_name) do
'linux'
end
let(:exploit_page) do
server.instance_variable_get(:@exploit_receiver_page)
end
let(:expected_profile) do
{
:source =>'script',
:os_name =>'Windows XP',
:ua_name =>'MSIE',
:ua_ver =>'8.0',
:arch =>'x86',
:office =>'null',
:activex => [ {clsid: '{D27CDB6E-AE6D-11cf-96B8-444553540000}', method: 'LoadMovie'} ],
:proxy => false,
:language => 'en-us',
:tried => true
}
end
before do
Rex::ServiceManager.stub(:start => service_double)
end
@ -58,7 +89,8 @@ describe Msf::Exploit::Remote::BrowserExploitServer do
it_should_behave_like 'Msf::Exploit::JSObfu'
describe "#get_module_resource" do
describe '#get_module_resource' do
it "should give me a URI to access the exploit page" do
module_resource = server.get_module_resource
expect(module_resource).to include(exploit_page)
@ -67,127 +99,68 @@ describe Msf::Exploit::Remote::BrowserExploitServer do
describe '#has_bad_activex?' do
context 'when there is a bad activex' do
let(:js_ax_value) { "#{expected_profile[:activex][0][:clsid]}=>#{expected_profile[:activex][0][:method]}=>false" }
let(:js_ax_value) { "#{first_profile_info['activex'][0][:clsid]}=>#{first_profile_info['activex'][0][:method]}=>false" }
it 'returns false' do
expect(server.has_bad_activex?(js_ax_value)).to be_truthy
end
end
context 'when there is no bad activex' do
let(:js_ax_value) { "#{expected_profile[:activex][0][:clsid]}=>#{expected_profile[:activex][0][:method]}=>true" }
let(:js_ax_value) { "#{first_profile_info['activex'][0][:clsid]}=>#{first_profile_info['activex'][0][:method]}=>true" }
it 'returns true' do
expect(server.has_bad_activex?(js_ax_value)).to be_falsey
end
end
end
describe "#get_bad_requirements" do
let(:rejected_requirements) do
server.get_bad_requirements(fake_profile)
describe '#get_bad_requirements' do
let(:this_profile) do
MessagePack.unpack(first_packed_profile)
end
context 'when given the expected profile' do
it "should not contain any bad requirements" do
expect(server.get_bad_requirements(expected_profile)).to eq([])
let(:requirements) { {} }
before(:each) do
r = server.instance_variable_get(:@requirements)
requirements.each_pair do |key, value|
r[key] = value
end
server.instance_variable_set(:@requirements, r)
end
context 'when all requirements are met' do
let(:requirements) { first_profile_info }
it 'returns an empty bad requirement array' do
expect(server.get_bad_requirements(this_profile)).to be_empty
end
end
context 'when attempting to match :os_name' do
let(:fake_profile) do
{ :os_name => expected_os_name }
end
before do
server.instance_variable_set(:@requirements, {:os_name => /win/i})
end
it "identifies :os_name as a requirement not met" do
expect(rejected_requirements).to eq([:os_name])
context 'when the os_name requirement is not met' do
let(:requirements) { {'os_name'=>'Linux'} }
it 'returns os_name in the array as a bad requirement' do
expect(server.get_bad_requirements(this_profile)).to eq(['os_name'])
end
end
context 'when attempting to match :ua_ver' do
context 'against version 25.0' do
let(:expected_ua_ver) { '25.0' }
let(:fake_profile) do
{ :ua_ver => expected_ua_ver }
end
before do
server.instance_variable_set(:@requirements, {:ua_ver => ua_ver})
end
context "with the regex /26\.0$/" do
let(:ua_ver) { /26\.0$/ }
it "should reject :ua_ver" do
expect(rejected_requirements).to include(:ua_ver)
end
end
context "with the regex /25\.0$/" do
let(:ua_ver) { /25\.0$/ }
it "should accept :ua_ver" do
expect(rejected_requirements).not_to include(:ua_ver)
end
end
context "with a Proc that checks if version is between 1-5" do
let(:ua_ver) { lambda{ |ver| ver.to_i.between?(1, 5) } }
it "should reject :ua_ver" do
expect(rejected_requirements).to include(:ua_ver)
end
end
context "with a Proc that checks if version is between 20-26" do
let(:ua_ver) { lambda{ |ver| ver.to_i.between?(20, 26) } }
it "should accept :ua_ver" do
expect(rejected_requirements).not_to include(:ua_ver)
end
end
context 'when a Linux regex cannot match a Winodws os_name' do
let(:requirements) { {'os_name'=>/Linux/} }
it 'returns os_name in the array as a bad requirement' do
expect(server.get_bad_requirements(this_profile)).to eq(['os_name'])
end
end
end
describe "#init_profile" do
it "should initialize an empety profile for tag 'random'" do
server.init_profile(profile_name)
ivar_target_profile = server.instance_variable_get(:@target_profiles)
expect(ivar_target_profile).to eq({profile_name=>{}})
end
end
describe "#get_profile" do
it "should return nil when a profile isn't found" do
server.init_profile(profile_name)
p = server.get_profile("non_existent_profile")
expect(p).to be_nil
end
it "returns a profile if found" do
server.init_profile(profile_name)
p = server.get_profile(profile_name)
expect(p).to eq({})
end
end
describe "#update_profile" do
it "updates my target profile's :os_name information" do
server.init_profile(profile_name)
profile = server.get_profile(profile_name)
server.update_profile(profile, :os_name, expected_os_name)
profile = server.get_profile(profile_name)
expect(profile[:os_name]).to eq(expected_os_name)
end
end
describe "#get_detection_html" do
describe '#get_detection_html' do
it "returns the detection code that the client will get" do
expected_user_agent = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)'
html = server.get_detection_html(expected_user_agent)
expect(html).not_to eq('')
end
end
describe "#on_request_exploit" do
describe '#on_request_exploit' do
it "raises a NoMethodError if called" do
fake_cli = nil
fake_request = nil
@ -198,130 +171,148 @@ describe Msf::Exploit::Remote::BrowserExploitServer do
end
end
describe "#get_target" do
describe '#get_target' do
it "returns a target" do
#
# Using Object for Msf::Module::Target
#
expected_object = Object
expected_object = double('Msf::Module::Target')
server.instance_variable_set(:@target, expected_object)
server.get_target.should eq(expected_object)
end
end
describe "#try_set_target" do
it "Sets a target based on requirements" do
#
# This testcase needs to be better somehow, but not sure how to actually create
# a Msf::Module::Target. All we're able to test here is making sure the method
# doesn't raise anything by exercising the code.
#
server.instance_variable_set(:@requirements, {:os_name => /win/i})
server.instance_variable_set(:@target, Object)
server.try_set_target(expected_profile)
server.get_target.should eq(Object)
describe '#try_set_target' do
let(:fake_targets) do
target = double('Msf::Module::Target')
allow(target).to receive(:opts) { first_profile_info }
[target]
end
before(:each) do
allow_any_instance_of(described_class).to receive(:targets) { fake_targets }
end
context 'when requirements match a target' do
it 'sets @target' do
expect(server.get_target).to be_nil
server.try_set_target(MessagePack.unpack(first_packed_profile))
expect(server.get_target).to eq(fake_targets.first)
end
end
end
describe "#extract_requirements" do
it "finds all the recognizable keys" do
requirements = {:os_name=>"Windows XP", :ua_name=>"MSIE", :ua_ver=>"8.0"}
matches = server.extract_requirements(requirements)
expect(matches).to eq(requirements)
describe 'extract_requirements' do
context 'when a recognizable requirement is given' do
it 'returns a hash that contains the recognizable requirement' do
expected_hash = {'os_name'=>'Linux'}
expect(server.extract_requirements(expected_hash)).to eq(expected_hash)
end
end
it "makes sure the keys are always symbols" do
requirements = {'os_name'=>"Windows XP", 'ua_name'=>"MSIE"}
matches = server.extract_requirements(requirements)
matches.each do |k,v|
expect(k.class).to eq(Symbol)
context 'when a unrecognizable requirement is given' do
it 'returns a hash that does not have the unrecognizable requirement' do
bad_hash = {'UNKNOWN_KEY'=>'VALUE'}
expect(server.extract_requirements(bad_hash)).to be_empty
end
end
end
describe '#retrieve_tag' do
context 'when the browser has a cookie that contains our tag' do
let(:tag) do
'tag'
end
let(:cookie) do
"__ua=#{tag};"
end
let(:cli_request) do
req = Rex::Proto::Http::Request.new
req.headers['Cookie'] = cookie
req
end
it 'returns the tag from the cookie' do
expect(server.retrieve_tag(cli, cli_request)).to eq(tag)
end
end
context 'when the browser does not have a tag' do
let(:cli_request) do
Rex::Proto::Http::Request.new
end
it 'returns a new one in MD5' do
expect(server.retrieve_tag(cli, cli_request)).to match(/^[0-9a-f]{32}$/)
end
end
end
describe '#on_request_uri' do
let(:cli) { double(:peerhost => '0.0.0.0') }
let(:cookie) { '' }
let(:headers) { {'Cookie' => cookie, 'User-Agent' => ''} }
let(:body) { '' }
let(:cookie_name) { Msf::Exploit::Remote::BrowserExploitServer::DEFAULT_COOKIE_NAME }
let(:request) do
double(:body => body, :headers => headers, :uri => server.get_resource )
before(:each) do
allow(server).to receive(:get_profile_info) { MessagePack.unpack(first_packed_profile) }
allow(server).to receive(:init_profile).with(kind_of(String))
allow(server).to receive(:update_profile)
allow(server).to receive(:process_browser_info)
allow(server).to receive(:send_response) { @send_response_called = true }
allow(server).to receive(:send_redirect) { @send_redirect_called = true }
allow(server).to receive(:send_not_found) { @send_not_found_called = true}
allow(server).to receive(:on_request_exploit) { @on_request_exploit_called = true }
allow(server).to receive(:on_request_exploit) { @on_request_exploit_called = true }
end
before do
server.stub(:send_redirect)
server.stub(:send_response)
server.stub(:send_not_found)
after(:each) do
@send_response_called = false
@send_redirect_called = false
@on_request_exploit_called = false
@send_not_found_called = false
@on_request_exploit_called = false
end
context 'when a new visitor requests the exploit' do
before { JSObfu.disabled = true }
after { JSObfu.disabled = false }
it 'calls send_response once' do
server.should_receive(:send_response).once
server.on_request_uri(cli, request)
end
it 'serves the os.js detection script' do
server.should_receive(:send_response) do |cli, html, headers|
expect(html).to include('os_detect')
end
server.on_request_uri(cli, request)
context 'when / is requested' do
it 'sends the information gathering page' do
cli_request = Rex::Proto::Http::Request.new
server.on_request_uri(cli, cli_request)
expect(@send_redirect_called).to be_truthy
end
end
context 'when a returning visitor requests the exploit' do
let(:body) { '' }
let(:tag) { 'joe' }
let(:cookie) { "#{cookie_name}=#{tag}" }
before { server.init_profile(tag) }
it 'calls send_redirect once' do
server.should_receive(:send_redirect).once
server.on_request_uri(cli, request)
end
it 'redirects to the exploit URL' do
server.should_receive(:send_redirect) do |cli, url|
expect(url).to end_with("#{exploit_page}/")
end
server.on_request_uri(cli, request)
context 'when info_receiver_page is requested' do
it 'sends an empty page' do
info_receiver_page_var = server.instance_variable_get(:@info_receiver_page)
cli_request = Rex::Proto::Http::Request.new
cli_request.uri = info_receiver_page_var
server.on_request_uri(cli, cli_request)
expect(@send_response_called).to be_truthy
end
end
context 'when a returning visitor from a previous msf run requests the exploit' do
let(:body) { '' }
let(:tag) { 'joe' }
let(:cookie) { "#{cookie_name}=#{tag}" }
before { JSObfu.disabled = true }
after { JSObfu.disabled = false }
it 'calls send_response once' do
server.should_receive(:send_response).once
server.on_request_uri(cli, request)
end
it 'serves the os.js detection script' do
server.should_receive(:send_response) do |cli, html, headers|
expect(html).to include('os_detect')
end
server.on_request_uri(cli, request)
context 'when noscript_receiver_page is requested' do
it 'sends a not-found' do
noscript_receiver_page_var = server.instance_variable_get(:@noscript_receiver_page)
cli_request = Rex::Proto::Http::Request.new
cli_request.uri = noscript_receiver_page_var
server.on_request_uri(cli, cli_request)
expect(@send_not_found_called).to be_truthy
end
end
context 'when exploit_receiver_page is requested' do
it 'calls on_request_exploit' do
exploit_receiver_page_var = server.instance_variable_get(:@exploit_receiver_page)
cli_request = Rex::Proto::Http::Request.new
cli_request.uri = exploit_receiver_page_var
server.on_request_uri(cli, cli_request)
expect(@on_request_exploit_called).to be_truthy
end
end
end
describe '#get_payload' do
let(:cli) {
Rex::Socket::Tcp
}
before(:each) do
allow(cli).to receive(:peerhost).and_return('0.0.0.0')
allow(cli).to receive(:peerport).and_return(4444)
target = double('Msf::Module::Target')
allow(target).to receive(:arch).and_return(nil)
allow(server).to receive(:get_target).and_return(target)
end
let(:encoded) { '@EXE@' }
@ -330,25 +321,15 @@ describe Msf::Exploit::Remote::BrowserExploitServer do
double(:encoded => encoded, :arch => ['x86'])
}
let(:x86_64_payload) {
double(:encoded => encoded, :arch => ['x86_64'])
let(:normalized_profile_info) {
first_profile_info.inject({}){|data,(k,v)| data[k.to_sym] = v; data}
}
context 'when the payload supports the visitor\'s browser architecture' do
it 'returns a payload' do
allow(server).to receive(:regenerate_payload).and_return(x86_payload)
expect(server.get_payload(cli, expected_profile)).to eq(encoded)
end
end
context 'when the payload does not support the visitor\'s browser architecture' do
it 'raises a BESException' do
allow(server).to receive(:regenerate_payload).and_return(x86_64_payload)
expect{server.get_payload(cli, expected_profile)}.to raise_error(Msf::Exploit::Remote::BrowserExploitServer::BESException)
expect(server.get_payload(cli, normalized_profile_info)).to eq(encoded)
end
end
end
end
end