FreeRDP: [RDPEAR] Truncated KERB_RPC_INTERNAL_NAME causes a client near-NULL dereference during cleanup
What fixes it
- 3.32.0FreeRDP, c/c++GitHub
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
[RDPEAR] Truncated KERB_RPC_INTERNAL_NAME causes a client near-NULL dereference during cleanup
> Private security report. Please keep this report and its attachments private > during triage and remediation.
Suggested advisory metadata
- Product: FreeRDP / libfreerdp client RDPEAR
- Affected versions: 3.9.0 through 3.31.1; current master at
4fa2b8efea2be8769e5499134f25182e64e13671
- Patched versions: none known
- Weakness: CWE-476, NULL Pointer Dereference
- Suggested severity: Moderate
- Conservative CVSS 3.1 vector:
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:H (5.3)
Summary
The RDPEAR NDR reader commits an attacker-controlled NameCount to a partially initialized KERB_RPC_INTERNAL_NAME before it has read the Names ref-pointer. If the packet ends at that point, generic error cleanup calls the descriptor's destructor with nameHints.count >= 2 and Names == NULL.
The destructor indexes the null base. Forming &obj->Names[0] is already undefined behavior; in GCC builds and unoptimized Clang builds, iteration one forms &obj->Names[1] and reads the Buffer field at address 0x28. A production-shaped CreateApReqAuthenticatorReq input terminates the parser in FreeRDP's default GCC Release configuration and under Clang ASan at -O0.
Root cause
ndr_read_KERB_RPC_INTERNAL_NAME() in channels/rdpear/common/rdpear_common.c does this:
if (!ndr_read_uint16(context, s, &res->NameType) ||
!ndr_read_uint16(context, s, &nameCount))
return FALSE;
res->nameHints.count = nameCount;
return ndr_read_refpointer(context, s, &names.ptrId) &&
ndr_push_deferreds(context, &names, 1);On truncation before the ref-pointer, ndr_read_pointedMessageEx() invokes the destructor on the zero-initialized, partially read object:
for (UINT32 i = 0; i < obj->nameHints.count; i++)
ndr_destroy_RPC_UNICODE_STRING(context, NULL, &obj->Names[i]);No guard establishes that obj->Names was allocated before the loop.
Reachability and reproduction
poc_partial_internal_name_cleanup.c embeds a valid CreateApReqAuthenticatorReq primary body and valid EncryptionKey deferred body. It then supplies NameType = 1, NameCount = 2, and ends immediately before the required Names pointer.
The same malicious-server RDPEAR path described in the scalar report reaches the parser when Remote Credential Guard is active. The Clang ASan -O0 result is:
ERROR: AddressSanitizer: SEGV on unknown address 0x000000000028
#0 ndr_destroy_RPC_UNICODE_STRING rdpear_common.c:246
#1 ndr_destroy_KERB_RPC_INTERNAL_NAME rdpear_common.c:370
#2 ndr_descr_destroy_KERB_RPC_INTERNAL_NAME
#3 ndr_read_pointedMessageEx ndr.c:929
#4 ndr_treat_deferred_read ndr.c:976
#5 main poc_partial_internal_name_cleanup.c:41GCC 13.3 at -O0, -O2, and the FreeRDP Release-equivalent -O3 -DNDEBUG LTO configuration also terminates with SIGSEGV for NameCount >= 2. Clang 18.1 at -O2 instead inlines a null-base check and rejects the same packet without a signal, although UBSan still identifies the null-pointer arithmetic. The demonstrated effect is therefore a compiler-dependent client process crash, not a universal deterministic crash. This report does not claim control of the near-NULL address, an arbitrary read/write, or code execution.
Fix direction and validation
PROPOSED_FIX.diff makes cleanup a no-op when Names was never allocated, and REGRESSION_TEST.diff drives the partial pointed-message failure with only four bytes. An alternative is to commit nameHints.count only after the ref-pointer has been parsed successfully, while keeping destructor-side hardening as defense in depth.
After applying the supplied guard, the identical harness exits normally:
DEFERRED_REJECT_WITHOUT_CRASH
TestRdpear TestNdr and TestRdpear TestNdrEar both pass under ASan, and a 1,781,846-execution follow-up fuzz run found no additional sanitizer crash.
The affected-version and public-duplicate review are shared with the scalar report. No exact public match was found; private reports remain unsearchable.
Reporter attribution: Han Sang-Kyung / HAN-BAMBO (@HAN-BAMBO)
Text from GitHub Advisory Database
Affected versions 1
- FreeRDP, c/c++>= 3.9.0, <= 3.31.1GitHub