# Van1338: Design Flaw in Riot Vanguard: $6,000

Vanguard's process-callback-based protection left a race window where NtOpenProcess could grab a full-access handle to Valorant before protection applied.

- Payout: $6,000
- Severity: medium
- Program: Riot Games
- Credited: Kento Oki
- Disclosed: 2023-09-22
- Write-up quality: 7/10
- Original: https://github.com/kkent030315/Van1338
- Library page: https://bugbountylibrary.com/r/blog%3Agithub-com-kkent030315-van1338
- Tags: domain:desktop impact:privesc kind:bounty platform:blog sev:medium tech:windows technique:race-window technique:source-review vuln:access-control vuln:privilege-escalation vuln:race-condition

**Target:** Windows kernel anti-cheat driver (ObRegisterCallbacks / PsSetCreateProcessNotifyRoutineEx)
**Root cause:** Vanguard protected processes in a process-creation callback rather than an object callback, so object callbacks fire before process callbacks and a handle opened in that window escapes access-mask stripping.
**Steps:**
1. Loop calling `GetValorantProcessId()` to obtain target PID.
2. Initialize OBJECT_ATTRIBUTES and CLIENT_ID with the target PID.
3. Issue `NtOpenProcess(&handle, PROCESS_ALL_ACCESS, &oa, &cid)` directly via syscall to avoid kernelbase proxy overhead.
4. Win the race before Vanguard's process callback applies protection, obtaining a full-access handle.
5. Use the handle within the interval before Vanguard's periodic handle-table scan strips the access mask.
**How found:** Reasoned about Windows process-creation ordering: object callbacks are invoked before process callbacks, and the creator needs PROCESS_VM_READ/WRITE for PEB patches, so the question became how fast NtOpenProcess could be issued to beat the protection callback.
**Exploitation:** Attacker repeatedly opens a PROCESS_ALL_ACCESS handle to the Valorant process in a tight syscall loop, racing the protection callback; once a handle is acquired, they have a window to perform operations (e.g. PEB patching) before Vanguard's periodic thread strips the mask via direct kernel object manipulation.
**Insight:** When a product protects processes via PsSetCreateProcessNotifyRoutineEx, test whether an object callback would fire earlier; race NtOpenProcess directly via syscall (bypassing kernelbase) to grab a handle in the pre-protection window, and check whether periodic handle-table sweeps leave a usable interval.
