diff --git a/lib/metasploit/framework/login_scanner/ipboard.rb b/lib/metasploit/framework/login_scanner/ipboard.rb index b12b7c5284..4d73da0b8d 100644 --- a/lib/metasploit/framework/login_scanner/ipboard.rb +++ b/lib/metasploit/framework/login_scanner/ipboard.rb @@ -46,16 +46,22 @@ module Metasploit base_uri = uri end - auth_uri = "#{base_uri}/index.php?app=core&module=global§ion=login&do=process" + auth_uri = "#{base_uri}/index.php" request = http_client.request_cgi( - 'uri' => auth_uri, - 'method' => 'POST', - 'vars_post' => { - 'auth_key' => server_nonce, - 'ips_username' => credential.public, - 'ips_password' => credential.private - }, + 'uri' => auth_uri, + 'method' => 'POST', + 'vars_get' => { + 'app' => 'core', + 'module' => 'global', + 'section' => 'login', + 'do' => 'process' + }, + 'vars_post' => { + 'auth_key' => server_nonce, + 'ips_username' => credential.public, + 'ips_password' => credential.private + } ) response = http_client.send_recv(request) diff --git a/spec/lib/metasploit/framework/login_scanner/ipboard_spec.rb b/spec/lib/metasploit/framework/login_scanner/ipboard_spec.rb index b04fbd2b53..8d70990e4e 100644 --- a/spec/lib/metasploit/framework/login_scanner/ipboard_spec.rb +++ b/spec/lib/metasploit/framework/login_scanner/ipboard_spec.rb @@ -3,8 +3,103 @@ require 'metasploit/framework/login_scanner/ipboard' describe Metasploit::Framework::LoginScanner::IPBoard do + subject { described_class.new } + it_behaves_like 'Metasploit::Framework::LoginScanner::Base', has_realm_key: true, has_default_realm: false it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket' it_behaves_like 'Metasploit::Framework::LoginScanner::HTTP' + context "#attempt_login" do + + let(:username) { 'admin' } + let(:password) { 'password' } + let(:server_nonce) { 'nonce' } + + let(:creds) do + Metasploit::Framework::Credential.new( + paired: true, + public: username, + private: password + ) + end + + let(:invalid_creds) do + Metasploit::Framework::Credential.new( + paired: true, + public: 'username', + private: 'novalid' + ) + end + + context "when Rex::Proto::Http::Client#connect raises Rex::ConnectionError" do + it 'returns status Metasploit::Model::Login::Status::UNABLE_TO_CONNECT' do + allow_any_instance_of(Rex::Proto::Http::Client).to receive(:connect).and_raise(Rex::ConnectionError) + expect(subject.attempt_login(creds).status).to eq(Metasploit::Model::Login::Status::UNABLE_TO_CONNECT) + end + end + + context "when Rex::Proto::Http::Client#connect raises Timeout::Error" do + it 'returns status Metasploit::Model::Login::Status::UNABLE_TO_CONNECT' do + allow_any_instance_of(Rex::Proto::Http::Client).to receive(:connect).and_raise(Timeout::Error) + expect(subject.attempt_login(creds).status).to eq(Metasploit::Model::Login::Status::UNABLE_TO_CONNECT) + end + end + + context "when Rex::Proto::Http::Client#connect raises EOFError" do + it 'returns status Metasploit::Model::Login::Status::UNABLE_TO_CONNECT' do + allow_any_instance_of(Rex::Proto::Http::Client).to receive(:connect).and_raise(EOFError) + expect(subject.attempt_login(creds).status).to eq(Metasploit::Model::Login::Status::UNABLE_TO_CONNECT) + end + end + + context "when valid IPBoard application" do + before :each do + allow_any_instance_of(Rex::Proto::Http::Client).to receive(:send_recv) do |cli, req| + + if req.opts['uri'] && req.opts['uri'].include?('index.php') && + req.opts['vars_get'] && + req.opts['vars_get']['app'] && + req.opts['vars_get']['app'] == 'core' && + req.opts['vars_get']['module'] && + req.opts['vars_get']['module'] == 'global' && + req.opts['vars_get']['section'] && + req.opts['vars_get']['section'] == 'login' && + req.opts['vars_get']['do'] && + req.opts['vars_get']['do'] == 'process' && + req.opts['vars_post'] && + req.opts['vars_post']['auth_key'] && + req.opts['vars_post']['auth_key'] == server_nonce && + req.opts['vars_post']['ips_username'] && + req.opts['vars_post']['ips_username'] == username && + req.opts['vars_post']['ips_password'] && + req.opts['vars_post']['ips_password'] == password + res = Rex::Proto::Http::Response.new(200) + res.headers['Set-Cookie'] = 'ipsconnect=ipsconnect_value;Path=/;,coppa=coppa_value;Path=/;' + elsif req.opts['uri'] && req.opts['uri'].include?('index.php') && req.opts['method'] == 'POST' + res = Rex::Proto::Http::Response.new(404) + else + res = Rex::Proto::Http::Response.new(200) + res.body = "name='auth_key' value='#{server_nonce}'" + end + + res + end + end + + context "when valid login" do + it 'returns status Metasploit::Model::Login::Status::SUCCESSFUL' do + expect(subject.attempt_login(creds).status).to eq(Metasploit::Model::Login::Status::SUCCESSFUL) + end + end + + context "when invalid login" do + it 'returns status Metasploit::Model::Login::Status::INCORRECT' do + expect(subject.attempt_login(invalid_creds).status).to eq(Metasploit::Model::Login::Status::INCORRECT) + end + end + + end + end + + end \ No newline at end of file