# HEVD-UninitializedStackVariable
HEVD:漏洞靶场,包含各种Windows内核漏洞的驱动程序项目,在Github上就可以找到该项目,进行相关的学习
Releases · hacksysteam/HackSysExtremeVulnerableDriver · GitHub
环境准备:
Windows 7 X86 sp1 虚拟机
使用VirtualKD和windbg双机调试
HEVD 3.0+KmdManager+DubugView
TriggerUninitializedMemoryStack函数存在漏洞,内核函数栈中的变量未初始化,如果用户传入的UserValue==MagicValue(0xBAD0B0B0),就会赋值参数value和回调函数callback地址,并在后面调用callback。如果uservalue不等MagicValue,就存在利用点。
在非安全版本中UninitializedMemory变量定义但是没有初始化,由于该变量布局在栈上,它会拥有一个之前调用函数遗留的随机垃圾值。
源码:
逆向HEVD.sys
if (UninitializedMemory.Callback) //此处判断回调函数是否为空,否则含有空指针漏洞,可利用在0页内存上构造payload,相关HEVD:
[原创]内核漏洞学习[5]-HEVD-NullPointerDereference-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com
那么既然有判断,就要换个方法,回调函数地址修改为不为0的地址,然后该地址指向payoad,
HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK控制码对应的派遣函数UninitializedMemoryStackIoctlHandlercase
UninitializedMemoryStackIoctlHandlercase 函数调用TriggerUninitializedMemoryStack触发漏洞
逆向获得IO控制码0x22202f
sub_4460E8-->sub_445FFA(漏洞函数)
(1)测试
因为uservalue=0xBAD0B0B0,所以打印出信息,
*(PDWORD32)(buf) = 0x12345678;
将值修改为0x12345678,再次运行
我们传入值与MagicValue值不匹配时,则会触发漏洞
在cmp处下断点,
HEVD!TriggerUninitializedMemoryStack偏移 0x53处,下断点,运行
执行我们的测试exp 断下来后查看
stack_init-callback=94083ed0-940839cc=504(1284 bytes)
如果我们比较失败了,继续下行调用函数的时候,我们调用的将是一个内核栈上的垃圾值!此值是不固定的,它会拥有一个之前调用函数遗留的随机垃圾值。
我们已经知道了该变量距离当前栈起始位置有多远,在驱动程序源代码中看到,易受攻击的代码被try/except包围,目标操作系统不会崩溃。无法用测试exp触发系统BSOD,
现在,如果我们可以将攻击者控制的数据放在与Stack Init 0x504的偏移处,我们就可以劫持指令指针。
如何从用户模式将用户控制的数据放在内核堆栈上?
看j00ru](https://j00ru.vexillium.org/2011/05/windows-kernel-stack-spraying-techniques/))写的方法,官方exp和fuzzysecurity用的都是NtMapUserPhysicalPages函数,它的一部分功能是拷贝输入的字节到内核栈上的一个本地缓冲区。最大尺寸可以拷贝1024*IntPtr::Size(32位机器上是4字节)=>4096字节,函数的栈最大也就 4096byte,所以传4096大小就可以占满一页内存,我们将所有内容都写成payload的地址
nt!NtMapUserPhysicalPages
鉴于 __chkstk 是一个特殊的过程,它将堆栈指针降低给定的字节数——这里是 0x10e8——该函数肯定会使用大量的本地存储(不止一个,典型的内存页!)
NtMapUserPhysicalPages调用MiCaptureUlongPtrArray
NtMapUserPhysicalPages函数分配一个包含1024的单位的本地缓冲区(就是该函数栈的缓冲区)可以在本地存储多达 4096(1024*sizeof(ULONG_PTR)) 个用户提供的字节(正好是一个内存页),
调用NtMapUserPhysicalPages之前的内核堆栈
调用NtMapUserPhysicalPages之后的堆栈布局
我们之前已经算出来,未初始化变量的callback距离stack_init 偏移0x504大小
那么我们的exp先调用NtMapUserPhysicalPages函数将我们的payload地址写入缓冲区,共写入4096字节大小,那么在我们调用漏洞函数,进而调用callback使,我们的callback值就是文章最开始说的“在非安全版本中UninitializedMemory变量定义但是没有初始化,由于该变量布局在栈上,它会拥有一个之前调用函数遗留的随机垃圾值” 所以调用callback,就会执行我们构造的payload
(2)漏洞利用
exp,可供参考
payload功能:遍历进程,得到系统进程的token,把当前进程的token替换,达到提权目的。
相关内核结构体:
在内核模式下,fs:[0]指向KPCR结构体
payload:
运行exp,喷射成功,提权成功:
注意:我们从用户模式控制内核堆栈上的数据。必须防止它被其他函数调用破坏。
为此,我们需要防止任何其他函数使用内核堆栈。只需确保在喷射内核栈并触发漏洞后不执行或调用任何其他函数即可。
内核函数栈中的变量定义后,进行初始化,比如源码给的安全版本:
nt!NtMapUserPhysicalPages and Kernel Stack-Spraying Techniques | j00ru//vx tech blog (vexillium.org)
Seebug
未初始化的堆栈变量 – Windows 内核开发 (payatu.com)
[原创]Windows Kernel Exploit 内核漏洞学习(6)-未初始化栈利用-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com
[翻译]Windows exploit开发系列教程第十三部分:内核利用程序之未初始化栈变量-外文翻译-看雪论坛-安全社区|安全招聘|bbs.pediy.com
ps:因为是自己边学习别记录的,就看着有点啰嗦???但是感觉写的很好理解。。
NTSTATUS
TriggerUninitializedMemoryStack(
_In_ PVOID UserBuffer
)
{
ULONG UserValue
=
0
;
ULONG MagicValue
=
0xBAD0B0B0
;
NTSTATUS Status
=
STATUS_SUCCESS;
/
/
/
/
Secure Note: This
is
secure because the developer
is
properly initializing
/
/
UNINITIALIZED_MEMORY_STACK to NULL
and
checks
for
NULL pointer before calling
/
/
the callback
/
/
UNINITIALIZED_MEMORY_STACK UninitializedMemory
=
{
0
};
/
/
安全版本: 栈变量初始化了
/
/
/
/
Vulnerability Note: This
is
a vanilla Uninitialized Memory
in
Stack vulnerability
/
/
because the developer
is
not
initializing
'UNINITIALIZED_MEMORY_STACK'
structure
/
/
before calling the callback when
'MagicValue'
does
not
match
'UserValue'
/
/
UNINITIALIZED_MEMORY_STACK UninitializedMemory;
/
/
不安全版本: 栈变量未初始化
PAGED_CODE();
__try
{
/
/
/
/
Verify
if
the
buffer
resides
in
user mode
/
/
ProbeForRead(UserBuffer, sizeof(UNINITIALIZED_MEMORY_STACK), (ULONG)__alignof(UCHAR));
/
/
/
/
Get the value
from
user mode
/
/
UserValue
=
*
(PULONG)UserBuffer;
DbgPrint(
"[+] UserValue: 0x%p\n"
, UserValue);
DbgPrint(
"[+] UninitializedMemory Address: 0x%p\n"
, &UninitializedMemory);
/
/
/
/
Validate the magic value
/
/
if
(UserValue
=
=
MagicValue) {
UninitializedMemory.Value
=
UserValue;
UninitializedMemory.Callback
=
&UninitializedMemoryStackObjectCallback;
}
DbgPrint(
"[+] UninitializedMemory.Value: 0x%p\n"
, UninitializedMemory.Value);
DbgPrint(
"[+] UninitializedMemory.Callback: 0x%p\n"
, UninitializedMemory.Callback);
DbgPrint(
"[+] Triggering Uninitialized Memory in Stack\n"
);
/
/
/
/
Call the callback function
/
/
if
(UninitializedMemory.Callback)
/
/
在此处判断回调函数是否为
0
,否则可利用
0
页内存,
{
UninitializedMemory.Callback();
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status
=
GetExceptionCode();
DbgPrint(
"[-] Exception Code: 0x%X\n"
, Status);
}
return
Status;
}
NTSTATUS
TriggerUninitializedMemoryStack(
_In_ PVOID UserBuffer
)
{
ULONG UserValue
=
0
;
ULONG MagicValue
=
0xBAD0B0B0
;
NTSTATUS Status
=
STATUS_SUCCESS;
/
/
/
/
Secure Note: This
is
secure because the developer
is
properly initializing
/
/
UNINITIALIZED_MEMORY_STACK to NULL
and
checks
for
NULL pointer before calling
/
/
the callback
/
/
UNINITIALIZED_MEMORY_STACK UninitializedMemory
=
{
0
};
/
/
安全版本: 栈变量初始化了
/
/
/
/
Vulnerability Note: This
is
a vanilla Uninitialized Memory
in
Stack vulnerability
/
/
because the developer
is
not
initializing
'UNINITIALIZED_MEMORY_STACK'
structure
/
/
before calling the callback when
'MagicValue'
does
not
match
'UserValue'
/
/
UNINITIALIZED_MEMORY_STACK UninitializedMemory;
/
/
不安全版本: 栈变量未初始化
PAGED_CODE();
__try
{
/
/
/
/
Verify
if
the
buffer
resides
in
user mode
/
/
ProbeForRead(UserBuffer, sizeof(UNINITIALIZED_MEMORY_STACK), (ULONG)__alignof(UCHAR));
/
/
/
/
Get the value
from
user mode
/
/
UserValue
=
*
(PULONG)UserBuffer;
DbgPrint(
"[+] UserValue: 0x%p\n"
, UserValue);
DbgPrint(
"[+] UninitializedMemory Address: 0x%p\n"
, &UninitializedMemory);
/
/
/
/
Validate the magic value
/
/
if
(UserValue
=
=
MagicValue) {
UninitializedMemory.Value
=
UserValue;
UninitializedMemory.Callback
=
&UninitializedMemoryStackObjectCallback;
}
DbgPrint(
"[+] UninitializedMemory.Value: 0x%p\n"
, UninitializedMemory.Value);
DbgPrint(
"[+] UninitializedMemory.Callback: 0x%p\n"
, UninitializedMemory.Callback);
DbgPrint(
"[+] Triggering Uninitialized Memory in Stack\n"
);
/
/
/
/
Call the callback function
/
/
if
(UninitializedMemory.Callback)
/
/
在此处判断回调函数是否为
0
,否则可利用
0
页内存,
{
UninitializedMemory.Callback();
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status
=
GetExceptionCode();
DbgPrint(
"[-] Exception Code: 0x%X\n"
, Status);
}
return
Status;
}
case HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK:
DbgPrint(
"****** HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK ******\n"
);
Status
=
UninitializedMemoryStackIoctlHandler(Irp, IrpSp);
DbgPrint(
"****** HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK ******\n"
);
break
;
case HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK:
DbgPrint(
"****** HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK ******\n"
);
Status
=
UninitializedMemoryStackIoctlHandler(Irp, IrpSp);
DbgPrint(
"****** HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK ******\n"
);
break
;
HANDLE hDevice
=
NULL;
/
/
int
main()
{
hDevice
=
CreateFileA(
"\\\\.\\HackSysExtremeVulnerableDriver"
, GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL
);
if
(hDevice
=
=
INVALID_HANDLE_VALUE || hDevice
=
=
NULL)
{
printf(
"[-]failed to get device handle !"
);
return
FALSE;
}
printf(
"[+]success to get device handle"
);
if
(hDevice) {
DWORD bReturn
=
0
;
char buf[
4
]
=
{
0
};
*
(PDWORD32)(buf)
=
0xBAD0B0B0
;
DeviceIoControl(hDevice,
0x22202f
, buf,
4
, NULL,
0
, &bReturn, NULL);
}
}
HANDLE hDevice
=
NULL;
/
/
int
main()
{
hDevice
=
CreateFileA(
"\\\\.\\HackSysExtremeVulnerableDriver"
, GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL
);
if
(hDevice
=
=
INVALID_HANDLE_VALUE || hDevice
=
=
NULL)
{
printf(
"[-]failed to get device handle !"
);
return
FALSE;
}
printf(
"[+]success to get device handle"
);
if
(hDevice) {
DWORD bReturn
=
0
;
char buf[
4
]
=
{
0
};
*
(PDWORD32)(buf)
=
0xBAD0B0B0
;
DeviceIoControl(hDevice,
0x22202f
, buf,
4
, NULL,
0
, &bReturn, NULL);
}
}
kd> bp HEVD!TriggerUninitializedMemoryStack
+
0x53
kd> g
kd> bp HEVD!TriggerUninitializedMemoryStack
+
0x53
kd> g
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-11-21 16:14
被pyikaaaa编辑
,原因: