{"schema":"threatline.record/2","record":{"id":"GHSA-93g7-296p-j77j","aliases":["GHSA-93g7-296p-j77j"],"summary":"[RDPEAR] Truncated KERB_RPC_INTERNAL_NAME causes a client near-NULL dereference during cleanup","details":"# [RDPEAR] Truncated KERB_RPC_INTERNAL_NAME causes a client near-NULL dereference during cleanup\r\n\r\n> Private security report. Please keep this report and its attachments private\r\n> during triage and remediation.\r\n\r\n## Suggested advisory metadata\r\n\r\n- Product: FreeRDP / libfreerdp client RDPEAR\r\n- Affected versions: 3.9.0 through 3.31.1; current master at\r\n  `4fa2b8efea2be8769e5499134f25182e64e13671`\r\n- Patched versions: none known\r\n- Weakness: CWE-476, NULL Pointer Dereference\r\n- Suggested severity: Moderate\r\n- Conservative CVSS 3.1 vector:\r\n  `CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:H` (5.3)\r\n\r\n## Summary\r\n\r\nThe RDPEAR NDR reader commits an attacker-controlled `NameCount` to a partially\r\ninitialized `KERB_RPC_INTERNAL_NAME` before it has read the `Names` ref-pointer.\r\nIf the packet ends at that point, generic error cleanup calls the descriptor's\r\ndestructor with `nameHints.count >= 2` and `Names == NULL`.\r\n\r\nThe destructor indexes the null base. Forming `&obj->Names[0]` is already\r\nundefined behavior; in GCC builds and unoptimized Clang builds, iteration one\r\nforms `&obj->Names[1]` and reads the `Buffer` field at address `0x28`. A\r\nproduction-shaped `CreateApReqAuthenticatorReq` input terminates the parser in\r\nFreeRDP's default GCC Release configuration and under Clang ASan at `-O0`.\r\n\r\n## Root cause\r\n\r\n`ndr_read_KERB_RPC_INTERNAL_NAME()` in\r\n`channels/rdpear/common/rdpear_common.c` does this:\r\n\r\n```c\r\nif (!ndr_read_uint16(context, s, &res->NameType) ||\r\n    !ndr_read_uint16(context, s, &nameCount))\r\n    return FALSE;\r\n\r\nres->nameHints.count = nameCount;\r\n\r\nreturn ndr_read_refpointer(context, s, &names.ptrId) &&\r\n       ndr_push_deferreds(context, &names, 1);\r\n```\r\n\r\nOn truncation before the ref-pointer, `ndr_read_pointedMessageEx()` invokes the\r\ndestructor on the zero-initialized, partially read object:\r\n\r\n```c\r\nfor (UINT32 i = 0; i < obj->nameHints.count; i++)\r\n    ndr_destroy_RPC_UNICODE_STRING(context, NULL, &obj->Names[i]);\r\n```\r\n\r\nNo guard establishes that `obj->Names` was allocated before the loop.\r\n\r\n## Reachability and reproduction\r\n\r\n`poc_partial_internal_name_cleanup.c` embeds a valid\r\n`CreateApReqAuthenticatorReq` primary body and valid `EncryptionKey` deferred\r\nbody. It then supplies `NameType = 1`, `NameCount = 2`, and ends immediately\r\nbefore the required `Names` pointer.\r\n\r\nThe same malicious-server RDPEAR path described in the scalar report reaches\r\nthe parser when Remote Credential Guard is active. The Clang ASan `-O0` result\r\nis:\r\n\r\n```text\r\nERROR: AddressSanitizer: SEGV on unknown address 0x000000000028\r\n    #0 ndr_destroy_RPC_UNICODE_STRING     rdpear_common.c:246\r\n    #1 ndr_destroy_KERB_RPC_INTERNAL_NAME rdpear_common.c:370\r\n    #2 ndr_descr_destroy_KERB_RPC_INTERNAL_NAME\r\n    #3 ndr_read_pointedMessageEx          ndr.c:929\r\n    #4 ndr_treat_deferred_read            ndr.c:976\r\n    #5 main                               poc_partial_internal_name_cleanup.c:41\r\n```\r\n\r\nGCC 13.3 at `-O0`, `-O2`, and the FreeRDP Release-equivalent `-O3 -DNDEBUG`\r\nLTO configuration also terminates with `SIGSEGV` for `NameCount >= 2`.\r\nClang 18.1 at `-O2` instead inlines a null-base check and rejects the same\r\npacket without a signal, although UBSan still identifies the null-pointer\r\narithmetic. The demonstrated effect is therefore a compiler-dependent client\r\nprocess crash, not a universal deterministic crash. This report does not claim\r\ncontrol of the near-NULL address, an arbitrary read/write, or code execution.\r\n\r\n## Fix direction and validation\r\n\r\n`PROPOSED_FIX.diff` makes cleanup a no-op when `Names` was never allocated,\r\nand `REGRESSION_TEST.diff` drives the partial pointed-message failure\r\nwith only four bytes. An alternative is to commit `nameHints.count` only after\r\nthe ref-pointer has been parsed successfully, while keeping destructor-side\r\nhardening as defense in depth.\r\n\r\nAfter applying the supplied guard, the identical harness exits normally:\r\n\r\n```text\r\nDEFERRED_REJECT_WITHOUT_CRASH\r\n```\r\n\r\n`TestRdpear TestNdr` and `TestRdpear TestNdrEar` both pass under ASan, and a\r\n1,781,846-execution follow-up fuzz run found no additional sanitizer crash.\r\n\r\nThe affected-version and public-duplicate review are shared with the scalar\r\nreport. No exact public match was found; private reports remain unsearchable.\r\n\r\nReporter attribution: Han Sang-Kyung / HAN-BAMBO (`@HAN-BAMBO`)\r\n","categories":["Unclassified"],"product":"FreeRDP","vendor":"","ecosystem":null,"affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-93g7-296p-j77j","source_modified_at":"2026-09-28T08:51:21Z","fetched_at":"2026-10-23T19:00:29.328130Z","package":{"ecosystem":"c/c++","name":"FreeRDP"},"range":">= 3.9.0, <= 3.31.1"}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-93g7-296p-j77j","source_modified_at":"2026-09-28T08:51:21Z","fetched_at":"2026-10-23T19:00:29.328130Z","package":{"ecosystem":"c/c++","name":"FreeRDP"},"version":"3.32.0"}],"cvss":5.3,"severity":"Medium","kev":{"listed":false},"epss":{"probability":null,"percentile":null,"score_date":null},"published_at":"2026-09-28T08:51:21Z","modified_at":"2026-09-28T08:51:21Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/FreeRDP/FreeRDP/security/advisories/GHSA-93g7-296p-j77j","fetched_at":"2026-10-23T19:00:29.328130Z","source_modified_at":"2026-09-28T08:51:21Z"}],"sources":["GitHub Advisory Database"],"references":[],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":null,"confidence":"unknown","reviewed":false},"change_timestamp":"2026-09-28T08:51:21Z","content_hash":"8b26aaca24a2f15d763070c701b39d4bb7fc4f8c8a8f1bc4dc5bedcab1e3d1a8"}}
