OriginalCompressedSegmentSize + Offset/Length之和进行检查,导致整型溢出,SrvNetAllocateBuffer对于size小于0x1100的情况默认申请了0x1200,虽然没有导致缓冲区溢出,但由于内存布局的特殊性,我们可以覆盖位于其中的指向保存解压后数据的指针
OriginalCompressedSegmentSize为0xffffffff,Offset/Length为8时(其实offset长度就是我们最后任意写的长度,具体原因后面分析),此时就会发生整型溢出


srvnet!SrvNetAllocateBuffer中的分配逻辑,经过分析可以看到当申请的内存大小小于0x1100时,默认分配了0x1278字节
SrvNetAllocateBuffer返回的内存地址并不是上面srvnet!SrvNetAllocateBuffer中调用ExAllocatePoolWithTag返回的地址(这里称为bufferA),而是位于bufferA + 0x1150偏移的地方(这里称为bufferB),且bufferB + 0x18的位置存放的是bufferA + 0x50

srvnet!SmbCompressionDecompress,其中存放解压数据的缓冲区为*(bufferB + 0x18) + SMB2 COMPRESSION_TRANSFORM_HEADER.offset,分析后面的memmove可以知道这是为SMB2 COMPRESSION_TRANSFORM_HEADER和compressed data之间的数据保留空间
Offset/Length (4 bytes): If SMB2_COMPRESSION_FLAG_CHAINED is set in Flags field, this field MUST be interpreted as Length. The length, in bytes, of the compressed payload. Otherwise, this field MUST be interpreted as Offset. The offset, in bytes, from the end of this structure to the start of compressed data segment.

经过上面的分析,其实如何实现任意地址任意写已经很明显了,我们可以通过覆盖位于bufferB + 0x18处的指针为我们想要写的地址,在 SMB2 COMPRESSION_TRANSFORM_HEADER和compressed data之间填充我们想要写的数据,通过上面的memmove实现任意地址任意写

因此可以通过compressed data segment覆盖bufferB + 0x18的位置,因为memmove的目的地址就是bufferB + 0x18,而memmove的源地址和长度分别是someData和Offset/Length,所以我们就可以通过构造这几个位置的数据来达到任意地址任意写。
布置任意地址写的位置是通过Offset/Length来计算的,因为srvnet!SmbCompressionDecompress会为someData预留Offset/Length大小的位置,所以计算方法为AR_Write_addr_offset = 0x1150 - 0x50 + 0x18 - Offset,代码实现为:

NetBIOS头的StreamProtocolLength也要修正

Token地址


https://paper.seebug.org/1164/
https://ricercasecurity.blogspot.com/2020/04/ill-ask-your-body-smbghost-pre-auth-rce.html
原文:https://www.cnblogs.com/DreamoneOnly/p/12781899.html