diff --git a/Gemfile.lock b/Gemfile.lock index 7c9926fc05524..94324a452ab02 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,7 +61,7 @@ PATH metasploit-concern metasploit-credential (>= 6.0.21) metasploit-model - metasploit-payloads (= 2.0.246.pre.9) + metasploit-payloads (= 2.0.246.pre.10) metasploit_data_models (>= 6.0.15) metasploit_payloads-mettle (= 1.0.48.pre.3) mqtt @@ -369,7 +369,7 @@ GEM drb mutex_m railties (>= 7.0, < 8.1) - metasploit-payloads (2.0.246.pre.9) + metasploit-payloads (2.0.246.pre.10) metasploit_data_models (6.0.18) activerecord (>= 7.0, < 8.1) activesupport (>= 7.0, < 8.1) diff --git a/lib/msf/core/payload/transport_config.rb b/lib/msf/core/payload/transport_config.rb index 2dd7943a591a1..3e37cea6aa75a 100644 --- a/lib/msf/core/payload/transport_config.rb +++ b/lib/msf/core/payload/transport_config.rb @@ -32,9 +32,14 @@ def transport_config_reverse_ipv6_tcp(opts={}) def transport_config_bind_tcp(opts={}) ds = opts[:datastore] || datastore + # For bind TCP, the payload binds on the target machine. LHOST is not meaningful + # for bind payloads and is often left blank. If unset, bind to all interfaces: + # '::' for IPv6 targets, '0.0.0.0' for IPv4. This avoids the mistake of binding + # to the target's public/external IP which may not be assigned to any local interface. + lhost = ds['LHOST'].presence || (Rex::Socket.is_ipv6?(ds['RHOST'].to_s) ? '::' : '0.0.0.0') { scheme: 'tcp', - lhost: ds['LHOST'], + lhost: lhost, lport: ds['LPORT'].to_i }.merge(timeout_config(opts)) end diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 52349ed678b74..24634175b0450 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -74,7 +74,7 @@ Gem::Specification.new do |spec| # are needed when there's no database spec.add_runtime_dependency 'metasploit-model' # Needed for Meterpreter - spec.add_runtime_dependency 'metasploit-payloads', '2.0.246.pre.9' + spec.add_runtime_dependency 'metasploit-payloads', '2.0.246.pre.10' # Needed for the next-generation POSIX Meterpreter spec.add_runtime_dependency 'metasploit_payloads-mettle', '1.0.48.pre.3' # Needed by msfgui and other rpc components diff --git a/spec/lib/msf/core/payload/transport_config_spec.rb b/spec/lib/msf/core/payload/transport_config_spec.rb new file mode 100644 index 0000000000000..88a7bf8a812cb --- /dev/null +++ b/spec/lib/msf/core/payload/transport_config_spec.rb @@ -0,0 +1,56 @@ +require 'msf/core/payload/transport_config' + +RSpec.describe Msf::Payload::TransportConfig do + subject do + obj = Object.new + obj.extend(Msf::Payload::TransportConfig) + obj + end + + def datastore_for(values) + ds = Hash.new(nil) + values.each { |k, v| ds[k] = v } + ds + end + + describe '#transport_config_bind_tcp' do + context 'when LHOST is set' do + it 'uses LHOST as the transport host' do + ds = datastore_for('LHOST' => '10.0.0.1', 'LPORT' => '4444') + config = subject.transport_config_bind_tcp(datastore: ds) + expect(config[:lhost]).to eq('10.0.0.1') + end + end + + context 'when LHOST is blank and RHOST is an IPv4 address' do + it 'binds to all IPv4 interfaces (0.0.0.0) rather than the target address' do + ds = datastore_for('LHOST' => '', 'RHOST' => '192.168.1.10', 'LPORT' => '4444') + config = subject.transport_config_bind_tcp(datastore: ds) + expect(config[:lhost]).to eq('0.0.0.0') + end + + it 'avoids a nil transport URL that breaks meterpreter initialisation' do + ds = datastore_for('LHOST' => nil, 'RHOST' => '192.168.1.10', 'LPORT' => '4444') + config = subject.transport_config_bind_tcp(datastore: ds) + expect(config[:lhost]).not_to be_nil + expect(config[:lhost]).to eq('0.0.0.0') + end + end + + context 'when LHOST is blank and RHOST is an IPv6 address' do + it 'binds to all IPv6 interfaces (::)' do + ds = datastore_for('LHOST' => '', 'RHOST' => '::1', 'LPORT' => '4444') + config = subject.transport_config_bind_tcp(datastore: ds) + expect(config[:lhost]).to eq('::') + end + end + + context 'when both LHOST and RHOST are blank' do + it 'defaults to binding on all IPv4 interfaces (0.0.0.0)' do + ds = datastore_for('LHOST' => nil, 'RHOST' => nil, 'LPORT' => '4444') + config = subject.transport_config_bind_tcp(datastore: ds) + expect(config[:lhost]).to eq('0.0.0.0') + end + end + end +end