Crash triage

Crash triage

TL;DR: Turn a crash dump into an answer to two questions — which bug class is this, and is it exploitable — using !analyze -v, register/disassembly context, page-heap, and a few WinDbg helpers.

What it is

A workflow for taking a crash artefact (user-mode .dmp, kernel MEMORY.DMP, or a live debugger break) and producing: bug class (OOB-W, UAF, type confusion, null deref, integer underflow), root-cause function, and a first-cut exploitability call. Triage discards uninteresting crashes quickly so fuzzing effort flows back into the corpus.

Preconditions / where it applies

  • Reproducible crash with a saved input or a dump.
  • Symbols available (Microsoft public symbol server srv*c:\sym*https://msdl.microsoft.com/download/symbols).
  • For heap bugs: page-heap (gflags /p /enable target.exe /full) so the bug fires at the corruption site instead of much later.

Technique

Open the dump.

1
2
3
windbg -z crash.dmp
.sympath SRV*c:\sym*https://msdl.microsoft.com/download/symbols
.reload /f

Headline command.

1
!analyze -v

The summary lists EXCEPTION_CODE, FAULTING_IP, BUCKET_ID and a heuristic exploitability score. Treat the score as a hint, never as proof.

Look at the actual fault site.

  • r — registers at fault. Look for attacker-controlled values in RIP, RAX, RCX.
  • u @rip / ub @rip — disassemble around the crash; identify the instruction class (write through register, indirect call, vtable deref).
  • dps @rsp L20 — stack walk and search for return addresses outside expected modules.
  • kb — call stack with parameters.

Heap bugs.

  • With page-heap on, the crash fires on the guard page directly adjacent to the corrupted allocation; !heap -p -a <addr> shows the allocation stack.
  • !address <addr> tells you region type (heap, stack, image, mapped file).

Kernel crashes.

  • !analyze -v plus !pool <addr>, !process 0 0, !irql, !locks.
  • !verifier if Driver Verifier is on — its sub-codes (0xC4, 0xC9, 0xCB) name the driver bug class.

Classification rules of thumb.

  • Write-through-pointer with attacker-influenced destination → likely exploitable.
  • Indirect call/jump through tainted register → exploitable modulo CFG/cfg-cet-kernel.
  • Read from NULL or from a non-canonical address with no attacker influence on the source → usually not.
  • Stack canary __report_gsfailure → overflow happened but mitigations caught it; still useful as a coarse bug locator.

De-duplicating fuzzer output. Hash on (BUCKET_ID, last-3 stack frames) and triage one representative per bucket.

Detection and defence

  • WER (Windows Error Reporting) collects mini-dumps centrally; mining them surfaces in-the-wild crashes attackers also see.
  • Driver Verifier in production-like test rings catches many kernel bugs before release.
  • Address sanitizer-style mitigations (page heap, Application Verifier) should be on for any fuzz target.

References

See also: kernel-debugging-with-windbg, mona-py, fuzzing-windows-drivers.