CVE漏洞中文网

0DayBank一个专门收集整理全球互联网漏洞的公开发布网站
  1. 首页
  2. 安全资讯
  3. 正文

【技术分享】常见内核后门分析

2017年1月13日 1182点热度 0人点赞 0条评论

前言


科普,科普,科普,重要的事情说三遍,请各位内核牛绕道。

最近哥们给我秀起了内核文件隐藏、32位下给力的Hacker Defender,号称网站管理员无法解决,在t00ls上也看见有童鞋在问相关的问题说用户被隐藏怎么找都找不到等等,经过实战发现确实很多管理员都不知道,在这里科普下希望借助安全客让更多的人知道这一利用手法从而提升安全防护能力和安全意识。

所用工具


xoslab(Easy File Locker)

Hacker defender Rootkit

核心功能介绍及分析:

1. Xoslab:

内核文件隐藏(居然可以骗PChunter!隐藏真实的回调函数地址)

防止文件删除

跟文件相关的所有操作

2. Hacker Defender

隐藏用户

隐藏文件

隐藏进程

客户端可连接服务器上任意开放的端口并且反弹一个shell,该shell拥有最高权限

以上功能过D盾、安全狗等。

3. 小工具的想法

针对这些功能的利用与防御


测试系统:Win7 x64平台

Xoslab的文件隐藏

下载、安装、打开

http://p9.qhimg.com/t01fe4246ea4598713f.png

安装完成后添加要隐藏的文件,然后.......就没有什么然后了!!! 没了呗。

http://p9.qhimg.com/t01c409138ea7c53940.png

不要惊慌!上图不是dir溢出!!确实没了,问题是这是一个软件,管理员也会打开这个软件,那怎么办呢。其实很简单。把自身的驱动和ini配置文件也添加成隐藏文件并且不可删除后把程序目录

该删的删干净留一个驱动即可。

http://p5.qhimg.com/t011bfa4a9c52b2af39.png

通过webshell.pub 或者D盾扫描或者那只什么狗来着的软件,基本都是扫不出来的。如果你遇到过类似的问题.那好吧,下面我带你一起来分析分析这件事情。

实现原理分析: 在网上看到有童鞋发帖说删除什么ini什么的,这些都是治标不治本的办法万一人家自己写一个驱动隐藏呢,这时候又咋处理?所以原理非常重要。我推荐求其道而弃其术。

为了证明我不是娱乐圈。咳咳,今天也不是给大家展示怎么利用工具的,下面来分析下这件事情的实现原理。

CPU的权限级别

驱动的加载

Minifilter

干掉回调

对PChunter隐藏真正的回调

1. 首先我们得从CPU架构说起

http://p3.qhimg.com/t01fd8d5d3da629c515.png

这张图呢描述的是CPU的分级情况,我们的操作系统(kernel)运行在Ring0级别,我们看到的和使用的实际上是在ring3层。了解这个原理以后我们就可以知道我们用户看到的并非是真实的。

2. 驱动的加载

想要获取驱动权限只需要在一个驱动即可,下面来简单说下:

2.1 .x86下驱动加载

此平台下,你就是爸爸,知道这个就行了,使用一种古老而神秘的技术SSDT Hook 都能使你叱咤风云!

2.2. x64下驱动加载

1、Win7问世以后微软号称是最安全的操作系统,因为有了PatchGuard的保护一旦你对系统底层相关代码进行修改,微软会对你使用大招BSOD大法让你生活不能自理,但是也可以通过bootkit技术等等废掉他的PG,不是今天讨论的主题。

2、x64下所有驱动加载必须签名,不过好像并没有什么软用,据大肉鸡老师说现在的rootkit木马一分钟一个签名,sao的飞起!

3、HIPS对签名加载的拦截,这个嘛,同上,或者直接允许。反正你都有administrator权限。

总结下,64位下加载驱动并不是什么难事。

3. File System Minifilter Drivers(Minifilter)技术

在x64下面各种HOOK变得不是那么现实,但是可以通过微软提供的各种回调达到对文件的访问控制需求。

http://p2.qhimg.com/t019091ea26ad3b1c01.png

图必不可少,看得懂的看看不懂的自行百度。

其实也就是IRP发下来后经过一些分层过滤驱动,在这个地方有一大堆回调会挨个执行.

http://p4.qhimg.com/t01a87d4352c65f2a92.png

文件查找过程为通过FindNextFile下发后从KiFastCallEntry进入内核,IRP将在内核里进行传递。其中IPR_MJ_DIRECTORY_CONTROL就是对目录遍历的IRP,再次进行拦截即可对文件进行隐藏。

隐藏的一些代码细节有兴趣的童鞋可参考https://www.oschina.net/code/snippet_614253_43729 进行查看。

好了,我们要干掉他。

http://p8.qhimg.com/t01a3218568156df2a6.png

通过PChunter的移除过滤器貌似好像大概可以达到效果,然而现实会给你一记响亮的耳光.因为程序自开线程检测该回调是否被移除如果被移除会再次添加,而且这小婊砸还回调了filterUnload.

这怎么破呢,其实方法很多,大牛告诉过我无数个方法,我觉得最实用的就是直接retn掉函数的第一句。

代码流程

遍历minifilter回调

拿到IPR_MJ_DIRECTORY_CONTROL相关的回调地址

关闭CR0保护

回调函数内存地址开头处写入C3(会标retn)指令

关闭CR0保护

关闭后开启D盾或者安全狗或者等等扫描工具开扫把,扫完记得恢复被改写的回调,免得出现意想不到的问题。

http://p0.qhimg.com/t01ed16f809c26ca7e4.png

跟360这个函数差不多即可。

有人要喊了,Talk is cheap. Show me the code!!!

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
KIRQL WPOFFx64()
{
KIRQL irql = KeRaiseIrqlToDpcLevel();
UINT64 cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
}
void WPONx64(KIRQL irql)
{
UINT64 cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
}
//以上代码是关闭64位下CR0保护的两个函数
#include <intrin.h>
ULONG FltFilterOperationsOffset=0x188; //WIN7 OFFSET of fltmgr!_FLT_FILTER->PFLT_OPERATION_REGISTRATION
typedef struct _FLT_OPERATION_REGISTRATION
{
UCHARMajorFunction;
ULONGFlags;
PVOIDPreOperation;
PVOIDPostOperation;
PVOIDReserved1;
} FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION;
typedef struct _FLT_FILTER
{
UCHAR buffer[1024];
} FLT_FILTER, *PFLT_FILTER;
NTSTATUS
__fastcall
FltEnumerateFilters
(
    PFLT_FILTER *FilterList,
    ULONG FilterListSize,
    PULONG NumberFiltersReturned
);
NTSTATUS
__fastcall
FltObjectDereference
(
    PVOID FltObject
);
KIRQL WPOFFx64()
{
KIRQL irql = KeRaiseIrqlToDpcLevel();
UINT64 cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
return irql;
}
void WPONx64(KIRQL irql)
{
UINT64 cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);
}
ULONG EnumMiniFilter()
{
longntStatus;
ULONGuNumber;
PVOIDpBuffer = NULL;
ULONGuIndex = 0, DrvCount = 0;
PVOIDpCallBacks;
PVOID   pFilter=NULL;
PFLT_OPERATION_REGISTRATION pNode;
do
{
if(pBuffer != NULL)
{
ExFreePool(pBuffer);
pBuffer = NULL;
}
ntStatus = FltEnumerateFilters(NULL,  0, &uNumber);
if(ntStatus != STATUS_BUFFER_TOO_SMALL)
break;
pBuffer = ExAllocatePoolWithTag(NonPagedPool, sizeof(PFLT_FILTER) * uNumber, 'mnft');
if(pBuffer == NULL)
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
ntStatus = FltEnumerateFilters(pBuffer, uNumber, &uNumber);
}
while (ntStatus == STATUS_BUFFER_TOO_SMALL);
if(! NT_SUCCESS(ntStatus))
{
if(pBuffer != NULL)
ExFreePool(pBuffer);
return 0;
}
DbgPrint("MiniFilter Count: %ld\n",uNumber);
DbgPrint("------\n");
__try
{
KIRQL irql = WPOFFx64();
                //我举个栗子....通过这种方式可以写,在下面自己判断吧。我这里做测试随便写写。
PVOID *posFun = 0xFFFFF88005731704;
char code[3] = {0x33,0xC0,0xC3};
RtlCopyMemory(posFun, code, 3);
posFun = 0xFFFFF880057313F8;
RtlCopyMemory(posFun, code, 3);
WPONx64(irql);
while(DrvCount<uNumber)
{
pFilter = (PVOID)(*(PULONG64)((PUCHAR)pBuffer + DrvCount * 8));
pCallBacks = (PVOID)((PUCHAR)pFilter + FltFilterOperationsOffset);
pNode = (PFLT_OPERATION_REGISTRATION)(*(PULONG64)pCallBacks);
__try
{
while(pNode->MajorFunction != 0x80)//IRP_MJ_OPERATION_END
{
if (pNode->MajorFunction<28)//MajorFunction id is 0~27
{
        //一会解释神奇的现象
if (pNode->PreOperation == 0xFFFFF88005731FC0){
pNode->PreOperation = 0xFFFFF88000DB8980;
DbgPrint("Object=%p\tPreFunc=%p\tPostFunc=%p\tIRP=%X\n",
pFilter,
pNode->PreOperation,
pNode->PostOperation,
pNode->MajorFunction
);
}
}
pNode++;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
FltObjectDereference(pFilter);
DbgPrint("[EnumMiniFilter]EXCEPTION_EXECUTE_HANDLER: pNode->MajorFunction\n");
ntStatus = GetExceptionCode();
ExFreePool(pBuffer);
return uIndex;
}
DrvCount++;
FltObjectDereference(pFilter);
DbgPrint("------\n");
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
FltObjectDereference(pFilter);
DbgPrint("[EnumMiniFilter]EXCEPTION_EXECUTE_HANDLER\n");
ntStatus = GetExceptionCode();
ExFreePool(pBuffer);
return uIndex;
}
if(pBuffer != NULL)
{
ExFreePool(pBuffer);
ntStatus=STATUS_SUCCESS;
}
return uIndex;
}
//网上找的,朋友说是TA老师的源码,哈哈。反正别给老夫提什么内核框架,反正就是一把梭,一把梭。

http://p6.qhimg.com/t014d84b8cb0a132ee9.png

上面有一个神奇的现象代码中通过FltEnumerateFilters枚举minifilter的函数地址拿到这块内存后修改pNode指针的值或者删除,惊奇的发现PChunter列表的值也发生变化了,那讲道理的说我删除这个值,PChunter应该也没有了,估计这一块内存共用的哇,PChunter也采用相同方式来枚举。有图有真相。

http://p4.qhimg.com/t012eb699d2738fd4f6.png

这里留给小伙伴们去扩展吧。

至此第一个工具分析完成了。

Hacker Defender 分析

由于时间关系后门没时间搞环境测试了,简单说下吧!驱动加载后把自身进程提升为system进程,R3无权限访问。然后通过R3进程对系统进行HOOK,所有功能基于R3 HOOK完成。

开始分析时各种看R0的东西,发现一点都没对R0做修改和处理,心想这尼玛,05年的东西这么强大? 最后.....

http://p1.qhimg.com/t01ad840a7c33f92d17.png

在这个界面检测出了700多钩子直接右键全部恢复,hacker defender所有功能消失,又是一把梭解决问题!

演示视频


老外有视频我就不造轮子了,这个rootkit太低级了!不过对于大多数网管还是有效的。

总结


由于工作的关系时间并不充裕暂且先写到这,本来打算写一个小工具造福不懂技术的网管们,让他们运行程序以后系统就变得跟当初一样了,安全狗、D盾等软件也会发挥作用。

想法是美好的,然而工作太忙要写大量的Javascript、HTML5哎,无奈.加班什么的。希望看了本文的小伙伴能写出这样的小工具造福一小部分人,为网络安全添砖加瓦!

BTW:求教minifilter正确的遍历姿势!!!THX

本文由 安全客 原创发布,如需转载请注明来源及本文地址。
本文地址:http://bobao.360.cn/learning/detail/3403.html0day

标签: 暂无
最后更新:2017年1月13日

小助手

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论

COPYRIGHT © 2024 www.pdr.cn CVE漏洞中文网. ALL RIGHTS RESERVED.

鲁ICP备2022031030号

联系邮箱:wpbgssyubnmsxxxkkk@proton.me