NetScaler 13.1 Vulnerability Scanner
=============================================================================================================================================
| # Title NetScaler 13.1 Vulnerability Scanner
=============================================================================================================================================
| # Title : NetScaler 13.1 Vulnerability Scanner |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.1 (64 bits) |
| # Vendor : https://www.netscaler.com/ |
=============================================================================================================================================
POC :
[+] References : https://packetstorm.news/files/id/212005/ & CVE-2025-6543
[+] Summary :
This module scans for vulnerable Citrix NetScaler (ADC) instances affected by CVE-2025-6543.
It identifies vulnerable versions through SNMP and SSH banner grabbing.
[+] Usage :
msf6 > use auxiliary/scanner/netscaler/cve_2025_6543
msf6 > set RHOSTS 192.168.1.0/24
msf6 > set THREADS 10
msf6 > run
[+] POC :
##
# Module: auxiliary/scanner/netscaler/cve_2025_6543
# Version: 1.0
# Author: indoushka
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::SNMPClient
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize(info = {})
super(update_info(info,
'Name' => 'Citrix NetScaler CVE-2025-6543 Vulnerability Scanner',
'Description' => %q{
This module scans for vulnerable Citrix NetScaler (ADC) instances affected by CVE-2025-6543.
It identifies vulnerable versions through SNMP and SSH banner grabbing.
},
'Author' => ['indoushka'],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2025-6543'],
['URL', 'https://support.citrix.com/article/CTXxxxxxx/netscaler-security-update']
],
'DisclosureDate' => '2025-11-26'
))
register_options([
OptString.new('COMMUNITY', [true, 'SNMP community string', 'public']),
OptString.new('SSH_USER', [false, 'SSH username']),
OptString.new('SSH_PASS', [false, 'SSH password']),
OptPort.new('SSH_PORT', [true, 'SSH port', 22]),
OptBool.new('USE_SNMP', [true, 'Use SNMP for detection', true]),
OptBool.new('USE_SSH', [true, 'Use SSH for detection', false]),
OptPath.new('RHOSTS_FILE', [false, 'File containing list of targets']),
OptBool.new('EXPLOIT_CHECK', [true, 'Perform basic exploit validation', false])
])
deregister_options('VERSION') # SNMP version handled internally
end
# Safe builds mapping
SAFE_BUILDS = {
'14.1' => [47, 46],
'13.1' => [59, 19],
'13.1FIPS' => [37, 235],
'13.1NDCPP' => [37, 236],
'12.1' => [55, 328]
}.freeze
def run_host(ip)
# Check SNMP if enabled
if datastore['USE_SNMP']
check_snmp(ip)
end
# Check SSH if enabled and credentials provided
if datastore['USE_SSH'] && datastore['SSH_USER'] && datastore['SSH_PASS']
check_ssh(ip)
end
end
def check_snmp(ip)
begin
connect_snmp
# SNMP OID for system description
oid = '1.3.6.1.2.1.1.1.0'
response = snmp.get_value(oid)
if response
vprint_status("#{ip} - SNMP Response: #{response}")
process_banner(ip, response, 'SNMP')
else
vprint_error("#{ip} - No SNMP response received")
end
rescue ::SNMP::RequestTimeout, ::SNMP::UnsupportedVersion => e
vprint_error("#{ip} - SNMP error: #{e.class} - #{e.message}")
rescue ::Exception => e
vprint_error("#{ip} - SNMP connection failed: #{e.class} - #{e.message}")
ensure
disconnect_snmp
end
end
def check_ssh(ip)
begin
# Create SSH connection
ssh_opts = {
port: datastore['SSH_PORT'],
auth_methods: ['password', 'keyboard-interactive'],
password: datastore['SSH_PASS'],
non_interactive: true,
config: false,
verbose: :error
}
::Timeout.timeout(10) do
ssh = Net::SSH.start(ip, datastore['SSH_USER'], ssh_opts)
# Execute version command
version_output = ssh.exec!('show version | head -n 10')
vprint_status("#{ip} - SSH Response: #{version_output}")
process_banner(ip, version_output, 'SSH')
ssh.close
end
rescue ::Timeout::Error
vprint_error("#{ip} - SSH connection timeout")
rescue Net::SSH::AuthenticationFailed
vprint_error("#{ip} - SSH authentication failed")
rescue ::Exception => e
vprint_error("#{ip} - SSH connection failed: #{e.class} - #{e.message}")
end
end
def process_banner(ip, banner, method)
# Parse NetScaler version from banner
version_info = parse_netscaler_version(banner)
return unless version_info
branch = version_info[:branch]
build_major = version_info[:build_major]
build_minor = version_info[:build_minor]
tag = version_info[:tag]
full_branch = branch + tag
build = [build_major, build_minor]
# Check if vulnerable
vulnerable = is_vulnerable?(full_branch, build)
# Report findings
if vulnerable
print_good("#{ip} - VULNERABLE ? - #{full_branch} #{build_major}.#{build_minor} via #{method}")
# Report vulnerability
report_vuln(
host: ip,
name: 'Citrix NetScaler CVE-2025-6543',
refs: references,
info: "Vulnerable NetScaler version detected: #{full_branch} #{build_major}.#{build_minor}"
)
# Perform exploit check if requested
if datastore['EXPLOIT_CHECK']
exploit_check(ip)
end
else
print_status("#{ip} - PATCHED - #{full_branch} #{build_major}.#{build_minor} via #{method}")
end
# Report service information
report_service(
host: ip,
port: method == 'SNMP' ? 161 : datastore['SSH_PORT'],
proto: 'tcp',
name: method.downcase,
info: "NetScaler #{full_branch} #{build_major}.#{build_minor}"
)
end
def parse_netscaler_version(banner)
# Regex to match NetScaler version patterns
patterns = [
/NetScaler.*?(?<branch>\d+\.\d+)[-_](?<major>\d+)\.(?<minor>\d+)(?:[-_](?<tag>FIPS|NDCPP))?/i,
/NS.*?(?<branch>\d+\.\d+)[-_](?<major>\d+)\.(?<minor>\d+)(?:[-_](?<tag>FIPS|NDCPP))?/i,
/Version:\s*(?<branch>\d+\.\d+).*?Build:\s*(?<major>\d+)\.(?<minor>\d+)/i
]
patterns.each do |pattern|
match = banner.match(pattern)
next unless match
return {
branch: match[:branch],
build_major: match[:major].to_i,
build_minor: match[:minor].to_i,
tag: match[:tag] || ''
}
end
vprint_error("Could not parse NetScaler version from: #{banner[0,100]}...")
nil
end
def is_vulnerable?(branch, build)
safe_build = SAFE_BUILDS[branch]
return true unless safe_build # Unknown branch, assume vulnerable
# Compare builds
build[0] < safe_build[0] || (build[0] == safe_build[0] && build[1] < safe_build[1])
end
def exploit_check(ip)
vprint_status("#{ip} - Performing exploit validation...")
# Basic connectivity checks for potential exploitation
checks = [
{ port: 80, service: 'HTTP Management' },
{ port: 443, service: 'HTTPS Management' },
{ port: 3010, service: 'MIP Management' },
{ port: 3008, service: 'Python Management' }
]
checks.each do |check|
if port_open?(ip, check[:port])
vprint_status("#{ip} - #{check[:service]} accessible on port #{check[:port]}")
end
end
# Report potential exploitation vector
report_note(
host: ip,
type: 'netscaler.cve_2025_6543.vulnerable',
data: 'Vulnerable NetScaler instance identified for CVE-2025-6543'
)
end
def port_open?(ip, port, timeout = 3)
begin
socket = Rex::Socket::Tcp.create(
'PeerHost' => ip,
'PeerPort' => port,
'Timeout' => timeout
)
socket.close
return true
rescue
return false
end
end
def run
# Load targets from file if specified
if datastore['RHOSTS_FILE'] && File.exist?(datastore['RHOSTS_FILE'])
hosts = File.readlines(datastore['RHOSTS_FILE']).map(&:strip).reject(&:empty?)
hosts.each { |host| run_host(host) }
else
super
end
end
end
Greetings to :=====================================================================================
jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)|
===================================================================================================