N-day recovery from vendor fixes: diff before/after builds, infer the bug class from added guards, verify with a PoC that crashes only the unpatched build. Adapted to platform rules: authorization boundaries, standing quality gates, no simulated results, rea MCP notes. Adapted from zhaoxuya520/reverse-skill skills/patch-diff-exploit (MIT). Refs #76
6.6 KiB
Executable File
name, description
| name | description |
|---|---|
| patch-diff | Invoke this skill when the task is recovering a vulnerability from a vendor fix: a CVE with a patch but no public PoC, Patch Tuesday triage, a distribution backport gap, or a closed-source security patch that needs root-cause analysis. Diff the patched and unpatched builds, infer the bug class from the added guards, and verify with a PoC that crashes only the unpatched build. |
Patch Diff (N-day recovery from vendor fixes)
Under an authorized engagement, recover a vulnerability from the fix that closed it: obtain the before and after builds, align symbols, diff the binaries, read the added checks to infer the bug class, then prove the bug with a PoC that reproduces on the unpatched build and runs clean on the patched one. The standing finding quality gates and the adversarial gate in your system prompt apply throughout: a crash you cannot reproduce on demand is a hypothesis, not a finding.
Attribution: adapted from the patch-diff-exploit playbook of zhaoxuya520/reverse-skill (MIT License).
Boundaries and authorization
| Allowed | Prohibited |
|---|---|
| Diffing and testing builds in your own lab, on machines covered by written engagement scope, or inside CTF and benchmark ranges | Running a recovered PoC against systems you do not own, even when a program covers the underlying bug |
| Writing PoCs that demonstrate the bug (crash, hang, memory-corruption evidence) | Turning a PoC into a deliverable payload |
Publishing or reporting with {target}, {host}, {user} placeholders |
Publishing unredacted hostnames, IPs, or usernames |
If the environment lacks a tool a step needs (see Environment notes), report the gap and stop. Never simulate diff output or fabricate crash evidence.
When to use
- Known CVE, no public PoC. The advisory names the component and bug class but ships only the fix.
- Patch Tuesday triage. Rank the month's kernel and driver fixes by likely exploitability before committing to deep analysis.
- Backport gap. The mainline fix is merged but a distribution, LTS branch, or OEM kernel has not picked it up.
- Closed-source security patches. Drivers, engines, virtualization components where the advisory is the only documentation.
Core method
patched build (after) unpatched build (before)
│ │
└───────── binary diff ─────────┘
│
function-level diff (matched / unmatched / changed)
│
focus on functions with moderate match scores (0.5 to 0.95)
│
read the new guards: bounds checks, locks, zeroing, refcounts
│
infer the bug class: OOB, race, UAF, info leak, int overflow
│
PoC on the unpatched build
│
verify: unpatched crashes, patched runs clean → bug confirmed
Patch pattern to bug class lookup:
| New guard in the patched build | Likely bug class |
|---|---|
if (a + b < a), __builtin_add_overflow |
integer overflow |
KeAcquireSpinLock, mutex_lock |
race condition (TOCTOU, double free) |
if (idx >= MAX), if (len > buf_size) |
out-of-bounds read or write |
RtlZeroMemory, memset(struct, 0, ...) |
uninitialized memory info leak |
InterlockedDecrement plus refcount check |
UAF, reference-count bug |
ProbeForRead / ProbeForWrite |
unvalidated user-mode pointer |
SeAccessCheck, capability checks |
missing permission check |
| removed or tightened IOCTL codes | attack-surface reduction (study the old path) |
Workflow
Step 1: obtain before and after builds.
- Windows: Microsoft Update Catalog for the MSU packages of two builds (last unpatched, first patched); unpack with
expand.exe pkg.msu -F:* out\, then the inner.cab; extractntoskrnl.exe,win32kfull.sys, and the affected drivers. - Linux:
apt download linux-image-<version>for both builds,dpkg-deb -xeach; restore the vmlinux ELF fromboot/vmlinuzwith extract-vmlinux; fetch the matching dbgsym package. - Third-party software: vendor archives for the N-1 and N installers.
Record sha256 for every artifact; the report cites hashes, not filenames.
Step 2: align symbols.
- Windows:
symchk /v module.dll /s SRV*c:\sym*https://msdl.microsoft.com/download/symbols. - Linux: vmlinux plus
System.mapor dbgsym for both builds.
Step 3: diff.
- Preferred in this environment:
ghidriff old_build new_build -o out/(Ghidra headless, pip-installable, markdown report). - BinDiff or Diaphora only where an IDA/Ghidra toolchain with exports already exists.
Step 4: locate the change. Filter to functions with similarity roughly 0.5 to 0.95: identical functions are noise, completely different ones are usually new or renamed. Read the added conditions, loop bounds, and deleted blocks (a deletion is also a clue). Judge by control flow and data flow, not token-level diffs; patches carry unrelated cleanup and renames.
Step 5: write and verify the PoC.
- Integer overflow: boundary values (
INT_MAX-1,0xFFFFFFFF). - Race: concurrent threads hammering the open/close plus ioctl path.
- UAF: spray, free, reuse.
- OOB: length or index just past the guard.
Run the same harness against the patched build and confirm the clean result; an untested patched build proves nothing, since the crash could be environmental.
Warnings
- A patch can shrink the attack surface instead of fixing the bug. The original flaw may still be reachable from another path; treat the fix as a lead, not ground truth.
- Mitigations are not fixes. CFG/XFG instrumentation such as
_guard_xfg_dispatch_icall_fptris hardening, not a root-cause fix. - Compiler churn is diff noise. Compare builds from the same toolchain; never diff across major compiler versions.
- Anonymize before anything leaves the lab.
Environment notes for this platform
- The rea MCP server (
mcp__rea__*) opens files read-only on the host and never executes targets. Use it for structure, strings, and symbol questions on either build; files must live under the shared data mount and rea takes host-side paths. ghidriffneeds Java plus a Ghidra install. Verify availability before promising a diff; if no diff toolchain exists, say so and stop.- PoC execution belongs in an authorized lab VM, never in the task container.
Completion self-check
- every workflow step was executed, not just read
- the diff, the inferred bug class, and the PoC artifacts are in the work dir with hashes
- the patched build was actually tested and its clean result recorded