Created
June 6, 2017 01:00
-
-
Save jduck/49183a4399e50645e327542829b2a639 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 6b4eba818a99db7cd0b268d5410b0b2e1aa1aab9 Mon Sep 17 00:00:00 2001 | |
From: "Joshua J. Drake" <[email protected]> | |
Date: Wed, 12 Nov 2014 19:31:56 -0600 | |
Subject: [PATCH] rebase http-proxy patch | |
--- | |
lib/rex/io/stream_server.rb | 9 ++ | |
lib/rex/proto.rb | 2 + | |
lib/rex/proto/http/server.rb | 7 + | |
lib/rex/proto/proxy.rb | 77 +++++++++++ | |
lib/rex/proto/proxy/http.rb | 243 +++++++++++++++++++++++++++++++++ | |
lib/rex/proto/proxy/socks4a.rb | 72 +--------- | |
modules/auxiliary/server/http_proxy.rb | 79 +++++++++++ | |
7 files changed, 422 insertions(+), 67 deletions(-) | |
create mode 100644 lib/rex/proto/proxy.rb | |
create mode 100644 lib/rex/proto/proxy/http.rb | |
create mode 100644 modules/auxiliary/server/http_proxy.rb | |
diff --git a/lib/rex/io/stream_server.rb b/lib/rex/io/stream_server.rb | |
index 188fb63..b452521 100644 | |
--- a/lib/rex/io/stream_server.rb | |
+++ b/lib/rex/io/stream_server.rb | |
@@ -102,6 +102,15 @@ module StreamServer | |
end | |
# | |
+ # Remove a client socket without closing it | |
+ # | |
+ def remove_client(client) | |
+ if (client) | |
+ clients.delete(client) | |
+ end | |
+ end | |
+ | |
+ # | |
# This method waits on the server listener thread | |
# | |
def wait | |
diff --git a/lib/rex/proto.rb b/lib/rex/proto.rb | |
index dbfd86c..39375c9 100644 | |
--- a/lib/rex/proto.rb | |
+++ b/lib/rex/proto.rb | |
@@ -7,6 +7,8 @@ require 'rex/proto/drda' | |
require 'rex/proto/iax2' | |
require 'rex/proto/kerberos' | |
+require 'rex/proto/proxy' | |
+ | |
module Rex | |
module Proto | |
diff --git a/lib/rex/proto/http/server.rb b/lib/rex/proto/http/server.rb | |
index ca0ed82..6e9e559 100644 | |
--- a/lib/rex/proto/http/server.rb | |
+++ b/lib/rex/proto/http/server.rb | |
@@ -185,6 +185,13 @@ class Server | |
end | |
# | |
+ # Remove the client without closing it. | |
+ # | |
+ def remove_client(cli) | |
+ listener.remove_client(cli) | |
+ end | |
+ | |
+ # | |
# Mounts a directory or resource as being serviced by the supplied handler. | |
# | |
def mount(root, handler, long_call = false, *args) | |
diff --git a/lib/rex/proto/proxy.rb b/lib/rex/proto/proxy.rb | |
new file mode 100644 | |
index 0000000..2980837 | |
--- /dev/null | |
+++ b/lib/rex/proto/proxy.rb | |
@@ -0,0 +1,77 @@ | |
+require 'thread' | |
+ | |
+require 'rex/proto/proxy/socks4a' | |
+require 'rex/proto/proxy/http' | |
+ | |
+module Rex | |
+module Proto | |
+module Proxy | |
+ | |
+# | |
+# A mixin for a socket to perform a relay to another socket. | |
+# | |
+module Relay | |
+ | |
+ # | |
+ # Relay data coming in from relay_sock to this socket. | |
+ # | |
+ def relay( relay_client, thread_name, relay_sock ) | |
+ @relay_client = relay_client | |
+ @relay_sock = relay_sock | |
+ | |
+ # start the relay thread (modified from Rex::IO::StreamAbstraction) | |
+ @relay_thread = Rex::ThreadFactory.spawn(thread_name, false) do | |
+ loop do | |
+ closed = false | |
+ buf = nil | |
+ | |
+ begin | |
+ s = Rex::ThreadSafe.select( [ @relay_sock ], nil, nil, 0.2 ) | |
+ if( s == nil || s[0] == nil ) | |
+ next | |
+ end | |
+ rescue | |
+ closed = true | |
+ end | |
+ | |
+ if( closed == false ) | |
+ begin | |
+ buf = @relay_sock.sysread( 32768 ) | |
+ closed = true if( buf == nil ) | |
+ rescue | |
+ closed = true | |
+ end | |
+ end | |
+ | |
+ if( closed == false ) | |
+ total_sent = 0 | |
+ total_length = buf.length | |
+ while( total_sent < total_length ) | |
+ begin | |
+ data = buf[total_sent, buf.length] | |
+ sent = self.write( data ) | |
+ if( sent > 0 ) | |
+ total_sent += sent | |
+ end | |
+ rescue | |
+ closed = true | |
+ break | |
+ end | |
+ end | |
+ end | |
+ | |
+ if( closed ) | |
+ @relay_client.stop if @relay_client | |
+ ::Thread.exit | |
+ end | |
+ end | |
+ end | |
+ | |
+ end | |
+ | |
+end | |
+ | |
+end | |
+end | |
+end | |
+ | |
diff --git a/lib/rex/proto/proxy/http.rb b/lib/rex/proto/proxy/http.rb | |
new file mode 100644 | |
index 0000000..21b7bf8 | |
--- /dev/null | |
+++ b/lib/rex/proto/proxy/http.rb | |
@@ -0,0 +1,243 @@ | |
+# | |
+# Http Proxy - jduck | |
+# | |
+ | |
+require 'rex/proto/http/server' | |
+ | |
+module Rex | |
+module Proto | |
+module Proxy | |
+ | |
+ | |
+module Request | |
+ def initialize | |
+ super | |
+ self.uri_obj = nil | |
+ end | |
+ | |
+ def uri_obj= | |
+ self.uri = uri_obj | |
+ end | |
+ | |
+ attr_accessor :uri_obj | |
+end | |
+ | |
+ | |
+module Response | |
+ def initialize | |
+ super | |
+ self.orig_req = nil | |
+ end | |
+ | |
+ def orig_req= | |
+ self.req = orig_req | |
+ end | |
+ | |
+ attr_accessor :orig_req | |
+end | |
+ | |
+ | |
+# | |
+# The Proxy::Http class | |
+# | |
+class Http < Rex::Proto::Http::Server | |
+ | |
+ # | |
+ # Callbacks that can be modified by consumers | |
+ # | |
+ def on_http_request(cli, req) | |
+ if (on_http_request_proc) | |
+ return on_http_request_proc.call(cli, req) | |
+ end | |
+ true | |
+ end | |
+ | |
+ def on_http_response(cli, res) | |
+ if (on_http_response_proc) | |
+ return on_http_response_proc.call(cli, res) | |
+ end | |
+ true | |
+ end | |
+ | |
+ def on_http_connect(cli, res) | |
+ if (on_http_connect_proc) | |
+ return on_http_connect_proc.call(cli, res) | |
+ end | |
+ true | |
+ end | |
+ | |
+ attr_accessor :on_http_request_proc | |
+ attr_accessor :on_http_response_proc | |
+ attr_accessor :on_http_connect_proc | |
+ | |
+protected | |
+ | |
+ # | |
+ # Put humpty dumpty back together again. | |
+ # | |
+ def rebuild_uri(request) | |
+ # reconstitute the requested URI based on the parts | |
+ uri = "#{request.resource}?" | |
+ | |
+ # NOTE: #normalize! screws with the request, so fix its madness. | |
+ if uri =~ /:\/[^\/]/ | |
+ uri.gsub!(':/', '://') | |
+ end | |
+ | |
+ # reconstitute the query string | |
+ vars = [] | |
+ request.qstring.each { |k,v| | |
+ vars << "#{k}=#{v}" | |
+ } | |
+ | |
+ vars_comb = vars.join('&') | |
+ vars_comb.gsub!(/ /, '+') | |
+ | |
+ # combine resource and qstring | |
+ uri << vars_comb | |
+ uri | |
+ end | |
+ | |
+ def send_ok(cli, request) | |
+ ok = Rex::Proto::Http::Response::OK.new | |
+ cli.put(ok.to_s) | |
+ end | |
+ | |
+ | |
+ # Overrides for stuff from Rex::Proto::Http::Server | |
+ | |
+ def dispatch_request(cli, request) | |
+ case request.method | |
+ | |
+ when "GET", "POST" | |
+ begin | |
+ uri = URI.parse(rebuild_uri(request)) | |
+ rescue ::Exception => e | |
+ send_e404(cli, request) | |
+ wlog("Exception in HttpProxy dispatch_request while parsing request URI: #{e.class}: #{e}") | |
+ wlog("Call Stack\n#{e.backtrace.join("\n")}") | |
+ close_client(cli) | |
+ return | |
+ | |
+ end | |
+ | |
+ # Allow callers to change the incoming request | |
+ request.extend(Request) | |
+ request.uri_obj = uri | |
+ | |
+ return if not on_http_request(cli, request) | |
+ | |
+ # Now, we must connect to the target server and repeat the request. | |
+ rcli = Rex::Proto::Http::Client.new( | |
+ uri.host, | |
+ uri.port || 80, | |
+ self.context) | |
+ | |
+ # Delete headers that end up getting duplicated. They should end up the | |
+ # same value, but having two of them still isn't helpful :-/ | |
+ request.headers.delete('Host') | |
+ request.headers.delete('Content-Length') | |
+ | |
+ # Send the request | |
+ rreq = rcli.request_raw({ | |
+ 'uri' => uri.path + "?" + uri.query, | |
+ 'method' => request.method, | |
+ 'headers' => request.headers, | |
+ 'data' => request.body, | |
+ }) | |
+ | |
+ begin | |
+ # Read the response | |
+ resp = rcli.send_recv(rreq) | |
+ | |
+ rescue ::Exception => e | |
+ send_e404(cli, request) | |
+ wlog("Exception in HttpProxy dispatch_request while relaying request: #{e.class}: #{e}") | |
+ wlog("Call Stack\n#{e.backtrace.join("\n")}") | |
+ close_client(rcli) | |
+ close_client(cli) | |
+ return | |
+ | |
+ end | |
+ | |
+ # Add the original request (prior to rebuilding) | |
+ resp.extend(Response) | |
+ resp.orig_req = request | |
+ | |
+ # Don't rescue exceptions in this, they should be shown to whoever is | |
+ # implementing on_http_response. | |
+ return if not on_http_response(cli, resp) | |
+ | |
+ begin | |
+ # Send it back to the requesting client | |
+ cli.put(resp.to_s) | |
+ | |
+ rescue ::Exception => e | |
+ send_e404(cli, request) | |
+ wlog("Exception in HttpProxy dispatch_request while relaying response: #{e.class}: #{e}") | |
+ wlog("Call Stack\n#{e.backtrace.join("\n")}") | |
+ | |
+ end | |
+ | |
+ # Bye! | |
+ close_client(rcli) | |
+ close_client(cli) | |
+ | |
+ when "CONNECT" | |
+ host,port = request.resource.split(':') | |
+ port = port.to_i | |
+ | |
+ return if not on_http_connect(cli, request) | |
+ | |
+ # only tunnel SSL requests | |
+ if port != 443 | |
+ send_e404(cli, request) | |
+ ilog("HttpProxy: Rejecting CONNECT request for #{host}:#{port} from #{cli.peerhost} ...", LEV_2); | |
+ close_client(cli) | |
+ return | |
+ end | |
+ | |
+ # Now, we must connect to the target server and relay the data... | |
+ # NOTE: according to rfc2817, the data accompanying this request should be included too. | |
+ begin | |
+ rcli = Rex::Socket::Tcp.create({ | |
+ 'PeerHost' => host, | |
+ 'PeerPort' => port, | |
+ 'Context' => self.context, | |
+ }) | |
+ | |
+ rescue ::Exception => e | |
+ wlog("Exception in Proxy dispatch_request while handling CONNECT: #{e.class}: #{e}") | |
+ wlog("Call Stack\n#{e.backtrace.join("\n")}") | |
+ | |
+ send_e404(cli, request) | |
+ close_client(cli) | |
+ return | |
+ | |
+ end | |
+ | |
+ # don't let the server's client monitor see this guy anymore | |
+ remove_client(cli) | |
+ | |
+ # Tell the client it's go time. | |
+ send_ok(cli, request) | |
+ | |
+ # Relay the data back and forth | |
+ cli.extend(Rex::Proto::Proxy::Relay) | |
+ rcli.extend(Rex::Proto::Proxy::Relay) | |
+ cli.relay(nil, "HttpProxySSLRelay (c2s)", rcli) | |
+ rcli.relay(nil, "HttpProxySSLRelay (s2c)", cli) | |
+ | |
+ # If we are given a trusted CA cert to sign with, we could also | |
+ # transparently generate a CERT and MITM the SSL data. | |
+ | |
+ end | |
+ | |
+ end | |
+ | |
+end | |
+ | |
+end | |
+end | |
+end | |
+ | |
diff --git a/lib/rex/proto/proxy/socks4a.rb b/lib/rex/proto/proxy/socks4a.rb | |
index aa02e27..e7e9004 100644 | |
--- a/lib/rex/proto/proxy/socks4a.rb | |
+++ b/lib/rex/proto/proxy/socks4a.rb | |
@@ -5,6 +5,7 @@ | |
require 'thread' | |
require 'rex/logging' | |
require 'rex/socket' | |
+require 'rex/proto/proxy' | |
module Rex | |
module Proto | |
@@ -155,69 +156,6 @@ class Socks4a | |
end | |
# | |
- # A mixin for a socket to perform a relay to another socket. | |
- # | |
- module Relay | |
- | |
- # | |
- # Relay data coming in from relay_sock to this socket. | |
- # | |
- def relay( relay_client, relay_sock ) | |
- @relay_client = relay_client | |
- @relay_sock = relay_sock | |
- # start the relay thread (modified from Rex::IO::StreamAbstraction) | |
- @relay_thread = Rex::ThreadFactory.spawn("SOCKS4AProxyServerRelay", false) do | |
- loop do | |
- closed = false | |
- buf = nil | |
- | |
- begin | |
- s = Rex::ThreadSafe.select( [ @relay_sock ], nil, nil, 0.2 ) | |
- if( s == nil || s[0] == nil ) | |
- next | |
- end | |
- rescue | |
- closed = true | |
- end | |
- | |
- if( closed == false ) | |
- begin | |
- buf = @relay_sock.sysread( 32768 ) | |
- closed = true if( buf == nil ) | |
- rescue | |
- closed = true | |
- end | |
- end | |
- | |
- if( closed == false ) | |
- total_sent = 0 | |
- total_length = buf.length | |
- while( total_sent < total_length ) | |
- begin | |
- data = buf[total_sent, buf.length] | |
- sent = self.write( data ) | |
- if( sent > 0 ) | |
- total_sent += sent | |
- end | |
- rescue | |
- closed = true | |
- break | |
- end | |
- end | |
- end | |
- | |
- if( closed ) | |
- @relay_client.stop | |
- ::Thread.exit | |
- end | |
- end | |
- end | |
- | |
- end | |
- | |
- end | |
- | |
- # | |
# Create a new client connected to the server. | |
# | |
def initialize( server, sock ) | |
@@ -309,11 +247,11 @@ class Socks4a | |
raise "Failed to handle the clients request." | |
end | |
# setup the two way relay for full duplex io | |
- @lsock.extend( Relay ) | |
- @rsock.extend( Relay ) | |
+ @lsock.extend( Rex::Proto::Proxy::Relay ) | |
+ @rsock.extend( Rex::Proto::Proxy::Relay ) | |
# start the socket relays... | |
- @lsock.relay( self, @rsock ) | |
- @rsock.relay( self, @lsock ) | |
+ @lsock.relay( self, "SOCKS4AProxyServerRelay (l2r)", @rsock ) | |
+ @rsock.relay( self, "SOCKS4AProxyServerRelay (r2l)", @lsock ) | |
rescue | |
wlog( "Client.start - #{$!}" ) | |
self.stop | |
diff --git a/modules/auxiliary/server/http_proxy.rb b/modules/auxiliary/server/http_proxy.rb | |
new file mode 100644 | |
index 0000000..5ecaae4 | |
--- /dev/null | |
+++ b/modules/auxiliary/server/http_proxy.rb | |
@@ -0,0 +1,79 @@ | |
+## | |
+# This file is part of the Metasploit Framework and may be subject to | |
+# redistribution and commercial restrictions. Please see the Metasploit | |
+# web site for more information on licensing and terms of use. | |
+# http://metasploit.com/ | |
+## | |
+ | |
+require 'thread' | |
+require 'msf/core' | |
+require 'rex/proto/proxy/http' | |
+ | |
+class Metasploit3 < Msf::Auxiliary | |
+ | |
+ include Msf::Auxiliary::Report | |
+ | |
+ def initialize(info = {}) | |
+ super(update_info(info, | |
+ 'Name' => 'HTTP Proxy Server', | |
+ 'Description' => 'This module provides an HTTP proxy server that uses the builtin Metasploit routing to relay connections.', | |
+ 'Author' => [ 'jduck' ], | |
+ 'License' => MSF_LICENSE, | |
+ 'Actions' => | |
+ [ | |
+ [ 'Proxy' ] | |
+ ], | |
+ 'PassiveActions' => | |
+ [ | |
+ 'Proxy' | |
+ ], | |
+ 'DefaultAction' => 'Proxy')) | |
+ | |
+ register_options( | |
+ [ | |
+ OptString.new( 'SRVHOST', [ true, "The address to listen on", '0.0.0.0' ] ), | |
+ OptPort.new('SRVPORT', [ true, "The daemon port to listen on", 80 ]), | |
+ ], self.class) | |
+ end | |
+ | |
+ def setup | |
+ super | |
+ @mutex = ::Mutex.new | |
+ @hproxy = nil | |
+ end | |
+ | |
+ def cleanup | |
+ @mutex.synchronize do | |
+ if( @hproxy ) | |
+ print_status( "Stopping the HTTP proxy server" ) | |
+ @hproxy.stop | |
+ @hproxy = nil | |
+ end | |
+ end | |
+ super | |
+ end | |
+ | |
+ def run | |
+ opts = { | |
+ 'ServerHost' => datastore['SRVHOST'], | |
+ 'ServerPort' => datastore['SRVPORT'], | |
+ 'Context' => {'Msf' => framework, 'MsfExploit' => self} | |
+ } | |
+ | |
+ @hproxy = Rex::Proto::Proxy::Http.new( | |
+ datastore['SRVPORT'], | |
+ datastore['SRVHOST'], | |
+ false, | |
+ { | |
+ 'Msf' => framework, | |
+ 'MsfExploit' => self | |
+ }) | |
+ | |
+ print_status( "Starting the HTTP proxy server" ) | |
+ | |
+ @hproxy.start | |
+ @hproxy.wait | |
+ end | |
+ | |
+end | |
+ | |
-- | |
1.9.1 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment