首页
论坛
课程
招聘
[驱动开发] [HOOK/注入] [开源分享] [原创]CVE主防模块分享
2021-4-7 09:01 5557

[驱动开发] [HOOK/注入] [开源分享] [原创]CVE主防模块分享

2021-4-7 09:01
5557

概述:

  业余学习笔记,主防模块分享。

本篇环境:

1
2
3
4
1. wdk 7600 or Up new wdk
2. vs2015/17/19
3. Windows Win7 x86 sp1 / x64
4. IE Version 10/11

知识点:

基础:

  DriverCallback/InlineHook/electron/ALPC

安全:

  漏洞/样本分析

假设/构造:

  从0开始想对病毒/漏洞进行防御,抵御已知的恶意手法/CVE,做一个非常简单的防御系统,样本分析或者Poc分析必不可少。基础恶意代码可以从攻击手法和多点防御,基础漏洞可以从某点上下文关键变量,内存判断。

IE部署问题:

1
2
3
4
5
6
1. Ie8 部分CVE_js/vbs启动调试崩溃,解决方案更新至10/11
2. 升级IE 11之前需要安装必备的补丁包,链接如下:
https://docs.microsoft.com/zh-cn/troubleshoot/browsers/prerequisite-updates-for-ie-11
3. 升级IE 11,F12 Debug空白,仿真报错,安装补丁IE11-Windows6.1-KB3008923-x86即可解决。
4. js支持友好,vbs支持不友好,仿真兼容改成5。如果vbs还是不友好,Poc中加入VB函数,Windbg可以下断识,做为单步来Windbg观察。
vbs/js有些可以不使用IE调试,能触发就行(触发和利用两个概念)。

  优质文章防御思路可以快速上手,推荐银雁冰好文有条有理(文末附带防御思路)。当然你可以找关键点,不去理解调试过程,但是失去了学习的意义:
https://bbs.pediy.com/thread-228371.htm

构造方案:

1) Driver Callback监控Process-Thread-Module-Reg,如IE漏洞进程启动或者加载oleaut32.dll时候触发注入,ALPC通知应用层注入Mon.dll。

 

2) r3_Inject_DLL注入IE进行Inlinehook,监控变量及内存否存在uaf。触发拦截通知ServerPort,UI决定动作放行/拦截。

代码实现:

基础框架:

 

alpc工程是lib,Cvecheck(DLL)-CveServer(Server)都链接该Lib库通信。

ALPC:

通信流程如下(端口一对一模式):

 

  NtAlpcCreatePort创建AlpcPort,利用AlpcSendWaitR等待端口Port被请求接收包,多线程非预期有可能不是上线请求,需要判断自定义的功能号区分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum CommandofCodeID
{
    ALPC_DRIVER_DLL_INJECTENABLE = 1,
    ALPC_DRIVER_DLL_INJECTDISABLE,
 
    ALPC_DRIVER_CONNECTSERVER = 10,
    ALPC_DRIVER_CONNECTSERVER_RECV,
    ALPC_DLL_CONNECTSERVER,
    ALPC_DLL_CONNECTSERVER_RECV,
    ALPC_UNCONNECTSERVER,
 
    ALPC_DLL_MONITOR_CVE = 30,
    ALPC_DLL_INJECT_SUCCESS,
    ALPC_DLL_INJECT_FAILUER
};

  客户端NtAlpcConnect,服务端AlpcSendWaitR接收到连接请求,用NtAlpcAccept关联MessageId关联,绑定客户端handle。

 

  服务端绑定NtAlpcAccept同时,也会发送给客户端消息Msg,参数7不但关联MessageId,且可以返回一个数据包给客户端,客户端接收之后可以处理第一次的上线请求。

 

 

  客户端发送Server消息单纯PortHandle是不够的,发送至客户端pmRequest里面的MessageId字段标识客户端连接时候ID。
  r3创建事件消息,句柄通过上线握手传递给驱动(客户端),驱动拿到手进行对象获取。应用场景就驱动事件等待r3处理进入睡眠,处理完成后被唤醒继续执行。代码中给出了这种方式应用示例没有使用,注入成功理论传递至驱动在做判断/使用内核态事件。

 

目前ALPC端口是1对1,后续会更改成1对多。

Server:

ALPC服务端,分别处理DLL_Monitor消息和Driver_Monitor。

 

 

负责注入DLL(注入器) --- 代码中直接用了Apc注入,不在传输至r3,因为这种方案会导致死锁。

 

Driver <--> Server:

负责回调拦截相关进程/模块,监控触发注入操作。

 

Driver会先初始化ALPC_PORT,发送ALPC_DRIVER_CONNECTSERVER告诉服务端请求连接。

 

 

Server接收到Driver请求构造回复,用于第一次上线将R3初始化句柄传递至r0,后续没用这种方式。

 

 

Driver连接成功后创建读线程,AlpcRecvServerMsgROUTINE负责阻塞接收服务端发来的MSG请求。

1
2
3
4
5
6
7
8
PsCreateSystemThread(
    &g_Recvhandle,
    THREAD_ALL_ACCESS,
    NULL,
    NtCurrentProcess(),
    NULL,
    (PKSTART_ROUTINE)AlpcRecvServerMsgROUTINE,
    NULL);

 

进行模块监控,回调内容针对CVE-2016-0189,过滤条件oleaut32.dll和iexplore.exe。

1
status = PsSetLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)PsLoadImageCallbacks);
1
if (NULL != wcsstr(FullImageName->Buffer, L"Windows\\System32\\oleaut32.dll"))

 

  如果是iexplore.exe进程且加载了oleaut32.dll(代码中是注册的模块回调,先监视ole加载,后判断iexplore进程),触发注入之后ALPC发送r3开始注入,回调中KeWaitForSingleObject事件等待,等待注入完成再执行,真实测试中r3再跨进程内存申请会阻塞VirtualAllocEx(这种方案会造成死锁),所以直接回调APC注入解决。

 

  关于上述阻塞问题,大佬给除了较为详细的回答:因为模块回调是从mapview内进入的,内部有上线程锁,然后你又在同一线程allocate,vad的分配映射不能嵌套。因为如果发生缺页,那么就属于同一线程的物理页面竞争,而负责这块的平衡集管理器是按线程策略调度物理页面的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//  Send MSG r3 Server to Process HookMsg
DIRVER_INJECT_DLL drinjectdll = { 0, };
INT32 Pids = 0;
drinjectdll.ImageBase = ImageInfo->ImageBase;
drinjectdll.Pids = PsGetCurrentProcessId();
drinjectdll.univermsg.ControlId = ALPC_DRIVER_DLL_INJECTENABLE;
 
 
AlpcSendMsgtoInjectDll(&drinjectdll);
 
//
// Wait Inject Process
//
if (&g_kEvent)
{
    // KeWaitForSingleObject(g_pInjectEvent, Executive, KernelMode, FALSE, NULL); // INFINITE
    // Wait
    KeWaitForSingleObject(&g_kEvent, Executive, KernelMode, FALSE, NULL);
    DbgBreakPoint();
    KeClearEvent(&g_kEvent);
}
 
// APC注入
    KAPC* Apc;
    Apc = (PKAPC)ExAllocatePool(NonPagedPool, sizeof(KAPC));
    RtlSecureZeroMemory(Apc, sizeof(KAPC));
 
    KeInitializeApc(Apc, KeGetCurrentThread(), 0, (PKKERNEL_ROUTINE)APCInjectorRoutine, 0, 0, KernelMode, 0);
    KeInsertQueueApc(Apc, 0, 0, IO_NO_INCREMENT);

  AlpcSendMsgtoInjectDll(ALPC_DRIVER_DLL_INJECTENABLE)负责告诉Server需要注入iexplore.exe,CrrentPid/oleaut32.dll.ImageBase传递r3。

 

  Server接收ALPC_DRIVER_DLL_INJECTENABLE处理,首先将创建Map映射ImageBase,DLL被注入后直接可以使用ImageBase(因为我不知道再回调完成之前是否可以使用GetModuleHandle获取得到DLLBaseAddr)。

 

  注入使用常规远程线程注入方式会出现问题,上述已经解释原因。Server创建Share共享内存,发送给Driver可以进行ALPC注入了。

 

 

ALPC_DLL_INJECT/FAILUER调用号一开始是为了r3注入准备的,但现在功能只是激活回调中的等待事件。

1
2
3
4
5
6
UNIVERMSG univermsg = { 0, };
if (nStatus)
    univermsg.ControlId = ALPC_DLL_INJECT_SUCCESS;
else
    univermsg.ControlId = ALPC_DLL_INJECT_FAILUER;
AlpcSendtoClientMsg(*SendtoPort, &univermsg, msgid);

Driver读线程中接收到INJECT.MSG功能号,进行事件唤醒。

 

事件唤醒完成之后,进行apc-DLL注入,如下所示:

 

基本第一阶段完成,第二阶段DLL-hook级检测。

DLL <--> Server:

DLL负责InlineHook,监控相关内存变化漏洞规则验证,触发通知Server处理。

 

 

DLL注入iexplore.exe成功之后,先要获取共享MAP中的ImageBase,其次初始ALPC Port。

 

 

  DLL-ALPC初始化完成后,下面就是CVE-2016-0819检测,可以弹窗阻塞iex进程,OD附加进行调试,主要调试hook代码及CVE检测代码。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NTSTATUS InitVariantChangeTypeExHook(
    PVOID oleauthandle
)
{
    // Get VariantChangeTypeEx Address Save Old Addr or Virtual Mem Copy Opecode to VirMemory
    PVOID VariantChangeTypeExaddr = GetProcAddress((HMODULE)oleauthandle, "VariantChangeTypeEx");
 
    do
    {
        // Check ArgAddr
        if ((0 >= !VariantChangeTypeExaddr) || (0 >= !VariantChangeTypeExHook_Callback))
            break;
 
        // inline Hook
        syscall_VariantChangeTypeEx = (FnVariantChangeTypeExHook)Dll_Hook(VariantChangeTypeExaddr, VariantChangeTypeExHook_Callback);
 
    } while (false);
 
    return 0;
}

  IDA反汇编拷贝前12个字用来Hook。申请内存tramp,拷贝前12byte,紧跟着jmp跳转至原函数地址+12,这样调用原函数时候,tramp就可以直接调用,从而绕过hook的前12byte(Sandboxie代码中有Hook分析函数,寻找合适的Hook点):

 

 

 

Hook完成之后就是对关键点检测,银雁冰文章中给出了具体的检测方案,直接套用如下:

 

 

  如果是漏洞,事件等待,DLL发送ALPC_DLL_MONITOR_CVE告诉Server监视到漏洞,Server将通过匿名管道将PID-CVE数据发送至UI,UI等待用户操作。

1
2
3
4
5
6
7
8
9
10
11
case ALPC_DLL_MONITOR_CVE:
/*++
    通知UI需要处理命中事件,等待UI返回
--*/
{
    MONITORCVEINFO* MonCveInfo = (MONITORCVEINFO*)((BYTE*)lpMem + sizeof(PORT_MESSAGE));
    if (!pipobj)
        break;
    pipobj->PipSendMsg((wchar_t*)MonCveInfo, sizeof(MONITORCVEINFO));
    // pipobj->PipClose();
}

第二阶段主要是注入和CVE监控。

UI <--> Server:

UI负责交互/用户决定(第一版用的DUilib/后删除使用electron)

 

  UI匿名管道接到Server的数据,将数据提取/创建UI界面提示用户有CVE攻击。等待用户放行还是结束,等待30s,选择默认拦截。拦截的话,直接结束浏览器/不调用该Api都可以,还可以还原数据大小,如果允许激活事件继续执行即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
if (g_PipServerPortHandle)
{
    do
    {
        // PeekNamePipe用来预览一个管道中的数据,用来判断管道中是否为空
        if (!PeekNamedPipe(g_PipServerPortHandle, NULL, NULL, &dwRead, &dwAvail, NULL) || dwAvail <= 0)
        {
            break;
        }
        if (ReadFile(g_PipServerPortHandle, Databuffer, BUFSIZE, &dwRead, NULL))
        {                                                      
            if (dwRead != 0)
            {
                // 直接提示处理
            }
        }
    } while (TRUE);
}
return 0;
 
// strcpy(packheader.protocol, "TCP");   
while (true)
{
 
    if (-1 == g_pipui.PipSendMsg(&packheader, sizeof(IPPACKHANDER)))
    {
        printf("[3+]Client Pip inactive!\r\n");
        // 关闭Pip,失败意味着客户端已经关闭匿名管道
        g_pipui.PipClose();
 
        // 重新开启匿名管道,等待客户端上线
        if (-1 == g_pipui.StartServerPip())
        {
            auto error = GetLastError();
            printf("[~]Error: %d\r\n", error);
        }
    }
    Sleep(5000);
}

后记:

  框架实现起来相对简单,后续也会跟着好文学习分享。
  github: https://github.com/TimelifeCzy/HIPS-HIDS_CveMod


第五届安全开发者峰会(SDC 2021)议题征集正式开启!

最后于 2021-4-7 09:18 被一半人生编辑 ,原因:
收藏
点赞8
打赏
分享
最新回复 (7)
雪    币: 7235
活跃值: 活跃值 (6166)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
pureGavin 活跃值 2 2021-4-7 09:20
2
0
感谢分享
雪    币: 2636
活跃值: 活跃值 (437)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
皮皮虾啊 活跃值 2021-4-7 10:29
3
0
看懂了,但又感觉不太懂
雪    币: 9189
活跃值: 活跃值 (3752)
能力值: ( LV12,RANK:252 )
在线值:
发帖
回帖
粉丝
一半人生 活跃值 4 2021-4-7 19:19
4
0
皮皮虾啊 看懂了,但又感觉不太懂[em_86]
雪    币: 57
活跃值: 活跃值 (716)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 活跃值 1 2021-4-7 20:23
5
0
感谢分享!
雪    币: 4277
活跃值: 活跃值 (607)
能力值: ( LV7,RANK:113 )
在线值:
发帖
回帖
粉丝
Adventure 活跃值 2021-4-7 22:51
6
0
可以研究研究怎么从利用手法上防御未知漏洞
雪    币: 397
活跃值: 活跃值 (2904)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
SpyGOD 活跃值 2021-5-8 23:26
7
0
大佬在哪高就
雪    币: 5454
活跃值: 活跃值 (841)
能力值: ( LV2,RANK:150 )
在线值:
发帖
回帖
粉丝
九阳道人 活跃值 3 2021-5-21 21:29
8
0
看不懂,但就是觉得很牛逼
游客
登录 | 注册 方可回帖
返回