# CVE-2022-23270 — Windows PPTP `raspptp.sys` Remote Kernel Use-After-Free / Double-Free of a Call Context via `CallGetCall`

---

## Summary

| | |
|---|---|
| **Product** | Microsoft Windows — `raspptp.sys` (RAS PPTP VPN driver, RRAS server) |
| **CVE ID** | CVE-2022-23270 |
| **Impact** | Remote Code Execution — **pre-authentication**, kernel |
| **MSRC severity** | Critical |
| **CWE** | CWE-416: Use After Free → Double Free (race condition, CWE-362) |
| **Patch Date** | May 10, 2022 |
| **Pre-patch binary** | `raspptp.sys` 10.0.19041.1586 (Mar 2022) — SHA256 `e2792fc0ea333c8d2afa3030bc344a3438f79d0520f5b0c8411f2c7e1863a2f5` |
| **Post-patch binary** | `raspptp.sys` 10.0.19041.1706 (May 10 2022 fix) — SHA256 `49106033d9b81a5c413d2f4e4547a0f2071a1e95b4d9226c6c7290c7163c01ff` |
| **Fix gating** | None — object-lifetime rework (rundown protection + refcount) |

---

## Product Description

`raspptp.sys` implements Windows PPTP VPN. PPTP uses a **TCP 1723 control
connection** plus a **GRE data connection**; the control connection sets up and
manages **Calls**, each identifying a data stream so PPTP can multiplex several
streams over one connection. A **call context** (`CtlCall`) is created per call.
The control connection is reachable **pre-authentication** on TCP 1723, so a
memory-safety bug in call handling is a remote kernel RCE.

---

## Vulnerability Summary

A call context can be retrieved two ways, both keyed by the server-generated
**Call ID**:

- a **globally accessible Call-ID-indexed array** (`CallGetCall`), which can
  return **any** call from **any** control connection, and
- the **per-control-connection linked list** (`EnumListEntry` / `EnumComplete`),
  which contains only that connection's calls.

Several control-connection message handlers incorrectly use **`CallGetCall`** —
reaching calls that belong to *other* control connections — and parts of PPTP
handling run concurrently. Two operations then race on the **same** `CtlCall`:

1. **Closing a control connection** walks its call linked list and frees each
   call context via `CtlpCleanup`.
2. An **`OutgoingCallReply` with an error** frees the call it relates to — and
   looks that call up with `CallGetCall`, so it can free a call while the close
   routine is freeing it in another thread.

Run consecutively, the call context is **freed twice** — a use-after-free /
double-free (CWE-416/CWE-362) in kernel pool.

---

## Prerequisites and Constraints

- **Remote, unauthenticated** — reachable on TCP 1723 of an RRAS/PPTP server.
- The attacker learns a Call ID from the server's Incoming/Outgoing call reply,
  then drives the two racing paths (control-connection close + errored
  `OutgoingCallReply`) concurrently.
- Because `CallGetCall` exposes calls across control connections, the attacker
  can target a call from a second connection to widen the race window.
- Result is a UAF/double-free in kernel non-paged pool → DoS, and kernel RCE with
  grooming.

---

## Vulnerability Details

### Call Chain

```
Remote attacker (no auth), TCP 1723:
  thread A: close control connection -> CtlpCleanup walks CtlCallDoubleLinkedList,
            frees each CtlCall
  thread B: OutgoingCallReply(error) -> CallGetCall(AdapterCtx, CallId) -> frees the same CtlCall
        => CtlCall freed twice (UAF / double free)
```

### Root Cause

Two mistakes combine: control-connection handlers use the **global** `CallGetCall`
lookup (which crosses control-connection ownership) instead of the connection's
own linked list, and the call-context free path is not serialized/reference-
counted against the concurrent cleanup — so the same `CtlCall` is freed on two
threads.

### The patch (confirmed — diff, .1586 → .1706)

The May 2022 build reworks call/control object lifetime so a call cannot be freed
while another thread holds it:

- **Rundown protection** (`ExInitialize/Acquire/Release/WaitForRundownProtectionRelease`
  / `ExRundownCompleted`) is added around call/control object use and before free.
- New **`ReferenceRefCount` / `DereferenceRefCount`** add explicit reference
  counting; `CallGetCall`, `CallReceiveDatagramCallback`, `CtlpCleanup`,
  `CtlpEngine` and related paths take a reference while using the call and free it
  only once no user remains — eliminating the double-free.

### Patch Completeness Assessment

Not feature-gated — a structural object-lifetime rework (2022, pre-CFR-gating).
Patch state is determined by file version. The same overhaul fixes the
control-context UAF **CVE-2022-21972** in this build.

---

## Detection Guidance

**Network.** Inbound PPTP on **TCP 1723** where a client rapidly issues call
setup, errored `OutgoingCallReply`, and control-connection close in patterns
designed to race call teardown — especially referencing Call IDs across
connections.

**Crash signature.** Kernel double-free / pool-corruption bugchecks in
`raspptp!CtlpCleanup` / `CallGetCall` / `CtlpEngine` (`TPTP`/call pool tags) on
VPN servers.

**Exposure.** Restrict or disable the RRAS PPTP role; do not expose TCP 1723 to
untrusted networks (PPTP is deprecated).

---

## References

- LRQA / Nettitude — *CVE-2022-23270: Windows Server VPN Remote Kernel Use After
  Free Vulnerability*.
- MSRC advisory — CVE-2022-23270 (PPTP Remote Code Execution)
- Full binary diff: `/data/patch_diffs/raspptp_sys-cve-2022-23270-ghidriff.md`
- Sibling bug: CVE-2022-21972 (raspptp.sys control-context UAF, same build)
