Rootkit技术
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Rootkit技术
rootkit的主要分类
早期的rootkit主要为应用级rootkit,应用级rootkit主要通过替换login、ps、ls、netstat 等系统工具,或修改.rhosts等系统配置文件等实现隐藏及后门;硬件级rootkit主要指bios rootkit,可以在系统加载前获得控制权,通过向磁盘中写入文件,再由引导程序加载该文件重新获得控制权,也可以采用虚拟机技术,使整个操作系统运行在rootkit掌握之中;目前最常见的rootkit是内核级rootkit。
内核级rootkit又可分为lkm rootkit、非lkm rootkit。
lkm rootkit主要基于lkm技术,通过系统提供的接口加载到内核空间,成为内核的一部分,进而通过hook系统调用等技术实现隐藏、后门功能。
非lkm rootkit主要是指在系统不支持lkm机制时修改内核的一种方法,主要通过/dev/mem、/dev/kmem设备直接操作内存,从而对内核进行修改。
非lkm rootkit要实现对内核的修改,首先需要获得内核空间的内存,因此需要调用kmalloc分配内存,而kmalloc是内核空间的调用,无法在用户空间直接调用该函数,因此想到了通过int 0x80调用该函数的方法。
先选择一个不常见的系统调用号,在sys_call_table 中找到该项,通过写/dev/mem直接将其修改为kmalloc函数的地址,这样当我们在用户空间调用该系统调用时,就能通过int 0x80进入内核空间,执行kmalloc函数分配内存,并将分配好的内存地址由eax寄存器返回,从而我们得到了一块属于内核地址空间的内存,接着将要hack的函数写入该内存,并再次修改系统调用表,就能实现hook系统调用的功能。
rootkit的常见功能
隐藏文件:通过strace ls可以发现ls命令其实是通过sys_getdents64获得文件目录的,因此可以通过修改sys_getdents64系统调用或者更底层的readdir实现隐藏文件及目录,还有对ext2文件系统直接进行修改的方法,不过实现起来不够方便,也有一些具体的限制。
隐藏进程:隐藏进程的方法和隐藏文件类似,ps命令是通过读取/proc文件系统下的进程目录获得进程信息的,只要能够隐藏/proc文件系统下的进程目录就可以达到隐藏进程的效果,即hook sys_getdents64和readdir等。
隐藏连接:netstat命令是通过读取/proc文件系统下的net/tcp和net/udp文件获得当前连接信息,因此可以通过hook sys_read调用实现隐藏连接,也可以修改tcp4_seq_show和udp4_seq_show等函数实现。
隐藏模块:lsmod命令主要是通过sys_query_module系统调用获得模块信息,可以通过hook sys_query_module系统调用隐藏模块,也可以通过将模块从内核模块链表中摘除从而达到隐藏效果。
嗅探工具:嗅探工具可以通过libpcap库直接访问链路层,截获数据包,也可以通过linux 的netfilter框架在IP层的hook点上截获数据包。
嗅探器要获得网络上的其他数据包需要将网卡设置为混杂模式,这是通过ioctl系统调用的SIOCSIFFLAGS命令实现的,查看网卡的当前模式是通过SIOCGIFFLAGS命令,因此可以通过hook sys_ioctl隐藏网卡的混杂模式密码记录:密码记录可以通过hook sys_read系统调用实现,比如通过判断当前运行的进程名或者当前终端是否关闭回显,可以获取用户的输入密码。
hook sys_read还可以实现login后门等其它功能。
日志擦除:传统的unix日志主要在/var/log/messages,/var/log/lastlog,/var/run/utmp,/var /log/wtmp下,可以通过编写相应的工具对日志文件进行修改,还可以将HISTFILE等环境变设为/dev/null隐藏用户的一些操作信息。
内核后门:可以是本地的提权后门和网络的监听后门,本地的提权可以通过对内核模块发送定制命令实现,网络内核后门可以在IP层对进入主机的数据包进行监听,发现匹配的指定数据包后立刻启动回连进程。
rootkit的主要技术:
lkm注射、模块摘除、拦截中断(0x80、0x01)、劫持系统调用、运行时补丁、inline hook、端口反弹……
lkm注射:也是一种隐藏内核模块的方法,通过感染系统的lkm,在不影响原有功能的情况下将rootkit模块链接到系统lkm中,在模块运行时获得控制权,初始化后调用系统lkm 的初始化函数,lkm注射涉及到elf文件格式与模块加载机制。
模块摘除:主要是指将模块从模块链表中摘除从而隐藏模块的方法,最新加载的模块总是在模块链表的表头,因此可以在加载完rootkit模块后再加载一个清除模块将rootkit模块信息从链表中删除,再退出清除模块,新版本内核中也可以通过判断模块信息后直接list_del。
拦截中断:主要通过sidt指令获得中断调用表的地址,进而获取中断处理程序的入口地址,修改对应的中断处理程序,如int 0x80,int 0x1等。
其中拦截int 0x1是较新的技术,主要利用系统的调试机制,通过设置DR寄存器在要拦截的内存地址上下断点,从而在执行到指定指令时转入0x1中断的处理程序,通过修改0x1中断的处理程序即可实现想要的功能劫持系统调用:和拦截中断类似,但主要是对系统调用表进行修改,可以直接替换原系统调用表,也可以修改系统调用表的入口地址。
在2.4内核之前,内核的系统调用表地址是导出的,因此可以直接对其进行修改。
但在2.6内核之后,系统调用表的地址已经不再导出,需要对0x80中断处理程序进行分析从而获取系统调用表的地址。
运行时补丁:字符设备驱动程序和块设备驱动程序在加载时都会向系统注册一个Struct file_operations结构实现指定的read、write等操作,文件系统也是如此,通过修改文件系统的file_operations结构,可以实现新的read、write操作等。
inline hook:主要是指对内存中的内核函数直接修改,而不影响原先的功能,可以采用跳转的办法,也可以修改对下层函数的call offset实现。
端口反弹:主要是为了更好的突破防火墙的限制,可以在客户端上监听80端口,而在服务器端通过对客户端的80端口进行回连,伪装成一个访问web服务的正常进程从而突破防火墙的限制。
RootKit 实现步骤
1)插入无效的数据。
无效的数据可以被插入到任何的网络流中,同样可以插入一些错误数据到固定的存储系统中,可能是敏锐的时间信息等,这样可能会破坏系统的备份。
这样将会侵犯系统的可靠性和完整性。
2)修改进入的ICMP 数据报。
使用ICMP 作为隐藏的渠道,补丁可以读取进入到核心的ICMP 数据报内部的命令。
3)修改进入的以太网数据报。
在不使用任何驱动组件的情况下担当嗅探器的角色。
如果它已经修改了以太网,那么同样可以发
送/接收与外网的数据报。
它还可以嗅探秘密的关键字。
4)修改已经存在的动态链接库,比如wininet.dll,追踪进入的数据。
5)修改入侵检测系统。
它可以修改如Tripwire 或RealSecure 等程序来破坏它们的完整性。
6)修改审核系统,如系统事件日志来忽略一些事件日志消息。
获得Ring 0 特权
在Windows NT 下,普通用户没有直接写物理内存的权限"因此不能直接修改GDT 或IDT。
要获取Ring 0 权限,首先要求用户有写物理内存的权限,然后修改GDT,构造一个调用门,最后访问该调用门,执行Ring 0 代码[4]。
实现思路如下:
1)使用户可读写\Device\PhysicalMemory 对象GDT 位于内核区域,普通Ring 3 级应用程序无权直接访问该内存区域,Win-dows NT 提供了一个名为PhysicalMemory 的Section 对象,通过该对象可以读写物理内存。
通过分析,正常情况下只有SYSTEM 用户才有对该对象的读写权限,administrator 用户只有读权限。
要使特定普通用户有写该对象的权限,需要以administrator 用户身份增加另一个访问控制列表(ACL)到\Device\PhysicalMemory 对象:首先打开一个\Device\PhysicalMemory 对象的句柄并获得其安全描述符(GetSecurityInfo),接着在当前ACL 中增加读写权限(SetEntriesInAcl),最后更新安全描述符(Set-SecurityInfo)。
2)构造调用门:根据ntddk.h 中的定义,当selector=8 时,该调用门指向的处理代码以Ring 0 权限运行。
函数地址低16 位和函数地址高16 位分别为Ring 0 代码的地址的高低16 位。
Dpl 位为调用该门的代码的最低权限,应设为3,present 应设为1,表明该调用门可用。
3)将调用门写入GDT 中:从GDT 尾部开始循环查找GDT 中每一项,当找到一个不存在的描述符(paresent=0)时停止,并将该描述符的各项修改为前面构造的调用门结构。
4)访问调用门,运行Ring 0 代码:常规访问该调用门的方法不可行"因为当前应用程序运行在用户态,即Ring 3 级必须通过远调用far call 来访问该调用门。
3.3 核心代码实现
首先对函数SeAccessCheck()进行逆向分析,通过使用IDA 对函数进行了反汇编,部分代码如下所示。
8019A20C push [ebp+arg_24]
8019A20F push [ebp+arg_14]
8019A212 push edi
8019A213 push [ebp+arg_1C]
8019A216 push [ebp+arg_10]
8019A219 push [ebp+arg_18]
8019A21C push ebx
8019A21D push dword ptr [esi]
8019A21F push dword ptr [esi+8]
8019A222 push [ebp+arg_0]
8019A225 call sub_80199836
8019A22A cmp [ebp+arg_8], 0
8019A22E mov bl, al
8019A230 jnz short loc_8019A238
8019A232 push esi
8019A233 call SeUnlockSubjectContext
8019A238 mov al, bl
8019A23A pop edi
8019A23B pop esi
8019A23C pop ebx
8019A23D pop ebp
8019A23E retn 28h
8019A23E SeAccessCheck endp
子程序被SeAccessCheck 所调用。
大多数的工作都是在此完成,因此对该函数进行了修改。
80199AD3 mov [ebp+var_C], 0
80199ADA add esi, 8
80199ADD cmp [ebp+var_10], 0
80199AE1 jz loc_80199B79
80199AE7 test edi, edi ;
80199AE9 jz loc_80199B79
80199AEF test byte ptr [esi+1], 8
80199AF3 jnz short loc_80199B64
80199AF5 mov al, [esi]
80199AF7 test al, al
80199AF9 jnz short loc_80199B14
80199AFB lea eax, [esi+8]
80199AFE push eax
80199AFF push [ebp+var_8]
80199B02 call sub_801997C2
80199B07 test al, al
80199B09 jz short loc_80199B64
80199B0B mov eax, [esi+4]
80199B0E not eax
80199B10 and edi, eax
80199B12 jmp short loc_80199B64。