Back to Intelligence

Chinese-Speaking Threat Actor Exploits Zyxel GS1900 Switches and WordPress to Steal Government Data — Detection and Hardening Guide

SA
Security Arsenal Team
September 23, 2026
13 min read

Security researchers have disclosed an active campaign attributed to a Chinese-speaking threat actor that is chaining exploitation of ZyXEL GS1900 Smart Managed Switches and vulnerable WordPress deployments to gain initial access, establish persistence, and exfiltrate sensitive government data. The scope is significant: 996 compromised devices and more than 18,500 records stolen from backend databases.

This campaign matters for three reasons that every SOC lead and CISO should internalize immediately:

  1. It targets two of the most neglected asset classes in any environment — network edge infrastructure (managed switches) and public-facing CMS platforms. These are the systems that fall out of patch cycles, sit outside EDR coverage, and quietly run outdated firmware for years.
  2. The actor is treating edge devices as beachheads, not targets. A compromised switch or WordPress host is a pivot point into internal networks, credential stores, and databases that would otherwise be unreachable.
  3. The data theft is already done at scale. Eighteen and a half thousand records from government backend databases means this actor has operationalized the full kill chain — access, persistence, discovery, collection, and exfiltration.

If you operate Zyxel GS1900-series switches anywhere in your environment, or you expose WordPress to the internet on behalf of any business unit, you need to assume you are in this actor's target set and act accordingly.

Technical Analysis

Affected Products and Attack Surface

The campaign focuses on two distinct but complementary attack surfaces:

ZyXEL GS1900 Smart Managed Switch series — Layer 2 managed switches commonly deployed in government, education, and SMB environments for VLAN segmentation and edge aggregation. These devices run embedded web management interfaces (typically reachable over HTTP/HTTPS on ports 80/443) with a history of authentication bypass, command injection, and improper input validation flaws. Critically, they are network appliances with no EDR visibility — once compromised, they are nearly invisible to traditional endpoint telemetry.

WordPress — the world's most deployed CMS. Exploitation typically arrives through one of three vectors:

  • Outdated core installations with known unauthenticated vulnerabilities
  • Vulnerable third-party plugins and themes (the dominant root cause of WordPress compromise)
  • Weak or reused administrative credentials against /wp-login.php or /xmlrpc.php

How the Attack Chain Works (Defender's View)

Based on the reported tradecraft, the operation follows a pattern consistent with other Chinese state-nexus campaigns against edge infrastructure:

  1. Initial Access — Edge Device Exploitation. The actor scans for internet-exposed Zyxel GS1900 management interfaces and exploits known vulnerabilities to gain administrative control of the switch. Compromised switches provide man-in-the-middle positioning on internal traffic, the ability to reconfigure VLANs, and a stealthy persistence layer that survives endpoint re-imaging.

  2. Initial Access — WordPress Exploitation. In parallel, the actor exploits vulnerable WordPress instances to drop web shells (typically PHP-based) into the web root or uploads directories. From a web shell, the actor executes operating-system commands under the web service account (www-data, apache, or the IIS application pool identity).

  3. Persistence and Credential Access. On the WordPress host, the actor harvests wp-config.php — which contains cleartext database credentials — and establishes persistence through rogue plugins, modified theme files, or scheduled tasks. On the switch, modified firmware or startup configuration changes survive reboots.

  4. Discovery and Collection. Using the database credentials from wp-config.php, the actor pivots directly to backend databases and executes bulk extraction queries. The reported 18,500+ records strongly suggests automated mysqldump-style collection or scripted SELECT ... INTO OUTFILE operations.

  5. Exfiltration. Staged data is exfiltrated over HTTPS to actor-controlled infrastructure, often routed through previously compromised third-party infrastructure to complicate attribution and blocking.

Exploitation Status

This is confirmed, active, in-the-wild exploitation at scale — not a theoretical or proof-of-concept scenario. Nearly a thousand devices have been compromised and government records have already been exfiltrated. If your Zyxel switches or WordPress sites are reachable from the internet and running outdated software, assume targeting is either underway or imminent. Check both vendors' security advisories and CISA's Known Exploited Vulnerabilities (KEV) catalog for the specific flaw identifiers affecting your firmware and plugin versions, and treat any KEV-listed entries as patch-now emergencies under Binding Operational Directive 22-01 timelines (or your internal equivalent SLA).

Detection & Response

The detections below target the behaviors this actor must perform regardless of which specific vulnerability they used for entry: web shell drops and execution on WordPress hosts, anomalous child processes from web server workers, and direct database extraction activity.

Sigma Rules

YAML
---
title: Web Server Process Spawning Shell or System Commands
tid: 3f8a2c14-9b7d-4e61-a5c2-8d4e6f0a1b23
status: experimental
description: Detects web server worker processes (Apache, Nginx, PHP-FPM, IIS w3wp) spawning shells or command interpreters, consistent with web shell execution following WordPress exploitation.
references:
  - https://www.bleepingcomputer.com/news/security/chinese-hackers-exploit-multiple-technologies-to-steal-govt-data/
  - https://attack.mitre.org/techniques/T1505/003/
author: Security Arsenal
date: 2026/02/14
tags:
  - attack.persistence
  - attack.execution
  - attack.t1505.003
  - attack.t1059
logsource:
  category: process_creation
  product: windows
detection:
  selection_parent:
    ParentImage|endswith:
      - '\w3wp.exe'
      - '\httpd.exe'
      - '\php-cgi.exe'
  selection_child:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
      - '\whoami.exe'
      - '\net.exe'
      - '\net1.exe'
      - '\certutil.exe'
      - '\bitsadmin.exe'
  condition: selection_parent and selection_child
falsepositives:
  - Rare legitimate CMS plugins performing system operations (backup plugins, update mechanisms)
  - Internal administration tools invoked via IIS app pools
level: high
---
title: PHP Web Shell Dropped in WordPress Directories
id: 7c1e4d92-3a58-4f06-b9e1-2d7a5c8f6043
status: experimental
description: Detects creation of PHP files in WordPress uploads, cache, or web-accessible directories, a hallmark of web shell deployment after plugin or core exploitation.
references:
  - https://www.bleepingcomputer.com/news/security/chinese-hackers-exploit-multiple-technologies-to-steal-govt-data/
  - https://attack.mitre.org/techniques/T1505/003/
author: Security Arsenal
date: 2026/02/14
tags:
  - attack.persistence
  - attack.t1505.003
logsource:
  category: file_event
  product: windows
detection:
  selection:
    TargetFilename|contains:
      - '\wp-content\uploads\'
      - '\wp-content\cache\'
      - '\wp-includes\'
      - '\inetpub\wwwroot\'
    TargetFilename|endswith:
      - '.php'
      - '.phtml'
      - '.php5'
      - '.phar'
  condition: selection
falsepositives:
  - Legitimate plugin/theme installations and updates writing PHP files (filter by Image — these should originate from wp-admin workflows, not from external tools or archive extraction)
level: high
---
title: Database Credential Access via wp-config.php
tid: a29f6b31-7d44-4c18-8e53-1f3b9a2d7c85
status: experimental
description: Detects command-line access to wp-config.php, which contains cleartext WordPress database credentials and is routinely harvested by actors post-exploitation for database pivoting.
references:
  - https://www.bleepingcomputer.com/news/security/chinese-hackers-exploit-multiple-technologies-to-steal-govt-data/
  - https://attack.mitre.org/techniques/T1552/001/
author: Security Arsenal
date: 2026/02/14
tags:
  - attack.credential_access
  - attack.t1552.001
logsource:
  category: process_creation
  product: linux
detection:
  selection:
    CommandLine|contains:
      - 'wp-config.php'
  filter_legit:
    CommandLine|contains:
      - 'wp-cli'
      - 'vim '
      - 'nano '
      - 'grep -r'
  condition: selection and not filter_legit
falsepositives:
  - System administrators legitimately editing or reviewing the configuration file
  - WP-CLI management operations
level: medium

KQL — Microsoft Sentinel / Defender

This query hunts for the full web-shell behavior chain across Windows and Linux hosts ingested into Sentinel: web server processes spawning command interpreters, followed by database tooling usage — the exact sequence this actor follows from WordPress compromise to bulk data extraction.

KQL — Microsoft Sentinel / Defender
let WebParents = dynamic(["w3wp.exe", "httpd.exe", "php-cgi.exe", "php-fpm", "nginx", "apache2", "httpd"]);
let SuspiciousChildren = dynamic(["cmd.exe", "powershell.exe", "pwsh.exe", "/bin/sh", "/bin/bash", "whoami", "id", "net.exe", "curl", "wget", "mysqldump", "mysql", "certutil.exe"]);
let Lookback = 7d;
union isfuzzy=true
    (DeviceProcessEvents
    | where TimeGenerated > ago(Lookback)
    | where InitiatingProcessFileName has_any (WebParents)
    | where FileName has_any (SuspiciousChildren) or ProcessCommandLine has_any ("wp-config.php", "mysqldump", "INTO OUTFILE")
    | project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine, AccountName, InitiatingProcessRemoteUrl, Source="MDE"),
    (Syslog
    | where TimeGenerated > ago(Lookback)
    | where Facility =~ "auth" or SyslogMessage has_any ("COMMAND=", "wp-config.php")
    | where SyslogMessage has_any ("mysqldump", "wp-config.php", "INTO OUTFILE", "bash -i", "nc -e", "curl", "wget")
    | project TimeGenerated, Computer, ProcessName, SyslogMessage, Source="Syslog"),
    (CommonSecurityLog
    | where TimeGenerated > ago(Lookback)
    | where DeviceVendor =~ "ZyXEL" or DeviceProduct has "GS1900"
    | where Message has_any ("login failed", "authentication failure", "config changed", "firmware") or (DestinationPort in (80, 443) and SourceIsPublic == true)
    | project TimeGenerated, DeviceProduct, SourceIP, DestinationIP, DestinationPort, Message, Source="ZyXEL-CEF")
| order by TimeGenerated desc

A second, focused hunt for inbound attacks against exposed Zyxel management interfaces and WordPress login endpoints at your perimeter:

KQL — Microsoft Sentinel / Defender
let Lookback = 14d;
CommonSecurityLog
| where TimeGenerated > ago(Lookback)
| where DestinationPort in (80, 443, 8080)
| where RequestURL has_any ("/wp-login.php", "/xmlrpc.php", "/wp-admin/", "/wp-content/uploads/") 
   or (DeviceVendor =~ "ZyXEL" and Message has_any ("login", "auth"))
| summarize Attempts = count(), DistinctSources = dcount(SourceIP), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceIP, RequestURL, DestinationIP
| where Attempts > 50 or DistinctSources > 10
| order by Attempts desc

Velociraptor VQL

Use this artifact to sweep WordPress hosts (Windows and Linux) for recently created PHP files in upload/cache directories — the highest-fidelity web shell indicator — plus web server processes with anomalous child processes.

VQL — Velociraptor
-- Hunt for web shells and web server process anomalies on WordPress hosts
SELECT * FROM foreach(
  row={
    SELECT Pid, Name, CommandLine, Exe, Username, CreateTime
    FROM pslist()
    WHERE Name =~ '(?i)(httpd|apache2|nginx|php-fpm|w3wp)'
  },
  query={
    SELECT Pid, Name, CommandLine, Exe, Username, CreateTime
    FROM pslist()
    WHERE PPID = _Pid
  })

-- Separately: enumerate PHP files modified in the last 14 days in web-accessible paths
SELECT FullPath, Size, Mtime, Ctime
FROM glob(globs=[
  'C:/inetpub/wwwroot/**/*.php',
  'C:/inetpub/wwwroot/**/wp-content/uploads/**',
  '/var/www/html/**/*.php',
  '/var/www/html/**/wp-content/uploads/**',
  '/srv/www/**/wp-content/uploads/**'
])
WHERE Mtime > now() - 1209600
ORDER BY Mtime DESC

Review any PHP file appearing in uploads/ with suspicion — legitimate WordPress deployments almost never place executable PHP there, and most hardening guides explicitly block PHP execution in that directory.

Remediation and Verification Script

The following Bash script audits a Linux-hosted WordPress server for indicators of this campaign: unexpected PHP in uploads, recently modified web-root files, suspicious crontab entries, and outbound connections from the web service account.

Bash / Shell
#!/bin/bash
# Security Arsenal — WordPress compromise triage for Zyxel/WordPress campaign
# Run as root on the suspected WordPress host. Read-only; makes no changes.

WEBROOT="${1:-/var/www/html}"
echo "=== [1] PHP files in uploads/cache (web shell indicator) ==="
find "$WEBROOT" -path "*/wp-content/uploads/*" -name "*.php*" -o \
     -path "*/wp-content/cache/*" -name "*.php*" 2>/dev/null

echo "=== [2] Web-root files modified in last 14 days ==="
find "$WEBROOT" -type f \( -name "*.php" -o -name "*.phtml" \) -mtime -14 -ls 2>/dev/null

echo "=== [3] Suspicious strings in recently modified PHP (base64/eval/assert) ==="
find "$WEBROOT" -type f -name "*.php" -mtime -30 -exec grep -lE \
  "(eval\(|assert\(|base64_decode|gzinflate|shell_exec|passthru|system\()" {} \; 2>/dev/null

echo "=== [4] Cron/systemd persistence for web service user ==="
crontab -l -u www-data 2>/dev/null; crontab -l -u apache 2>/dev/null
ls -la /etc/cron.d/ 2>/dev/null | grep -vE "^total|placeholder"

echo "=== [5] Outbound connections from web server processes ==="
ss -tunap 2>/dev/null | grep -E "apache2|httpd|php-fpm|nginx" | grep ESTAB

echo "=== [6] Database credential exposure check ==="
grep -E "DB_USER|DB_PASSWORD" "$WEBROOT/wp-config.php" 2>/dev/null | sed 's/=.*/= [REDACTED — rotate immediately]/'
stat -c '%a %U:%G %n' "$WEBROOT/wp-config.php" 2>/dev/null
echo "NOTE: wp-config.php must be 640/600 and owned by root:www-data or equivalent"

echo "=== Triage complete. Rotate DB credentials if sections 1-3 returned hits. ==="

For the Zyxel side, audit your switches from the network rather than trusting the device itself — a compromised switch cannot be trusted to report its own state:

Bash / Shell
#!/bin/bash
# Zyxel GS1900 exposure and configuration audit
# Run from a management host with SNMP/SSH access to the switch fleet.

SWITCHES="switch-inventory.txt"   # one IP per line

echo "=== [1] Firmware version inventory (compare against vendor advisory) ==="
while read -r SW; do
  echo "--- $SW ---"
  snmpwalk -v2c -c "$COMMUNITY" "$SW" SNMPv2-MIB::sysDescr.0 2>/dev/null
done < "$SWITCHES"

echo "=== [2] Detect internet-exposed management interfaces (run from external vantage) ==="
echo "Scan your public ranges for GS1900 fingerprints:"
echo "  nmap -sV -p 80,443 --script http-title <public-range> | grep -i zyxel"

echo "=== [3] Verify management plane restrictions ==="
echo "Confirm on each GS1900:"
echo "  - Web/SSH management bound to dedicated management VLAN only"
echo "  - HTTP (port 80) management DISABLED — HTTPS only"
echo "  - Default admin credentials rotated; per-device unique passwords"
echo "  - SNMPv1/v2c disabled in favor of SNMPv3 where supported"

echo "=== [4] Export running-config for offline diff against known-good baseline ==="
echo "  ssh admin@<switch> 'show running-config' > baseline-\$(date +%F).cfg"
echo "  Diff against your golden config — unexpected VLAN, ACL, or account changes = incident"

Remediation

Prioritize in this order — edge devices first, because they are both the stealthiest persistence layer and the least monitored:

1. Zyxel GS1900 fleet — patch and de-expose immediately.

  • Inventory every GS1900 in your environment (they hide in wiring closets, branch offices, and OT adjacencies) and record firmware versions.
  • Apply the latest firmware from Zyxel's official support portal and cross-reference the security advisories at https://www.zyxel.com/global/en/support/security-advisories for the specific vulnerabilities affecting your model and firmware train.
  • Remove the management interface from any internet-reachable path. There is no legitimate reason for a switch's web UI to be exposed to the internet. Bind management to a dedicated, ACL-restricted management VLAN reachable only from your jump hosts.
  • Disable HTTP management (HTTPS only), rotate all device credentials to unique per-device values, and disable SNMPv1/v2c where SNMPv3 is supported.
  • Export and diff running configurations against a known-good baseline. A compromised GS1900 may have rogue local accounts, modified VLANs, or changed syslog destinations — and may not survive a firmware flash cleanly, so plan for potential device replacement on confirmed compromise.

2. WordPress — update, harden, and hunt.

  • Update WordPress core, all plugins, and all themes to current versions. Delete — do not merely deactivate — unused plugins and themes; deactivated plugin code remains exploitable if reachable.
  • Check every installed plugin against CISA KEV and vendor advisories. Any plugin with a KEV-listed vulnerability must be updated or removed today, not next change window.
  • Block PHP execution in wp-content/uploads/ via web server configuration (php_flag engine off in an .htaccess/nginx location block) — this single control neutralizes the most common web shell drop location.
  • Rotate database credentials in wp-config.php and the database server itself if there is any suspicion of compromise. Treat exposed wp-config.php credentials as burned.
  • Deploy a WAF rule set (ModSecurity with OWASP CRS, or your CDN/WAF provider's managed WordPress ruleset) in front of all WordPress properties, and rate-limit /wp-login.php and /xmlrpc.php (disable XML-RPC entirely unless a business dependency requires it).

3. Database layer — assume the actor has the credentials.

  • Enforce network-level controls so database servers accept connections only from designated application hosts. The pivot from web shell to direct database extraction (how 18,500 records walked out) fails if the DB isn't reachable from arbitrary positions.
  • Enable database audit logging for bulk read patterns — large SELECT result sets, mysqldump/pg_dump execution, and INTO OUTFILE operations from the application account outside maintenance windows.
  • Review the last 90 days of database access logs for anomalous extraction volume if you operate any WordPress-adjacent databases.

4. Hunt retrospectively.

  • Run the detections above against at least 90 days of telemetry. Given the campaign's reported dwell scale, a clean patch today does not mean a clean environment — persistence may already be in place.
  • If you confirm compromise on a switch, treat every credential that transited that segment as exposed and rotate accordingly.

5. Strategic posture. This campaign is another data point in a trend we brief clients on constantly: state-nexus actors are industrializing exploitation of unmanaged edge devices and neglected web applications precisely because they sit outside EDR and patching discipline. Close the gap with asset inventory that includes network appliances, firmware patch SLAs equal to your OS SLAs, and syslog ingestion from every edge device into your SIEM — a switch that can't send you logs is a switch you can't defend.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.