Skip to content

Commit c0cb225

Browse files
authored
Merge pull request #21677 from bwatters-r7/fix/http_relay_uripath
Fix bug where relay server failed to work if the attacker used the UR…
2 parents c9447ac + 399b302 commit c0cb225

2 files changed

Lines changed: 40 additions & 6 deletions

File tree

lib/msf/core/exploit/remote/http_server/relay.rb

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,29 @@ def initialize(info = {})
2121
OptInt.new('RELAY_TIMEOUT', [true, 'Seconds that the relay socket will wait for a response after the client has initiated communication.', 25])
2222
], self.class
2323
)
24+
deregister_options('URIPATH')
2425
@relay_clients = {}
2526
@relay_clients_mutex = Mutex.new
2627
end
2728

29+
# Every request, regardless of path, is serviced by the relay state machine - clients
30+
# get bounced between targets via 307 redirects to randomly generated paths, so there's
31+
# no single meaningful URIPATH to relay traffic through. Route the base HttpServer's
32+
# resource registration straight to #on_relay_request at '/' instead of letting it mount
33+
# its own (otherwise unused) resource_uri resource: Rex::Proto::Http::Server dispatches
34+
# requests to the longest matching path prefix, so a second, more specific resource would
35+
# silently swallow any request that happened to match it.
2836
def start_service(opts = {})
2937
@logger = opts['Logger'] || self
3038

39+
opts['Uri'] = {
40+
'Proc' => Proc.new { |cli, req| on_relay_request(cli, req) },
41+
'Path' => '/'
42+
}
43+
3144
super
3245

3346
@http_relay_service = self.service
34-
35-
relay_path = '/'
36-
add_resource(
37-
'Proc' => Proc.new { |cli, req| on_relay_request(cli, req) },
38-
'Path' => relay_path
39-
)
4047
end
4148

4249
def on_relay_request(cli, req)

spec/lib/msf/core/exploit/remote/relay/http_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,33 @@ def get_client_state(server)
145145
end
146146
end
147147

148+
describe '#start_service' do
149+
let(:service_double) do
150+
service = double('service')
151+
allow(service).to receive(:server_name=)
152+
allow(service).to receive(:add_resource)
153+
service
154+
end
155+
156+
before do
157+
allow(Rex::ServiceManager).to receive(:start).and_return(service_double)
158+
end
159+
160+
it 'mounts a single catch-all resource at "/" regardless of URIPATH' do
161+
relay_server.datastore['URIPATH'] = '/custom-uripath'
162+
163+
mounted_paths = []
164+
allow(service_double).to receive(:add_resource) { |path, _opts| mounted_paths << path }
165+
166+
relay_server.start_service
167+
168+
# A second resource mounted at the (longer, more specific) URIPATH would silently swallow
169+
# any request matching it, since Rex::Proto::Http::Server routes to the longest matching
170+
# path prefix and the base HttpServer#on_request_uri handler for that resource is a no-op.
171+
expect(mounted_paths).to eq(['/'])
172+
end
173+
end
174+
148175
describe 'Target Iteration and Exhaustion' do
149176
let(:req1) { create_request("NTLM #{type1_b64}") }
150177
let(:req3) { create_request("NTLM #{type3_b64}") }

0 commit comments

Comments
 (0)