Like finding a master-key exploit for a residential deadbolt when you already have the homeowner's key ring
CVE-2026-93958 is an OS command injection in the D-Link R95 BE9500 Wi-Fi 7 Smart Router, firmware version BE9500_1.00.16. The DHMAPI SOAP interface at /DHMAPI/ accepts a SetTimeSettings action whose <NTPServer> XML field is passed — unsanitized — into a system() call inside /bin/ssi, which runs as root. An attacker who can log in to the router's admin panel can inject backtick-enclosed shell commands (e.g., ` id > /www/m_id.txt ) and achieve full root-level code execution on the device. The public PoC (HackSpeak/CVE-2026-93958`) automates the entire chain including HMAC-SHA256-authenticated login, payload injection, and output retrieval.
The vendor CVSS of 9.1 CRITICAL dramatically overstates the risk for enterprise environments. The vector PR:H confirms admin-level authentication is required — the attacker already needs the router's admin password. Several clickbait write-ups mischaracterize this as 'unauthenticated,' but the PoC itself performs a full login handshake first. The D-Link R95 is a consumer Wi-Fi 7 router sold at retail for home networking — it is not enterprise-grade network infrastructure. For a security team managing 10,000 hosts, this device likely appears nowhere in asset inventory, and if it does, it is shadow IT in a branch closet. The Scope: Changed rating reflects that a compromised router can impact downstream hosts, which is theoretically true but practically bounded to a single home or micro-office LAN segment. This is a MEDIUM for enterprise defenders.
4 steps from start to impact.
Reach the router's management interface
- Network path to the router's LAN IP or WAN management port if enabled
- Management interface is LAN-only by default
- Enterprise networks should not contain consumer D-Link Wi-Fi routers
- WAN management requires explicit user opt-in, which is rare
http.title:"D-Link R95" will surface any WAN-exposed units, but expect very low counts globally.Authenticate as router admin (PR:H)
Login action. The attacker sends Action=request signed with the static key withoutloginkey, extracts the Challenge/Cookie/PublicKey/SaltHash, derives a private key via PBKDF2, and completes login. This requires valid admin credentials. D-Link routers historically ship with admin / blank password, making default-credential attacks viable on unmanaged devices.- Valid admin username and password for the router
- Knowledge of the DHMAPI login protocol (documented in the public PoC)
- Users who changed the default password block this step entirely
- Enterprise-deployed devices (if any exist) would have changed credentials per policy
- The HMAC-SHA256 handshake is non-trivial without the PoC tooling
Inject OS command via NTPServer parameter
uid=<cookie>), the attacker sends a SOAP SetTimeSettings request with a crafted <NTPServer> value containing backtick-enclosed shell commands. The system() call in /bin/ssi evaluates the injected string inside double quotes, triggering command substitution. Commands execute as root. The field has a 63-byte limit, so longer payloads are base64-chunked across multiple requests.- Authenticated admin session from Step 2
- SOAP request crafted with correct API-AUTH HMAC headers
- 63-byte NTPServer field limit constrains payload size per request
- Cannot use
&character in payloads - Synchronous execution means complex payloads require multiple round-trips
SetTimeSettings with backtick characters in the body would catch this.Achieve persistent root shell on router
/www/e and retrieves it via HTTP GET, effectively creating an interactive webshell. From root on the router, the attacker can intercept all LAN traffic, poison DNS responses, pivot to internal hosts, or enroll the device in a botnet. Persistence is trivial via crontab or startup script modification.- Successful command injection from Step 3
- Blast radius is limited to the single LAN segment behind this consumer router
- No lateral movement to Active Directory, domain controllers, or enterprise infrastructure
- Router reboot or firmware reflash eliminates persistence
The supporting signals.
| In-the-wild exploitation | No confirmed active exploitation. Not listed in CISA KEV. No campaigns attributed. No GreyNoise tags observed. |
|---|---|
| Proof-of-concept | Public and functional. HackSpeak/CVE-2026-93958 provides r95_exp.py — a complete Python 3 exploit with interactive shell mode and single-command execution. Released as v1.0 on GitHub. |
| EPSS | 0.02175 (2.175%) — top ~18th percentile. Moderate probability of exploitation in the next 30 days, consistent with a PoC-available consumer device bug. |
| KEV status | Not listed. No CISA KEV entry as of 2026-09-23. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H — Network-accessible, low complexity, but admin credentials required (PR:H). Scope Changed inflates the score to 9.1 because router compromise can impact downstream hosts. |
| Affected versions | D-Link R95 firmware BE9500_1.00.16 confirmed. Reports also reference 1.01B06 series. |
| Fixed versions | No patch available. D-Link patch status listed as 'In Review' as of 2026-09-23. No fixed firmware version has been released. |
| Internet exposure | Minimal. The R95 is a consumer Wi-Fi 7 router; management interface is LAN-only by default. Shodan query http.title:"D-Link R95" expected to return very low results. No mass-exposure data available. |
| Disclosure date | 2026-09-20 — 3 days ago. Very fresh disclosure. |
| Researcher / reporter | HackSpeak (GitHub handle) — independent security researcher. No organizational affiliation identified. |
Why this verdict
- PR:H is a real gate, not a formality. The CVSS vector explicitly requires High privileges (admin credentials). The PoC demonstrates a full HMAC-SHA256 challenge-response login sequence. While D-Link default credentials (
admin/blank) weaken this in unmanaged home deployments, any enterprise-managed device should have changed credentials. This narrows the reachable attacker population significantly. - Consumer product with near-zero enterprise install base. The D-Link R95 is a retail Wi-Fi 7 home router priced at ~$300 on Amazon. It is not sold through enterprise channels, not supported by enterprise management platforms, and not designed for rack deployment. An enterprise managing 10,000 hosts is unlikely to have even one R95 in asset inventory. If present, it is shadow IT.
- LAN-only management surface by default. The DHMAPI interface is not WAN-exposed unless the user explicitly enables remote management, which is uncommon. The attacker must already be on the local network segment to reach the management portal, compounding the access requirement on top of PR:H.
- No active exploitation and no KEV listing. Despite a public PoC, there is no evidence of in-the-wild exploitation, no CISA KEV entry, and no attributed campaigns. EPSS at 2.175% is moderate, not alarming.
- Role multiplier: (a) *Low-value role (home network router):* ~95%+ of deployments. Compromise yields root on a home router — attacker can sniff residential traffic, poison DNS, join a botnet. No enterprise impact. (b) *Typical role (SOHO branch office):* ~5% of deployments. Compromise yields LAN pivot in a micro-office. No AD, no domain controllers, no fleet-scale blast radius. (c) *High-value role (enterprise network edge):* ~0% of deployments. The R95 is NOT an enterprise edge appliance (not FortiGate, not PAN-OS, not ASA). It does not appear in the high-value-role catalog. No floor applies.
- Scope Changed is theoretically correct but practically bounded. A compromised router can impact LAN hosts, but the R95 serves at most a handful of devices on a home or micro-office network. This is not fleet-scale or domain-scale blast radius.
Why not higher?
Upgrading to HIGH would require either confirmed active exploitation, significant enterprise install base, or a role in high-value infrastructure. None apply here. The D-Link R95 is a consumer Wi-Fi router absent from enterprise networks. Even the Scope Changed CVSS element only impacts a small residential LAN, not enterprise-scale assets. The PR:H requirement further limits exploitability to attackers who already possess admin credentials.
Why not lower?
Dropping to LOW would understate the risk for the small population of organizations that *do* have D-Link consumer routers in branch offices or home-worker setups. The PoC is public, functional, and gives root RCE. Default credentials on D-Link routers are well-known, making PR:H weaker than it appears on paper. A compromised router enables traffic interception and DNS poisoning for all hosts behind it, which is non-trivial even at small scale.
What to do — in priority order.
- Identify and remove D-Link R95 units from any managed network — This is a consumer router. It should not exist in enterprise infrastructure. Run a network scan for the R95's DHMAPI SOAP fingerprint or HTTP title. If found, replace with enterprise-grade equipment. No mitigation SLA applies at MEDIUM — go straight to the 365-day noisgate remediation SLA window, but given the trivial fix (swap the hardware), aim for 30 days.
- Change default admin credentials immediately on any discovered units — D-Link routers commonly ship with
admin/blank. The entire attack chain depends on valid admin credentials. Changing the password to a strong, unique value neutralizes the PoC entirely. Do this within hours if any units are found. - Disable WAN remote management — Ensure the 'Enable Remote Management' option in the R95 admin panel is turned off. This restricts the DHMAPI interface to LAN-only access, eliminating any internet-facing attack surface.
- Segment consumer devices on a dedicated VLAN — If removal is not immediately possible, place the R95 on an isolated VLAN with no route to corporate resources. This contains the blast radius of any compromise to the isolated segment.
- Monitor for D-Link firmware updates — D-Link lists the patch as 'In Review.' Subscribe to D-Link security advisories at https://www.dlink.com/en/security-bulletin and apply the firmware update when released.
- WAF/IPS rules on upstream firewall — the DHMAPI SOAP traffic is HTTPS-encrypted between the attacker (on LAN) and the router. An upstream firewall cannot inspect or block the injection payload without TLS interception, which is impractical for traffic to a LAN gateway.
- EDR on endpoints behind the router — EDR protects the hosts, not the router itself. The R95 runs embedded Linux with no agent support. EDR will not detect or prevent the command injection on the router.
- Network-level authentication (802.1X) — even with NAC, an attacker on the authenticated LAN segment can still reach the router management interface. 802.1X controls network admission, not router admin access.
Crowdsourced verification payload.
Run this script from any host that has HTTP(S) connectivity to the D-Link R95 management interface (typically a LAN host). Invoke with python3 check_cve_2026_93958.py <router_ip>. No special privileges required — it performs an unauthenticated HTTPS GET to fingerprint the firmware version.
#!/usr/bin/env python3
"""check_cve_2026_93958.py — Check D-Link R95 firmware version for CVE-2026-93958.
Usage: python3 check_cve_2026_93958.py <router_ip>
Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
"""
import sys
import ssl
import urllib.request
import re
VULNERABLE_VERSIONS = ["1.00.16", "1.01B06"]
def main():
if len(sys.argv) != 2:
print("Usage: python3 check_cve_2026_93958.py <router_ip>")
sys.exit(2)
host = sys.argv[1]
url = f"https://{host}/"
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
try:
req = urllib.request.Request(url, headers={"User-Agent": "noisgate-check/1.0"})
resp = urllib.request.urlopen(req, timeout=10, context=ctx)
body = resp.read().decode("utf-8", errors="ignore")
except Exception as e:
print(f"UNKNOWN — could not connect to {host}: {e}")
sys.exit(2)
# Check if this is a D-Link R95
if "R95" not in body and "BE9500" not in body and "D-Link" not in body:
print(f"UNKNOWN — {host} does not appear to be a D-Link R95.")
sys.exit(2)
# Try to extract firmware version from page content
version_match = re.search(r'(?:firmware|version|ver)[^0-9]*(\d+\.\d+[A-Za-z]*\.?\d*[A-Za-z]*\d*)', body, re.IGNORECASE)
if not version_match:
# Try alternate patterns
version_match = re.search(r'BE9500[_ ]*(\d+\.\d+\.\d+)', body, re.IGNORECASE)
if not version_match:
# Try DHMAPI info endpoint
try:
info_url = f"https://{host}/DHMAPI/"
info_req = urllib.request.Request(info_url, headers={"User-Agent": "noisgate-check/1.0"})
info_resp = urllib.request.urlopen(info_req, timeout=10, context=ctx)
info_body = info_resp.read().decode("utf-8", errors="ignore")
version_match = re.search(r'<FirmwareVersion>([^<]+)</FirmwareVersion>', info_body)
if not version_match:
version_match = re.search(r'<ModelName>[^<]*R95[^<]*</ModelName>', info_body)
if version_match:
fw_match = re.search(r'(\d+\.\d+\.\d+)', info_body)
if fw_match:
version_match = fw_match
except Exception:
pass
if version_match:
fw_version = version_match.group(1).strip()
print(f"Detected firmware version: {fw_version}")
for vuln_ver in VULNERABLE_VERSIONS:
if vuln_ver in fw_version:
print(f"VULNERABLE — firmware {fw_version} matches known vulnerable version {vuln_ver} (CVE-2026-93958).")
sys.exit(1)
print(f"PATCHED — firmware {fw_version} does not match known vulnerable versions.")
sys.exit(0)
else:
print(f"UNKNOWN — detected a D-Link device at {host} but could not determine firmware version. Verify manually via admin panel > Status.")
sys.exit(2)
if __name__ == "__main__":
main()What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.