CVE-2025-29824 — CLFS CClfsLogCcb Use-After-Free (IRP Race)
Last updated: 2026-04-10
Component: clfs.sys
Bug Class: Use-After-Free — CClfsLogCcb freed during IRP_MJ_CLEANUP while IOCTL still in flight
Patch: April 8, 2025 Patch Tuesday
Exploited ITW: Yes — Storm-2460 (RansomEXX), targeted Saudi Arabia, Spain, Venezuela, USA
Discoverer: Microsoft Threat Intelligence (during Storm-2460 investigation)
Related: Clfs, Use After Free, Race Conditions
Tags:clfs,uaf,irp-race,lookaside,ioctl,kernel-mode,lpe,ransomware
Summary
CVE-2025-29824 is a use-after-free in clfs.sys. When a CLFS file descriptor is closed, Windows sends two I/O requests to the driver: IRP_MJ_CLEANUP followed by IRP_MJ_CLOSE. The bug: CClfsLogCcb::Release (which may free the CClfsLogCcb object) was called during IRP_MJ_CLEANUP rather than IRP_MJ_CLOSE. This means:
IRP_MJ_CLEANUPfreesCClfsLogCcb- An IOCTL that uses
FsContext2(which points toCClfsLogCcb) can be issued from user space between CLEANUP and CLOSE, operating on freed memory
Patch: Move CClfsLogCcb::Release call from CClfsLogCcb::Cleanup to CClfsRequest::Close — so the object is not freed until CLOSE completes.
CClfsLogCcb Structure
CClfsLogCcb is the per-file-descriptor context block stored in FileObject->FsContext2. Size: 0x110 bytes.
Offset Field Type Notes
+0x00 vtable PTR64 CClfsLogCcb vtable
+0x08 ... ...
+0x18 reference_count LONG Decremented by CClfsLogCcb::Release
+0x20 ...
+0x98 m_Resource ERESOURCE (0x68 bytes) — executive resource for sync
+0x100 ...
The reference count starts at 1 when CClfsLogCcb is created. CClfsLogCcb::Release decrements it; when it hits 0, the destructor runs and the pool allocation is freed.
IRP Sequence and Race Window
Normal sequence:
User: CloseHandle(log_file)
→ Kernel: IRP_MJ_CLEANUP (CClfsRequest::Cleanup → CClfsLogCcb::Cleanup)
→ IRP_MJ_CLOSE (CClfsRequest::Close)
Buggy behavior (pre-patch):
IRP_MJ_CLEANUP:
CClfsLogCcb::Cleanup called
→ CClfsLogCcb::Release() ← decrements refcount → if 0, FREES CClfsLogCcb
← returns
*** RACE WINDOW: user can issue IOCTL here ***
*** FsContext2 still points to freed CClfsLogCcb ***
IRP_MJ_CLOSE:
CClfsRequest::Close called
(object already freed — but CLOSE code may use FsContext2 again)
Key fact: During IRP_MJ_CLEANUP, operations can still be initiated from user space. This is the standard Windows IRP model — IRP_MJ_CLEANUP signals intent to close, but descriptor is not yet invalid.
LookasideList Exploitation Requirement
CClfsLogCcb is freed via LookasideList (kernel lookaside list with depth 24). This means:
- Objects freed into a LookasideList with
< 24entries are not returned to the pool — they remain in the list - An attacker must fill the LookasideList before the freed object can actually be reallocated
- Required approach:
- Create ≥ 25 file descriptors for the same log file
- Close 24 of them → fills LookasideList (objects stay in list, not freed to pool)
- The 25th closure causes the freed
CClfsLogCcbto actually return to the pool → becomes available for reallocation - Race: between step 3’s CLEANUP and CLOSE, spray a same-size object to reclaim the pool slot
- Issue IOCTL to get
clfs.systo use the reclaimedCClfsLogCcbas if it were valid
Vulnerable IOCTLs
The following IOCTLs operate on CClfsLogCcb (from FsContext2) after it may be freed:
| IOCTL Code | Function | Notes |
|---|---|---|
0x8007A827 | CClfsRequest::StartArchival | Primary exploitation vector (uses CClfsLogCcb for archive context) |
0x8007281F | Archive-related IOCTL | Secondary vector |
0x80076856 | Log state query | Reads from CClfsLogCcb.m_Resource (ERESOURCE at +0x98) |
CClfsRequest::StartArchival is documented in the bi.zone deep-dive as the key IOCTL to weaponize:
__int64 CClfsRequest::StartArchival(CClfsRequest *this, PIRP Irp) {
CClfsLogCcb *pCcb = (CClfsLogCcb *)this->m_pFileObject->FsContext2;
// pCcb may be freed if CLEANUP raced ahead
pCcb->m_Resource.AcquireExclusive(); // UAF: corrupt ERESOURCE
...
}
Exploitation Strategy
After UAF trigger, CClfsLogCcb (0x110 bytes) is freed and can be reclaimed by a controlled allocation. Key targets:
Same-size pool object reclaim: Spray 0x110-byte pool objects with controlled content →
CClfsLogCcb.vtablebecomes controllable → next virtual call = arbitrary dispatchERESOURCE corruption (at +0x98):
ERESOURCEcontains linked lists; corrupt list pointers → arbitrary write onExReleaseResourceForThreadLiteunlinkReference count manipulation: Craft fake
CClfsLogCcbwithreference_count = 0to cause immediate re-free → double-free chaining
Post-UAF primitive: Identical to other CLFS exploits — pipe attribute AAR/AAW or PreviousMode (Win10):
- Win10: Corrupt
KTHREAD.PreviousMode→ arbitrary R/W → token steal - Win11: Pipe attribute AAR/AAW → read SYSTEM token → replace current process token
Attack Chain (Storm-2460 / RansomEXX)
1. dllhost.exe loads PipeMagic backdoor
2. CVE-2025-29824 exploit executed from dllhost.exe address space
3. Creates C:\ProgramData\SkyPdf\PDUDrv.blf (CLFS exploit artifact)
4. NtQuerySystemInformation for kernel address leaks
5. RtlSetAllBits via EPROCESS traversal → all privileges enabled
6. Code injection: winlogon.exe → procdump.exe → dllhost.exe
7. Procdump: dump lsass.exe → credential theft
8. RansomEXX: encrypt files via dllhost.exe --do [ransomware path]
Indicators of Compromise
- BLF file:
C:\ProgramData\SkyPdf\PDUDrv.blf - Process:
dllhost.exeexecutingprocdump.exe -ma lsass.exe - Privilege escalation technique:
RtlSetAllBitson token privileges
Detection
- EDR: Watch for
clfs.sysIOCTL calls from processes issuingIRP_MJ_CLEANUP+ IOCTL in rapid succession - File system:
C:\ProgramData\SkyPdf\PDUDrv.blfcreation - Behavior:
dllhost.exespawningprocdump.exewith-ma lsass.exearguments
References
- bi.zone — “Deep dive into CVE-2025-29824 in Windows” — 2025-08-18
- StarLabs / Ong How Chong — “My Blind Date with CVE-2025-29824” (CClfsLogCcb structure layout, PoC crash trace)
- Kaspersky — PipeMagic backdoor analysis (backdoor delivery vehicle)
- Microsoft MSRC — CVE-2025-29824 advisory — April 2025
