A malicious neighbor rewires the switch's management brain through a malformed web request
The CGI program in Zyxel GS1900-24E firmware through 2.90(AAHK.2)C0 contains an out-of-bounds write (CWE-787). An unauthenticated attacker who is adjacent to the switch (same L2 broadcast domain) can send a crafted HTTP request that overwrites memory and potentially executes OS commands. CIRCL data suggests other GS1900 models, including GS1900-HPv2, GS1900-48, and GS1900-48HPv2, may share the affected firmware line.
The vendor's HIGH (8.8) rating is broadly accurate for impact, but the AV:A vector already embeds the key limitation: this is not an internet-facing remote exploit. With no KEV listing and an EPSS probability of 0.293%, real-world urgency is lower than a pure CVSS reading suggests. However, full OS command execution on a network switch is a high-value outcome, so the rating stays HIGH rather than MEDIUM.
5 steps from start to impact.
Obtain network adjacency to the switch's management interface
- Same broadcast domain as the switch management interface
- No 802.1X/NAC preventing unauthorized devices
- Management VLAN isolation
- NAC/802.1X or port security
- Physical access controls
Discover the switch's web management endpoint
- Web management enabled (default on many SMB switches)
- HTTP/HTTPS port reachable from adjacent segment
- Web UI disabled by admin
- Management traffic on a separate ACL-restricted VLAN
- Non-default port
Send crafted HTTP request to the vulnerable CGI program
- Firmware version ≤ 2.90(AAHK.2)C0
- Exposed CGI endpoint
- Exploit reliability is uncertain on embedded MIPS/ARM with limited mitigations
- Crash-only outcomes may be more likely than code execution
Achieve OS command execution on the switch
- Successful exploitation of the OOB write
- No exploit mitigations that block control flow
- No widely available public PoC (VulDB reports no exploit exists; SOCRadar notes a 'public exploit' but no observed use)
- CPU architecture variability across GS1900 models
Lateral movement and traffic interception via the compromised switch
- Switch forwards production or management traffic
- No port mirroring anomaly alerts
- Encrypted traffic limits direct credential capture
- Proper segmentation and NGFW east-west controls can contain the blast radius
- NetFlow/IDS may detect unusual mirroring or VLAN changes
- Perimeter firewalling alone won't help because the attack originates from an adjacent LAN segment, not the internet.
- Endpoint EDR/AV won't detect the switch compromise because the code runs on the network device itself, not a monitored host.
- HTTPS alone does not stop the OOB write; the vulnerability is in the CGI program's input handling, not the transport layer.
The supporting signals.
| In-the-wild status | No known active exploitation; not KEV-listed. SOCRadar notes 'no use observed'. |
|---|---|
| Proof-of-concept availability | Conflicting: VulDB says *No exploit exists*; SOCRadar mentions *public exploit code published, no use observed*. Treat as low-confidence PoC. |
| EPSS | 0.00293 (0.293% probability) — very low likelihood of exploitation in next 30 days. |
| KEV status | Not listed in CISA KEV as of assessment date. |
| CVSS vector | CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — Adjacent, low complexity, no privileges, no user interaction, unchanged scope, high CIA impact. |
| Affected versions | GS1900-24E firmware ≤ 2.90(AAHK.2)C0; CIRCL lists related models GS1900-24EP, GS1900-HPv2, GS1900-48, GS1900-48HPv2 as potentially affected. |
| Fixed versions | Vendor advisory indicates upgrade to a fixed release, but specific fixed version is not shown in available sources. |
| Scanning/exposure | No GreyNoise/Shodan/Censys data in sources; these switches are typically internal-only, reducing internet attack surface. |
| Disclosure date | 2026-11-03 (MITRE/NVD record). |
| Reporting researcher | Not publicly attributed in the sources reviewed. |
Sources.
Why this verdict
- Adjacent-only access (AV:A): The attacker must already be on the same L2 segment or have compromised an adjacent host to reach the management interface. This eliminates pure internet-facing exploitation and pushes the likelihood down.
- No KEV or observed exploitation: Despite public exploit chatter, CISA KEV does not list it and EPSS is 0.293%, lowering real-world urgency.
- Role multiplier: GS1900 switches are network infrastructure. In any deployment where they forward production traffic, gaining OS-level control is a high-value outcome — traffic sniffing, VLAN hopping, and lateral movement — so the floor stays HIGH.
- Vendor CVSS is structurally sound: The 8.8 HIGH score already uses AV:A, so the base severity is appropriate; we keep the same bucket but apply downward pressure from the low EPSS.
Why not higher?
Not CRITICAL because the attack requires network adjacency (AV:A), there is no confirmed active exploitation or reliable public PoC, and the GS1900-24E is a small-business access switch rather than a perimeter security appliance or core datacenter chassis. The EPSS of 0.293% and absence from KEV further argue against a critical-rush posture.
Why not lower?
Not MEDIUM because an unauthenticated attacker on the same segment can gain full OS command execution on a network switch. That level of control can be leveraged for widespread traffic interception, DHCP/DNS poisoning, and pivoting to other VLANs. Even with low likelihood, the impact is severe enough to demand the HIGH patching window.
Crowdsourced verification payload.
Run this script from an auditor workstation with L2 adjacency to the switch management IP. It attempts to retrieve the firmware version from the web UI over HTTP and prints whether the device is VULNERABLE, PATCHED, or UNKNOWN. Invoke with python3 check_zyxel_gs1900.py <switch_ip>. No special privileges are needed beyond network access to the switch's management VLAN.
#!/usr/bin/env python3
"""Check Zyxel GS1900-24E firmware for CVE-2026-107441.
Outputs VULNERABLE, PATCHED, or UNKNOWN.
"""
import sys
import re
import urllib.request
import urllib.error
def get_body(url):
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
try:
with urllib.request.urlopen(req, timeout=5) as resp:
return resp.read(30000).decode('utf-8', errors='ignore')
except Exception:
return None
def extract_version(body):
if not body:
return None
# Zyxel GS1900 web UI often displays firmware in the footer or about page
patterns = [
r'(?:Firmware Version|FW Version|Software Version)[^\d]{0,10}(\d+\.\d+\([^\)]+\))',
r'(\d+\.\d+\([A-Z]+\.\d+\)C\d+)'
]
for pat in patterns:
m = re.search(pat, body, re.IGNORECASE)
if m:
return m.group(1)
return None
def is_vulnerable(version):
# Compare against vulnerable threshold 2.90(AAHK.2)C0.
# We do a conservative parser: if the major/minor is lower, vulnerable;
# if equal and build code is lexicographically <= 'AAHK.2', vulnerable.
m = re.match(r'(\d+)\.(\d+)\(([^\)]+)\)C(\d+)', version)
if not m:
return False
major, minor, build, cnum = int(m.group(1)), int(m.group(2)), m.group(3), int(m.group(4))
if major < 2:
return True
if major == 2 and minor < 90:
return True
if major == 2 and minor == 90:
# build code comparison: AAHK.2 vs AAHK.2; if lower => vulnerable
if build < 'AAHK.2':
return True
if build == 'AAHK.2' and cnum <= 0:
return True
return False
def main():
if len(sys.argv) != 2:
print('Usage: python3 check_zyxel_gs1900.py <switch_ip>')
sys.exit(2)
ip = sys.argv[1]
urls = [
f'http://{ip}/',
f'http://{ip}/index.htm',
f'http://{ip}/cgi-bin/about.cgi',
f'http://{ip}/web/version'
]
found_version = None
for url in urls:
body = get_body(url)
if body:
ver = extract_version(body)
if ver:
found_version = ver
break
if not found_version:
print('UNKNOWN: could not detect firmware version from web UI')
sys.exit(3)
print(f'Detected firmware: {found_version}')
if is_vulnerable(found_version):
print('VULNERABLE')
sys.exit(0)
else:
# If version is above threshold, report PATCHED (manual validation still recommended)
print('PATCHED')
sys.exit(0)
if __name__ == '__main__':
main()