CVE-2026-91954
Executive Summary
CVE-2026-91954 is a null pointer dereference (CWE-476) in the FreeRDP client's gdi_surface_bits function, triggered when processing a crafted RDP Surface Bits command that declares the NSCodec codec ID. A malicious or compromised RDP server can crash any connecting FreeRDP client — including when NSCodec support is disabled — resulting in a client-side denial of service. The vulnerability carries a CVSS v3.1 base score of 6.5 and affects FreeRDP versions prior to 3.31.0.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
FreeRDP is an open-source implementation of the Remote Desktop Protocol (RDP), used by client applications on Linux, macOS, and Windows, as well as in embedded and server-side tooling. The vulnerability resides in the GDI subsystem's handling of Surface Bits commands — an RDP update type used to transmit raw bitmap data to the client's graphics pipeline.
According to the NVD description, the null pointer dereference occurs in the function gdi_surface_bits when the incoming Surface Bits command specifies the NSCodec codec ID. NSCodec is a Microsoft RemoteFX-era codec used for efficient bitmap compression. The critical detail is that the crash occurs even when the codec is disabled on the client — meaning the vulnerable code path does not validate codec availability before dereferencing a codec-context structure that has not been initialized.
The vulnerability class is a classic uninitialized/missing-context dereference: a codec dispatch path assumes a decoder context (or function table) exists for the declared codec ID, but because NSCodec was administratively disabled, that context is NULL. The subsequent dereference crashes the process. Because the trigger is a server-supplied command in the RDP data stream, the attack is network-borne and requires only that a victim client connect to a hostile or man-in-the-middle-controlled RDP endpoint.
Attack surface: The RDP client's inbound graphics update parser. Any deployment where FreeRDP initiates outbound RDP connections to endpoints the user does not fully control (public jump hosts, BYOD remoting, automation that connects to third-party hosts).
Root cause characteristics (per CWE-476): missing NULL check on the codec context prior to use; codec enablement state not consulted during the dispatch decision for Surface Bits payloads.
No further code-level structure (struct names, parameter names, allocation routines) is disclosed in the NVD description; those details should not be assumed.
Impact Assessment
CVSS v3.1: 6.5 (Medium)
| Subscore | Value | Interpretation |
|---|---|---|
| Attack Vector (AV:N) | Network | Triggered over the RDP connection |
| Attack Complexity (AC:L) | Low | No race, no special conditions |
| Privileges Required (PR:N) | None | Requires no authentication to the client |
| User Interaction (UI:R) | Required | Victim must connect to the malicious server |
| Scope (S:U) | Unchanged | Impact confined to the client process |
| Confidentiality (C:N) | None | No data disclosure |
| Integrity (I:N) | None | No data modification |
| Availability (A:H) | High | Full process crash / client termination |
Business impact: Availability-only. A successful exploit terminates the FreeRDP client session, dropping the user's remote desktop and any unsaved work in the client context. In automated/headless deployments (kiosks, thin clients, CI runners, monitoring gateways that bridge to RDP hosts), a malicious server can repeatedly kill client processes, causing persistent service disruption.
Blast radius: Bounded to individual client processes. No persistence, no code execution, no lateral movement is implied by the CVSS vector or vulnerability class. The scope is Unchanged — the crash does not escape the client trust boundary. Impact scales with the number of FreeRDP clients that connect to attacker-influenced RDP endpoints.
EPSS: 0.00346 (~28th percentile) — low probability of exploitation in the wild, consistent with the absence of public PoC and the availability-only, client-crash nature of the bug. CISA KEV: No. Not currently associated with known adversary campaigns.
Exploitation Scenarios
No public PoC is available. The following chain is inferred strictly from the vulnerability class (null pointer dereference in a codec dispatch path) and the specific trigger described in the NVD entry (crafted Surface Bits command with NSCodec codec ID).
1. Reconnaissance / Target Identification
An adversary operates or compromises an RDP server that FreeRDP clients connect to. Targets are identified by observing FreeRDP client connections — either via a server they control (e.g., a public jump host, phishing-delivered .rdp connection, or a malicious VDI entry) or by positioning on the network path (MITM) to inject into an existing connection. No client authentication is needed by the attacker; only the client's willingness to connect (UI:R).
2. Triggering the Flaw
Once the FreeRDP client connects, the malicious server sends a Surface Bits command whose payload declares the NSCodec codec ID. Because the vulnerable code in gdi_surface_bits fails to validate that a valid codec context exists before dereferencing it, the command drives the client into a NULL dereference — irrespective of whether NSCodec was administratively disabled.
3. Payload / Execution Method The "payload" is simply a malformed/unsupported-codec Surface Bits command embedded in the normal RDP graphics update stream. No additional exploit primitive is required — the crash is immediate on parse. This is a pure availability attack at the protocol-message level.
4. Post-Exploitation Impact The FreeRDP client process crashes (availability loss). No code execution, no credential theft, no lateral movement is implied. Repeat delivery yields a persistent denial of service against clients forced to reconnect to the hostile endpoint.
Detection Guidance
Detection must focus on the server-to-client direction of the RDP stream — an unusual inversion from typical RDP detections, which monitor inbound connections to exposed RDP servers. Because the connection is outbound from the FreeRDP client and the trigger is a protocol-level graphics command, detection is best approached through endpoint telemetry (process crashes), memory-corruption signatures, and — where deep packet inspection of RDP is available — anomalous Surface Bits/NSCodec traffic.
Sigma Rule
title: FreeRDP Client Crash - Surface Bits NSCodec Null Pointer Dereference (CVE-2026-91954)
id: 8a3f1c2e-5b4d-4e7a-9c1b-2d6f0a9e4b71
status: experimental
description: Detects FreeRDP client process crashes consistent with CVE-2026-91954,
a null pointer dereference in gdi_surface_bits triggered by a crafted Surface Bits
command declaring the NSCodec codec ID from a malicious RDP server.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-91954
author: valtersit.com Threat Intelligence
date: 2026/01/01
logsource:
category: process_creation
product: windows
detection:
selection_img:
Image|endswith:
- '\wfreerdp.exe'
- '\xfreerdp.exe'
selection_crash:
CommandLine|contains:
- 'freerdp'
selection_event:
EventID:
- 1000 # Application Error
- 1001 # Windows Error Reporting
condition: selection_img and selection_event
falsepositives:
- Legitimate FreeRDP client crashes unrelated to NSCodec
- Debugging sessions
level: medium
tags:
- attack.t1190
- cve.2026.91954
YARA Rule
rule CVE_2026_91954_FreeRDP_NSCodec_NullDeref
{
meta:
author = "valtersit.com Threat Intelligence"
description = "Detects FreeRDP binaries referencing the gdi_surface_bits code path and NSCodec strings associated with CVE-2026-91954"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-91954"
cve = "CVE-2026-91954"
date = "2026-01-01"
strings:
$freerdp = "FreeRDP" ascii wide nocase
$func = "gdi_surface_bits" ascii wide
$nscodec = "NSCodec" ascii wide nocase
$surface = "Surface Bits" ascii wide nocase
condition:
uint16(0) == 0x5A4D and
filesize < 50MB and
$freerdp and
(
($func and $nscodec) or
($surface and $nscodec)
)
}
Suricata IDS Rule
alert tcp any 3389 -> any any (msg:"CVE-2026-91954 FreeRDP Surface Bits NSCodec Null Pointer Dereference Attempt"; flow:from_server,established; content:"|00 00 00 00|"; content:"NSCodec"; nocase; within:100; content:"Surface"; nocase; within:200; classtype:attempted-dos; reference:cve,2026-91954; reference:url,nvd.nist.gov/vuln/detail/CVE-2026-91954; sid:202691954; rev:1;)
Note: The exact RDP wire encoding of Surface Bits/NSCodec is not disclosed in the CVE data; the rule above matches the observable string artifacts and should be refined against a packet capture of the actual trigger in a lab before production deployment.
Elastic Detection
process where process.name in ("wfreerdp.exe", "xfreerdp.exe") and
event.action == "process-exited" and
event.outcome == "crash"
EQL variant for Windows event telemetry:
sequence by host.name
[process where process.name : "wfreerdp.exe" or process.name : "xfreerdp.exe"]
[any where event.category == "process" and event.type == "stop" and process.exit_code != 0]
Splunk SPL Query
index=windows (sourcetype=WinEventLog:Application OR sourcetype=WinEventLog:Security)
(
EventCode=1000 OR EventCode=1001
)
(Message="freerdp*" OR Message="*gdi_surface_bits*" OR Message="*NSCodec*")
| stats count min(_time) as first_seen max(_time) as last_seen
by host, Message
| convert ctime(first_seen) ctime(last_seen)
| sort - count
Wazuh OSSEC Rule
<group name="freerdp,cve_2026_91954,">
<rule id="100954" level="10">
<decoded_as>json</decoded_as>
<field name="process.name">wfreerdp.exe|xfreerdp.exe</field>
<field name="event.outcome">crash</field>
<description>CVE-2026-91954: FreeRDP client crash (possible Surface Bits/NSCodec null pointer dereference)</description>
<mitre>
<id>T1190</id>
</mitre>
<group>availability</group>
</rule>
<rule id="100955" level="12">
<if_sid>100954</if_sid>
<field name="event.reason">access violation|segmentation fault|SIGSEGV|null pointer</field>
<description>CVE-2026-91954: FreeRDP client crash with memory fault signature (high confidence exploitation)</description>
<mitre>
<id>T1190</id>
</mitre>
</rule>
</group>
Mitigation & Patch Guidance
Patch confidence: 2/4 (CLAIMED) — A single source (OSV) indicates the issue is resolved in FreeRDP 3.31.0. This fix has not been independently confirmed against the upstream commit or vendor advisory. Treat as provisional and verify before relying on it operationally.
1. Immediate mitigations
- Where possible, disable NSCodec on the FreeRDP client — although note the CVE explicitly states the crash occurs even when the codec is disabled, so this is not a reliable workaround; it reduces attack surface but does not eliminate the flaw.
- Restrict FreeRDP clients to connecting only to trusted, administratively controlled RDP endpoints; avoid connecting to public/untrusted RDP hosts.
- Deploy network segmentation to prevent MITM injection into RDP sessions.
- Where MTI protection exists, enforce TLS/tunneled RDP to prevent transcript tampering.
2. Patch / upgrade steps
- Upgrade FreeRDP to 3.31.0 or later. Verify the fix against the upstream repository changelog and the OSV advisory before considering the issue closed.
- For package-managed installs (Debian/Ubuntu/RHEL/Homebrew), track the distribution's backport advisory.
- Validate the fixed build by testing a FreeRDP client against a lab RDP server that emits a Surface Bits command declaring NSCodec with the codec disabled.
3. Long-term hardening
- Adopt a client allowlist policy for outbound RDP destinations.
- Monitor client crash telemetry centrally (as shown in the detection rules) to identify attack attempts.
- Maintain a rapid patch cadence for RDP client binaries, given their exposure in the inbound graphics-parsing path.
- Contribute a regression test upstream so that the Surface Bits/NSCodec dispatch path is covered against future regressions.
References
- NVD — CVE-2026-91954: https://nvd.nist.gov/vuln/detail/CVE-2026-91954
- OSV Advisory (patch source, single-source claim): https://osv.dev/list?q=CVE-2026-91954
- CWE-476 — NULL Pointer Dereference: https://cwe.mitre.org/data/definitions/476.html
- MITRE ATT&CK T1190 — Exploit Public-Facing Application: https://attack.mitre.org/techniques/T1190/
- FreeRDP Project / Release Notes (verify 3.31.0 fix): https://github.com/FreeRDP/FreeRDP/releases
Exploit-in-the-wild evidence: None. CISA KEV: No. No public PoC, no Nuclei template, no exploit code available. EPSS 0.00346 (28th percentile) — low exploitation likelihood. No known threat actor campaigns are associated with this CVE at the time of writing.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.