From 47d52a250e38261033d5b6a926f4e4e9ec08aa2b Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 25 Apr 2016 14:30:46 -0500 Subject: [PATCH] Fix #6806 and #6820 - Fix send_request_cgi! redirection This patch fixes two problems: 1. 6820 - If the HTTP server returns a relative path (example: /test), there is no host to extract, therefore the HOST header in the HTTP request ends up being empty. When the web server sees this, it might return an HTTP 400 Bad Request, and the redirection fails. 2. 6806 - If the HTTP server returns a relative path that begins with a dot, send_request_cgi! will literally send that in the GET request. Since that isn't a valid GET request path format, the redirection fails. Fix #6806 Fix #6820 --- lib/msf/core/exploit/http/client.rb | 52 ++++++++++++++----- spec/lib/msf/core/exploit/http/client_spec.rb | 32 ++++++++++++ 2 files changed, 70 insertions(+), 14 deletions(-) diff --git a/lib/msf/core/exploit/http/client.rb b/lib/msf/core/exploit/http/client.rb index 5b57e2e408..5d6636e155 100644 --- a/lib/msf/core/exploit/http/client.rb +++ b/lib/msf/core/exploit/http/client.rb @@ -1,4 +1,5 @@ # -*- coding: binary -*- + require 'uri' require 'digest' require 'rex/proto/ntlm/crypt' @@ -370,24 +371,47 @@ module Exploit::Remote::HttpClient return res unless res && res.redirect? && redirect_depth > 0 redirect_depth -= 1 - location = res.redirection - return res if location.nil? - - opts['redirect_uri'] = location - opts['uri'] = location.path - opts['rhost'] = location.host - opts['vhost'] = location.host - opts['rport'] = location.port - - if location.scheme == 'https' - opts['ssl'] = true - else - opts['ssl'] = false - end + return res if res.redirection.nil? + reconfig_redirect_opts!(res, opts) send_request_cgi!(opts, actual_timeout, redirect_depth) end + + # Modifies the HTTP request options for a redirection. + # + # @param res [Rex::Proto::HTTP::Response] HTTP Response. + # @param opts [Hash] The HTTP request options to modify. + # @return [void] + def reconfig_redirect_opts!(res, opts) + location = res.redirection + + if location.relative? + parent_path = File.dirname(opts['uri'].to_s) + parent_path = '/' if parent_path == '.' + new_redirect_uri = normalize_uri(parent_path, location.path.gsub(/^\./, '')) + opts['redirect_uri'] = new_redirect_uri + opts['uri'] = new_redirect_uri + opts['rhost'] = datastore['RHOST'] + opts['vhost'] = opts['vhost'] || opts['rhost'] || self.vhost() + opts['rport'] = datastore['RPORT'] + + opts['ssl'] = ssl + else + opts['redirect_uri'] = location + opts['uri'] = location.path + opts['rhost'] = location.host + opts['vhost'] = location.host + opts['rport'] = location.port + + if location.scheme == 'https' + opts['ssl'] = true + else + opts['ssl'] = false + end + end + end + # # Combine the user/pass into an auth string for the HTTP Client # diff --git a/spec/lib/msf/core/exploit/http/client_spec.rb b/spec/lib/msf/core/exploit/http/client_spec.rb index dc7705294d..27756e1841 100644 --- a/spec/lib/msf/core/exploit/http/client_spec.rb +++ b/spec/lib/msf/core/exploit/http/client_spec.rb @@ -12,6 +12,38 @@ RSpec.describe Msf::Exploit::Remote::HttpClient do mod end + describe '#reconfig_redirect_opts!' do + context 'when URI is http://127.0.0.1/test/redirect.php' do + it 'should return /test/redirect.php as the URI path' do + res = Rex::Proto::Http::Response.new + allow(res).to receive(:headers).and_return({'Location'=>'http://127.0.0.1/test/redirect.php'}) + opts = {} + subject.reconfig_redirect_opts!(res, opts) + expect(opts['uri']).to eq('/test/redirect.php') + end + end + + context 'when URI is /test/redirect.php' do + it 'should return /test/redirect.php' do + res = Rex::Proto::Http::Response.new + allow(res).to receive(:headers).and_return({'Location'=>'/test/redirect.php'}) + opts = {} + subject.reconfig_redirect_opts!(res, opts) + expect(opts['uri']).to eq('/test/redirect.php') + end + end + + context 'when URI is ./redirect.php' do + it 'should return /redirect.php' do + res = Rex::Proto::Http::Response.new + allow(res).to receive(:headers).and_return({'Location'=>'./redirect.php'}) + opts = {} + subject.reconfig_redirect_opts!(res, opts) + expect(opts['uri']).to eq('/redirect.php') + end + end + end + describe '#vhost' do let(:rhost) do