# From AI Agents to RCE - Building a Vulnerability Research Workflow

FreeRDP server redirection LoadBalanceInfo becomes oversized routing token, overflowing a 512-byte heap buffer; chained with USB-redirection heap leak to achieve client RCE.

- Payout: Research
- Severity: high
- Program: FreeRDP
- Found by (vendor credit): iarce-qb
- Write-up by: Julien Lair
- Disclosed: 2026-09-28
- CVE: CVE-2026-91964, CVE-2026-91963
- Write-up quality: 8/10
- Original: http://blog.quarkslab.com/from-ai-agents-to-rce-building-a-vulnerability-research-workflow.html
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-quarkslab-com-from-ai-agents-to-rce-building-a-vulnerability-research-workflow-html
- Tags: area:os domain:desktop domain:oss impact:data-leak impact:rce kind:research platform:research sev:high tech:linux technique:chained technique:source-review vuln:info-disclosure vuln:memory-corruption

**Target:** RDP client negotiation and USB redirection channel (libfreerdp/core/nego.c, channels/urbdrc/client/data_transfer)
**Root cause:** Server-supplied LoadBalanceInfo is reused as the routing token without the protocol length bound applied on the other path, so nego->RoutingTokenLength exceeds the 512-byte buffer allocated in nego_send_negotiation_request. Separately, a USB-redirection error path leaves an attacker-supplied OutputBufferSize in place with no data written, leaking uninitialized heap.
**Steps:**
1. Index FreeRDP at commit 993499447; reachability from `nego_recv` yields 9 files; slice 166 = libfreerdp/core/nego, slice 109 = channels/urbdrc/client/data_transfer.
2. Taint finds f-007 (attacker length into `nego->RoutingTokenLength` via `nego_set_routing_token`) and f-005 (same field as `Stream_Write` size in `nego_send_negotiation_request`).
3. Discard the safe path bounded by protocol length field; keep Server Redirection PDU path where LoadBalanceInfo is reused as routing token.
4. Malicious server sends 600-byte LoadBalanceInfo, wrapped with cookie header to 615 bytes.
5. Client reaches `Stream_Write` in `nego_send_negotiation_request` (nego.c:1098) via `rdp_client_redirect` (connection.c:715), copying 615 bytes into 512-byte `Stream_New` allocation (nego.c:1083) -> ASan heap-buffer-overflow WRITE of size 615.
6. Requires WITH_VERBOSE_WINPR_ASSERT=OFF (Debian/Ubuntu default); verbose asserts abort before memcpy.
7. USB path: error leaves OutputBufferSize set; ASan 0xcd fill shows 64/64 pattern bytes in emitted PDU region [36..100).
**How found:** Agentic harness: deterministic graph/taint core over-produced candidates (130 findings, 36 clusters), agent layer correlated f-007 and f-005 across the nego object lifetime that the engine reported separately, then dynamic ASan harnesses validated each candidate before chaining.
**Exploitation:** Hostile RDP server triggers the negotiation heap overflow (blind write) after session establishment; a server redirection forces client reconnect to trigger the write. The USB-redirection uninitialized heap disclosure leaks heap contents, used to derive libc base and defeat ASLR, then compose leak and write into one session to hijack control flow and execute a payload (/tmp/pwned). Researcher intervened twice: deriving libc base from leak and composing primitives into payload.
**Escalation:** Chained a blind heap write (CVE-2026-91964) with a heap info leak (CVE-2026-91963) to defeat ASLR and convert a crash primitive into client-side RCE.
**Insight:** When taint reports a source and a sink on the same struct field as separate findings, check every write path to that field: a length bound enforced on one protocol path (negotiation) is often absent on a second path (server redirection reusing LoadBalanceInfo), and build-config-dependent asserts (WITH_VERBOSE_WINPR_ASSERT) can mask the overflow in distro builds.

## Proof of concept (copied exactly from the original report)

**ASan heap-buffer-overflow trace from malicious server redirection**

```
==4514==ERROR: AddressSanitizer: heap-buffer-overflow
WRITE of size 615

#2 Stream_Write
#4 nego_send_negotiation_request libfreerdp/core/nego.c:1098
#9 rdp_client_redirect libfreerdp/core/connection.c:715

0x75e976227180 is located 0 bytes after 512-byte region
[0x75e976226f80,0x75e976227180) allocated by thread T2 here:

#1 Stream_New
#2 nego_send_negotiation_request libfreerdp/core/nego.c:1083
```

**Heap pattern leak measurement in emitted PDU**

```
[poc] heap pattern bytes (0xcd) in region [36..100): 64 / 64
```

