Neutron, our AI engine, scored 97.1% on UC Berkeley's CyberGym benchmark. Learn more

Deep Dive

Deep Dive

Zyxel GS1900 Pre-Auth RCE Exploited in the Wild (CVE-2026-107441)

Zyxel GS1900 switches before 2.90 patch 3 take an unauthenticated HTTP request that runs root commands, and attackers already used the flaw as a zero-day.

TL;DR

  • An out-of-bounds write in the GS1900 switch's dispatcher.cgi lets an unauthenticated attacker run operating-system commands as root. Binarly found the exploit in a threat actor's toolkit and tested it on real hardware.
  • Ten GS1900 models running firmware 2.90(xxx.2)C0 or earlier are affected. Zyxel published fixed 2.90(xxx.3)C0 builds on October 22, 2026, and its advisory on November 3, 2026.
  • Zyxel rates it 8.8 High and Binarly 9.8 Critical on CVSS v3.1. GreyNoise saw 996 switches exploited. CVE-2026-107441 is not in CISA KEV as of November 4, 2026; CVE-2026-7273 is, since September 21, 2026.
  • Update every listed model to its 2.90(xxx.3)C0 build now. No configuration workaround closes the flaw; see "How do I fix it?" for exposure reduction while a change window is arranged.

Am I affected?

All of these conditions must hold for a switch to be exposed:

  • The device is one of the ten GS1900 models Zyxel lists, running firmware 2.90(xxx.2)C0 or earlier.
  • The switch's web management interface is reachable from a network the attacker can use. Zyxel describes the attacker as LAN-based and adjacent. Binarly says the exploit needs only HTTP reachability to the management service, with no protocol-level adjacency.
  • The 2.90(xxx.3)C0 firmware for that model is not installed. The June 2026 2.90(xxx.2)C0 update does not fix this flaw.

Zyxel says on-market products that do not appear in its advisory are unaffected. The advisory lists every model and version pair.

Model Affected versions Fixed version
GS1900-8 2.90(AAHH.2)C0 and earlier 2.90(AAHH.3)C0
GS1900-8HP 2.90(AAHI.2)C0 and earlier 2.90(AAHI.3)C0
GS1900-10HP 2.90(AAZI.2)C0 and earlier 2.90(AAZI.3)C0
GS1900-16 2.90(AAHJ.2)C0 and earlier 2.90(AAHJ.3)C0
GS1900-24 2.90(AAHL.2)C0 and earlier 2.90(AAHL.3)C0
GS1900-24E 2.90(AAHK.2)C0 and earlier 2.90(AAHK.3)C0
GS1900-24EP 2.90(ABTO.2)C0 and earlier 2.90(ABTO.3)C0
GS1900-24HPv2 2.90(ABTP.2)C0 and earlier 2.90(ABTP.3)C0
GS1900-48 2.90(AAHN.2)C0 and earlier 2.90(AAHN.3)C0
GS1900-48HPv2 2.90(ABTQ.2)C0 and earlier 2.90(ABTQ.3)C0

Check what the switch runs:

  1. Log in to the switch's web configurator. The Getting Started screen lists the Model Name and the Firmware Version, as a string such as 2.90(AAHL.2)C0.
  2. Compare the model and version with the advisory list. For the models listed, every version before .3)C0 is affected, including .2)C0, .1)C0 and .0)C0. Only a version ending in .3)C0 carries the November fix.
  3. There is no package manager on the switch. Record the version in your asset inventory, because the same model number can run different firmware builds.

How do I fix it?

  1. Download the 2.90(xxx.3)C0 firmware for the exact model from Zyxel's download library. The model-specific suffix matters: a GS1900-8 uses AAHH, a GS1900-24 uses AAHL, and so on. The November 3 advisory links the model table and the release announcement.
  2. In the web configurator, open Maintenance > Firmware Upgrade, choose HTTP, select the .bix file for the model, and apply it. Keep the configuration backup you take before the upgrade.
  3. After the reboot, return to Getting Started and confirm that the Firmware Version shows the .3)C0 build.
  4. Patch every listed model, not only the one that faces the internet. The exploit needs reachability to the management interface, and a flat management VLAN gives an attacker that reachability after any foothold.

No vendor workaround closes the bug. If a change window is not available, reduce exposure while you wait:

  • Move the management interface to a dedicated management VLAN.
  • Restrict access to it with firewall rules that allow only known administrator hosts.
  • Disable WAN and remote management where the device offers those options.
  • Monitor the switch for configuration changes and unexpected reboots.

Those steps only reduce who can reach the vulnerable service. They do not fix the missing bounds checks, and Zyxel published no configuration that makes the parser safe. A switch that remains reachable is still exploitable.

What is the root cause?

dispatcher.cgi is the CGI program behind the switch's web configuration pages. It runs under the Boa web server and parses an HTTP POST body into a global buffer named reqParameters. The buffer is 0x23000 bytes, divided into 140 slots of 1024 bytes each. A second global, count, records how many parameters have been parsed.

Binarly decompiled firmware version 2.90(AAHH.1)C0 and published the vulnerable loop. The copy has no length check and the counter has no upper bound:

for ( pos = strchr(src, '&'); pos != nullptr; pos = strchr(pos + 1, '&') )
{
  dest = &reqParameters[1024 * count];
  memcpy(dest, src, pos - src);
  dest[pos - src] = 0;
  v10 = strchr(dest, '=');
  if ( v10 != nullptr )
  {
    if ( v10[1] == '&' )
      dest[512] = 0;
    else
      urlDecode(dest + 512, v10 + 1);
    *v10 = 0;
  }
  src = pos + 1;
  ++count;
}

src is the request body and pos marks the next &. For each field, the loop computes &reqParameters[1024 * count], copies everything up to the separator, and increments count. A field longer than 1024 bytes overruns its slot. There is no check that count stays under 140.

The two globals sit next to each other in the .bss section. Binarly's IDA listing shows the layout:

.bss:10003BC0 reqParameters:  .space 0x23000
.bss:10026BC0 count:          .space 4

The 140th field is the trigger. When count is 139, the copy starts at the last slot, reqParameters[1024 * 139], and a 1028-byte field writes four bytes past the end of the buffer. Those four bytes are count. An attacker who supplies a 32-bit value in that position replaces the parameter count with an arbitrary number.

After the loop, a second block handles the final field, which has no trailing &. It reuses the corrupted count:

if ( -src < strlen(src) )
{
  dest = &reqParameters[1024 * count];
  len = strlen(src);
  memcpy(dest, src, len);
  dest[strlen(src)] = 0;
  ...
  ++count;
}

count is multiplied by 1024 in a signed 32-bit expression. A value such as 0x013bffff wraps the product into a negative offset, so dest points before reqParameters instead of inside it. The write lands in the process's global offset table, the table of resolved function addresses. The exploit picks the entry for strcmp.

The program then calls web_util_paramVal_ret, which walks the parameter array with strcmp(reqParameters, param). The first parameter, which the attacker controls, is the command to run. With the strcmp entry replaced by the address of system, that comparison becomes system("id") and runs with the privileges of the CGI process, which Binarly validated as root.

A second exploit mode corrupts saved registers and the return address instead of the GOT. It returns through a gadget in the device's uClibc to call system. Both modes corrupt the same counter through the same unchecked copy. The captured exploit carries hard-coded addresses for firmware versions from 2.10 to 2.90 and supports the GS1900-24 first.

How is it exploited?

  1. The attacker sends an HTTP POST to /cgi-bin/dispatcher.cgi. The captured request body has a first field holding the command, 139 & separators, a 1024-byte field followed immediately by a 32-bit counter value, a separator, and the address the attacker wants in the GOT.
  2. web_util_initParaPairs copies each field into reqParameters without a length check. The 140th field overruns the last slot and overwrites count with the 32-bit value.
  3. The out-of-loop block computes &reqParameters[1024 * count] with the corrupted count. The signed multiplication overflows, and the destination points before the global buffer, at the GOT entry the attacker chose.
  4. The same block copies the final field to that address, replacing the strcmp entry with the address of system.
  5. web_util_paramVal_ret calls strcmp on the first parameter. The call now reaches system, and the first parameter runs as root.

The request needs no session, no credentials, and no user interaction. Binarly tested the exploit against real hardware and against emulated firmware, and it published the request layout and the two modes. The captured sample is a Python script obfuscated with PyArmor; GreyNoise deobfuscated the same script and found the same parameter-counter corruption.

How does the attack flow?

A single unauthenticated HTTP request corrupts the parameter counter, redirects a function pointer in the GOT, and turns the next comparison into a root command.

flowchart LR A["Unauthenticated POST to dispatcher.cgi"] --> B["web_util_initParaPairs copies 140 fields with no length check"] B --> C["140th field overwrites count"] C --> D["1024 * count wraps to a negative offset"] D --> E["Copy lands in the GOT: strcmp becomes system"] E --> F["web_util_paramVal_ret calls strcmp with the first field"] F --> G["Root command execution on the switch"]

What can an attacker do?

An attacker who reaches the web interface can run operating-system commands as root on the switch. Binarly demonstrated command execution with system("id") in the CGI process and validated the worker context as root. Zyxel's advisory says the flaw can execute OS commands through a crafted HTTP request.

The impact includes reading and changing the switch configuration. GreyNoise observed an actor taking configuration data, hashed root credentials, and network information from 996 switches in 48 countries. Those records give an attacker the management topology and a password hash to attack offline.

Zyxel's CVE record gives CVSS v3.1 8.8, CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, because it treats the attack as adjacent-network. Binarly assesses 9.8 Critical, CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, because the management service is HTTP and any route to it is enough.

The conditions are the same as in "Am I affected?": network reachability to the management interface, no authentication, and no user interaction. Zyxel describes the attacker as LAN-based and adjacent. Binarly assesses the attack vector as network because the service is HTTP and a router or NAT rule can expose it beyond the local segment.

This post keeps two exploitation claims apart. GreyNoise attributed the August activity to CVE-2026-7273, the flaw Zyxel patched in June. Binarly found that the captured exploit targets the parser bug later assigned CVE-2026-107441.

Binarly also found that all ten June-fixed releases remained vulnerable. The actor's access and the data theft are established by GreyNoise. The exact CVE label the actor intended is less important than the fact that the June patch did not close the path.

Why does the fix work?

The November patch checks the counter before it computes the destination, and it replaces the unbounded copy with a bounded helper. Binarly published the new loop:

for ( pos = strchr(queries, '&'); pos != nullptr; pos = strchr(pos + 1, '&') )
{
  dest = &reqParameters[1024 * counter];
  if ( counter >= 140 ) // the global counter is checked before the copy
    return 0;
  web_util_paramCopy(&reqParameters[1024 * counter], 512, src, pos - src);
  ...
  ++counter;
}

The counter >= 140 test runs before the copy, so the 140th field can no longer reach the slot that holds count. web_util_paramCopy limits each field to 511 bytes, so a field cannot overrun its 1024-byte slot either. The two changes close both halves of the bug: the counter corruption and the unbounded field copy.

The June patch did not do that. It added these checks:

if ( ++counter == 140 )
  return 0;
...
if ( counter >= 141 )
  counter = 140;

The first check runs after the copy that corrupts counter. Once the value is overwritten, the equality test fails and the out-of-loop block still executes. The second check only resets a counter that has already been used to compute a destination. Binarly tested all ten June-fixed releases on September 28, 2026 and found every one still exploitable.

Binarly's advisory also recommends enabling Full RELRO so the GOT is read-only. Zyxel's advisory does not say whether the new builds enable it. The patch closes the demonstrated write primitive. It does not make the GOT immutable, so treat Full RELRO as defense in depth rather than a substitute for the firmware update.

How do I check if I was compromised?

The switch may not log the POST body that triggers the bug. Start with the malware and the command-and-control infrastructure, then review what the switch held.

Binarly published hashes for the JITTERLY backdoor and the SIXZUT rootkit it drops. The SHA-256 values in its advisory and blog are:

  • JITTERLY: 0f6e757e82c4d91df5bd249f775b9970b59dee42cc0dfe40f879d77fc16821c6, 663a8088faca8e126a11648f5671fcf4eaf96435e10daf63a1d1b17d8385154c, 82d072ab03d90c0131b06a576eadf2c986a51f0de986d50db4e49be01bf125cb.
  • SIXZUT: 295133156b6a7d95b9c92f832f49e35235f1fbd3906e75c9202ef4cd65d1018c, 64677002011bc2e4bc642a4154a20fdb95620ebd0144b98fc60f917901ec8b92, 728810b07e911b235f6450e784391e39f68819a0a5d209662162916ffea25146.

The actor used the 981666[.]xyz domain with these hosts:

  • p1.981666[.]xyz, p2.981666[.]xyz, p3.981666[.]xyz.
  • s0.981666[.]xyz through s9.981666[.]xyz.

Binarly's analysis lists these IP addresses: 104.223.65[.]148, 203.88.123[.]148, 104.225.153[.]141, and 166.88.227[.]181. The malware used TCP 443, 6443, 8080, 8082, 5432, and 6379. Binarly published YARA rules for JITTERLY and SIXZUT in its blog.

If a switch was reachable and unpatched, assume the configuration and the root password hash were taken. Rotate the local administrator password, the SNMP community strings, and any RADIUS, TACACS+, or VPN secrets stored in the configuration.

Review the switch logs for configuration changes, new local users, and unexpected reboots. A failed attempt may leave no trace on the device, so absence of a log entry is not proof that the switch was untouched.

Timeline

The first patch shipped 140 days before the advisory that replaced it, and Binarly found the ten fixed releases still vulnerable 104 days after that first patch.

  • 2026-06-16: Zyxel publishes the CVE-2026-7273 advisory and the 2.90(xxx.2)C0 fixed releases.
  • 2026-08-17: On or about this date, GreyNoise observes an actor taking data from 996 GS1900 switches in 48 countries.
  • 2026-09-21: CISA adds CVE-2026-7273 to the Known Exploited Vulnerabilities catalog.
  • 2026-09-28: Binarly confirms that all ten releases advertised as CVE-2026-7273 fixes remain vulnerable.
  • 2026-09-29: Binarly notifies Zyxel's product security team.
  • 2026-10-11: Zyxel confirms the new vulnerability and reserves CVE-2026-107441.
  • 2026-10-22: Zyxel releases the 2.90(xxx.3)C0 firmware for the ten models.
  • 2026-11-03: Zyxel publishes its advisory, Binarly publishes BRLY-2026-057, and the CVE record is published.

The first CVE-2026-7273 patch shipped 140 days before the CVE-2026-107441 advisory, and Binarly found all ten fixed releases still vulnerable 104 days after the first patch.
Figure 1: From the first patch to the second advisory in 140 days. Source: Binarly BRLY-2026-057 and Zyxel advisories, checked November 4, 2026.

FAQ

Does the CVE-2026-7273 update protect a GS1900 switch?

No. The June 16, 2026 2.90(xxx.2)C0 update added checks after the vulnerable copy, and Binarly found every one of the ten fixed releases still exploitable on September 28, 2026. A switch needs the 2.90(xxx.3)C0 build.

Is CVE-2026-107441 in CISA KEV?

No, as of November 4, 2026, the new CVE is not in the Known Exploited Vulnerabilities catalog. The older CVE-2026-7273 has been in the catalog since September 21, 2026, with a federal due date of September 24, 2026.

Which GS1900 models are affected?

The ten models Zyxel lists are affected at 2.90(xxx.2)C0 and earlier. Zyxel says on-market products not listed in its advisory are unaffected, so check the exact model and firmware pair rather than the product family name.

Did the in-the-wild attacks use CVE-2026-107441?

GreyNoise tied the August activity to CVE-2026-7273. Binarly found that the captured exploit targets the parameter-counter bug later assigned CVE-2026-107441, and that the June patch did not block it. Both labels describe the same parser, and the November firmware is the first release that closes the path Binarly demonstrated.

Can I block the attack by restricting the management interface?

Restricting the interface reduces who can reach the vulnerable service, but it does not fix the missing bounds checks. Zyxel published no workaround that makes the parser safe. A switch that remains reachable on any interface can still be exploited, so schedule the firmware update.

Is the fix complete?

The November patch checks the counter before the copy and bounds each field to 511 bytes, which closes the two write primitives Binarly demonstrated. Binarly also recommends Full RELRO so the GOT cannot be overwritten. Zyxel's advisory does not state whether the new builds enable it, so apply the firmware and treat RELRO as an additional control.

Find exposed Zyxel GS1900 switches with Ostorlab

Ostorlab's attack surface discovery inventories internet-facing management interfaces, including network devices, so you know which switches to patch first. Use it to find the GS1900 management pages your organisation exposes before an attacker does.

References

  • Zyxel, "Zyxel security advisory for out-of-bounds write vulnerability in GS1900 series switches" (November 3, 2026). https://www.zyxel.com/global/en/support/security-advisories/zyxel-security-advisory-for-out-of-bounds-write-vulnerability-in-gs1900-series-switches-11-03-2026 (checked November 4, 2026)
  • Zyxel, "Zyxel security advisory for stack-based buffer overflow vulnerability in GS1900 series switches" (June 16, 2026). https://www.zyxel.com/global/en/support/security-advisories/zyxel-security-advisory-for-stack-based-buffer-overflow-vulnerability-in-gs1900-series-switches-06-16-2026 (checked November 4, 2026)
  • Binarly, "Pre-Auth RCE in Zyxel GS1900 Exploited in the Wild as a Zero-Day" (November 3, 2026). https://www.binarly.io/blog/zyxel-gs1900-rce-cve-2026-107441 (checked November 4, 2026)
  • Binarly, "BRLY-2026-057: Out-of-bounds write vulnerability in Zyxel GS1900 switches" (November 3, 2026). https://www.binarly.io/advisories/brly-2026-057 (checked November 4, 2026)
  • GreyNoise, "Open Season on Kapibala: Attacker Steals Over 18,000 Government Records Through WordPress Exploitation" (September 28, 2026), section on the ZyXEL GS1900 campaign: 996 switches in 48 countries, attributed to CVE-2026-7273. https://www.greynoise.io/blog/open-season-on-kapibala-attacker-steals-government-records-wordpress-exploitation (checked November 4, 2026)
  • CVE Program, "CVE-2026-107441" (published November 3, 2026). https://www.cve.org/CVERecord?id=CVE-2026-107441 (checked November 4, 2026)
  • CVE Program, "CVE-2026-7273" (published June 16, 2026). https://www.cve.org/CVERecord?id=CVE-2026-7273 (checked November 4, 2026)
  • Zyxel, "GS1900 Series User's Guide, V2.90 edition 2" (pages 61 and 242). https://download.zyxel.com/GS1900-8/user_guide/GS1900-8_V2.90_Ed2.pdf (checked November 4, 2026)
  • CISA, "Known Exploited Vulnerabilities Catalog" (feed checked November 4, 2026). https://www.cisa.gov/known-exploited-vulnerabilities-catalog (checked November 4, 2026)