来源:gyzy's Blog
本文已经发表在《黑客防线》2007年11月刊。作者及《黑客防线》保留版权,转载请注明原始出处
适合读者:溢出爱好者 前置知识:汇编语言、Windows内核基本原理 从卡巴漏洞管窥内核模式Shellcode的编写 文/图 gyzy[江苏大学信息安全系&EST] 在用户态(Ring3)下存在的种种漏洞在内核态(Ring0)一样存在,并且有些漏洞在用户态下反而比较少见,比较典型的例子就是 DeviceIOControl畸形参数这一类的漏洞。用户态的Exploit技术是内核Exploit技术的基础,假如连用户态下Exploit的一些概念还不是很清楚的话,建议读者先了解一下用户态的Exploit和Shellcode技术,这两者虽然相似,但是还是有很大的不同,在内核态下不像用户态下有丰富的API接口函数可供调用,并且要求也更高,一不小心就会导致蓝屏。网上有很多介绍驱动本地权限提升漏洞的文章,但对于写可用的 Shellcode这个问题都回避了,这次我以卡巴NDIS-TDI Hook驱动(Klick.sys)的畸形参数漏洞为蓝本,详细讲述一下如何编写能在内核下运行的Shellcode,KIS/KAV 6.0.0.0-6.0.0.307均存在此漏洞,测试的时候注意版本。在开始之前,先来介绍一下要用到的背景知识。
背景知识 Kernel&Userland 386 及以上的CPU实现了4个特权级模式(WINDOWS只用到了其中两个),其中特权级0(Ring0)是留给操作系统代码,设备驱动程序代码使用的,它们工作于系统核心态;而特权极3(Ring3)则给普通的用户程序使用,它们工作在用户态。运行于处理器核心态的代码不受任何的限制,可以自由地访问任何有效地址,进行直接端口访问。而运行于用户态的代码则要受到处理器的诸多检查,它们只能访问映射其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址,且只能对任务状态段(TSS)中I/O许可位图(I/O Permission Bitmap)中规定的可访问端口进行直接访问(此时处理器状态和控制标志寄存器EFLAGS中的IOPL通常为0,指明当前可以进行直接I/O的最低特权级别是Ring0)。以上的讨论只限于保护模式操作系统,象DOS这种实模式操作系统则没有这些概念,其中的所有代码都可被看作运行在核心态。处理器模式从Ring3向Ring0的切换发生在控制权转移时,有以下两种情况:访问调用门的长转移指令CALL,访问中断门或陷阱门的INT指令。具体的转移细节由于涉及复杂的保护检查和堆栈切换,不再赘述,请参阅相关资料。现代的操作系统通常使用中断门来提供系统服务,通过执行一条陷入指令来完成模式切换,在 INTEL X86上这条指令是INT,如在WIN9X下是INT30(保护模式回调),在Linux下是INT80,在WINNT/2000下是INT2E。用户模式的服务程序(如系统DLL)通过执行一个INTXX来请求系统服务,然后处理器模式将切换到核心态,工作于核心态的相应的系统代码将服务于此次请求并将结果传给用户程序。
NativeAPI 本机API是除了Win32 API,NT平台开放的另一个接口。本机API它运行在Ring0层,拥有没有限制的操作权限,因此微软出于安全考虑并没有发布支持这种应用的开发方法。 Win32 API中的所有调用最终都转向了ntdll.dll,再由它转发至ntoskrnl.exe。ntdll.dll是本机 API用户模式的终端。真正的接口在ntoskrnl.exe里完成。事实上,内核模式的驱动大部分都在调用这个模块。Ntdll.dll的主要作用就是让内核函数的特定子集可以被用户模式下运行的程序调用。Ntdll.dll通过软件中断int 2Eh进入ntoskrnl.exe,也就是通过中断门切换CPU特权级。
Windows的APC机制 APC是“异步过程调用 (Asyncroneus Procedure Call)”的缩写。从大体上说,Windows的APC机制相当于Linux的Signal机制,实质上是一种对于应用软件(线程)的“软件中断”机制。但是读者将会看到,APC机制至少在形式上与软件中断机制还是有相当的区别,而称之为“异步过程调用”确实更为贴切。APC与系统调用是密切连系在一起的,在这个意义上APC是系统调用界面的一部分。然而APC又与设备驱动有着很密切的关系。例如,ntddk.h中提供“写文件”系统调用 ZwWriteFile()、即NtWriteFile()的调用接口: NTSYSAPI NTSTATUS NTAPI ZwWriteFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length, IN PLARGE_INTEGER ByteOffset OPTIONAL, IN PULONG Key OPTIONAL ); 这里有个参数ApcRoutine,这是一个函数指针。什么时候要用到这个指针呢?原来,文件操作有“同步”和“异步”之分。普通的写文件操作是同步写,启动这种操作的线程在内核进行写文件操作期间被“阻塞(blocked)”而进入“睡眠”,直到设备驱动完成了操作以后才又将该线程“唤醒”而从系统调用返回。但是,如果目标文件是按异步操作打开的,即在通过W32的API函数CreateFile()打开目标文件时把调用参数 dwFlagsAndAttributes设置成FILE_FLAG_OVERLAPPED,那么调用者就不会被阻塞,而是把事情交给内核、不等实际的操作完成就返回了。但是此时要把ApcRoutine设置成指向某个APC函数。这样,当设备驱动完成实际的操作时,就会使调用者线程执行这个APC函数,就像是发生了一次中断。
[1] [2] [3] 下一页 |