【51CTO.com 独家翻译】在先前的文章中,我们深入探讨了如何通过Windows进程处理“特殊”热键,并认为主要是通过Win32k.sys中和WINLOGON.EXE进程。这一次,我们将努力使用由eEye公司改编的被称为SysRq的BootRootKit(引导层RootKit)。
更多的信息在eEye公司的bootroot的工程中,演示的相关幻灯片请到http://www.eeye.com/html/resources/downloads/other/index.html.下载。
SysRq(就是系统需求,它就像键盘中的SysRq按键)包含一个Kiobyte的引导代码,加固在Windows自身内核中,一旦有本地调用,它就会先注册一个特殊热键(control +Shift+SysRq),一旦激活就能打开获得最高级系统权限的cmd窗口。尽管这是非常有用的,同时也存在有争议的系统管理员的工具,它或许会被用在非法的用途中,所以我们目前尚未把它开源。然而,该项目是在一个可行性技术上进行的,如果我们来讨论它的技术细节和执行时还是很有趣并且很有意义的,这里我们建议读者要有熟悉Windows内核的水平和用户模式编程的相关知识。
SysRq 概况
SysRq的要求很简单:当热键被激活后,该引导扇区代码才会在系统命令提示符下运行。因为引导扇区代码,会一直驻守在window内核中,但cmd.exe命令是用户系统最容易的也是最经常的调用。由于很多热键处理由Winlogon完成,因为它主要作为系统对开始其他交互式过程处理(例如任务管理),例如用户模式代码就是最合适的主机进程。
正如我们前面提到,在FFDF0000h / 7FFE0000h处的用户共享数据是内核空间和用户空间的桥接,但是实际上,我们通过使用主机的用户代码来写入到本地winlogon进程空间的地址里面。
我们使用这个实用技术来调用这个代码,然后我们一直在这个项目中进行不断的修改。在执行syerq1.0版本的时候,winxp及更高的版本容易造成蓝屏,我们需要一个更好的解决方法。然而,这是要进行多次失败的尝试,这些失败的尝试看起来还是能给我们带来一定的受益,它囊括了全部的技术细节。
#p#SysRq1.0版本只能工作在Windows2000系统上
SysRq的第一个阶段与BootRootKit的相同,基本三个步骤:
1、安装一个在Int13h(Bios 磁盘服务)的钩子;
2、在OSLOADER补丁处并加载其程序;
3、Windows内核内存使用时要使用一次补丁。
这个问题的第三个步骤是我们只能加载可靠的内存寻址模块。BootRootKit覆盖在DOS中的的MZ和PE头NDIS.SYS的代码之间。因为它要用少于40h的字节的代码植入到内核中,但是sysrq持续更新的代码要比这远远大的多,所以我们必须做一些调整。
SysRq v1.0 也采用了相同的技术,在NTOSKRNL.EXE的DOS 代码里安装一个小的PspInitializeSystemDll 钩函数,但是这只是第一步。临时的钩函数代码通常映射在常规存储器的页里,并包含在SysRq的虚拟地址0处,通过修改固定的第一个页表的的C00000000h虚拟地址而进入。将SysRq复制到用户共享数据未用的FFDF0800h处,然后我们接着用钩函数替换原先的函数,以便我们更好的执行我们的代码。
我们的目标是要取代NTDLL.DLL的地址 !
LdrInitializeThunk,是我们已知的内核函数,用我们已知的初始化代码,它在用户态的每个进程里的用户共享数据里面,在7FFE0000h处NTOSKRNL初始化代码的通过我们叫做PspLookupsystemdllEntrypoint函数并且使用"LdrInitializeThunk"的字符串获得这个地址。我们发现这个call指令通过先搜索这个字符串,然后这个钩子函数再调用它自身而不是直接调用PspLookupsystemDllEntryPoint函数,我们只是想取代LdrInitializeThunk函数。(其他的NTDLL出口也使用这个函数,包括KiUserApcDispatcher 和KiUserExceptionDispatcher)。
钩子函数通过原函数调用,然后用一个指向我们在用户代码共享处的指针替换掉LdrInitializeThunk函数指针。当然,我们必须保持原来的指针能正常使用,以便完成我们自定义的初始化代码。
这个代码就是是SysRq的核心。每一个新线程都要先通过异步过程调用,这是非常完美的设计。因为我们想要我们的代码执行在每一个进程中,但是只能有一个winlogon生效,一个最简单的方法就是不让winlogon专门试图访问Windows内核。然而我们只是希望我们的代码运行在进程初始化,而不是在每一个线程的初始化,但是我们不能在用户模式下对这个挂钩。一个简单的方法就是检查加载数据是否在[PEB+0Ch]处,
如果为非null,那说明这个进程已经初始化,我们的代码不再做任何的操作。
当到达进程初始化之前,我们的代码继续搜索这个应用映像的unicode字符串的“SAS 窗口类,如果我们找到了它,那么接着在RegisterClassw调用中找到了这个字符串后并创建这个类。那时,我们找到SASWndProc,这个窗口存放类-通过检索在这个vWNDCLASS.LpfnWndProc之前的存储地址记录,然后我们钩住这个典型函数的入口点的修改。
我们的SASWndProc函数构造的尽可能的简单-我们假定能替换PUSH EBP/MOV EBP/SUB ESP (值小于等于800h)这些指令,通过检查MOV EDI,EDI"来检查最近新增加的Service Packs例如Windows XP的SP2.为了安装这个钩子,我们用这个代码写入正在使用的ZwProtecVirtualMemory函数中,然后在植入到相对应的prolog函数,并给SAS window发一条信息。
该钩子函数的行为就像一个非常基本的SASWndProc函数,因为我们用USER32.DLL来注册我们的热键。RegistrHotKey接收到WM_CREATE 信息,并处理这个热键,当WM_HOTKEY 信息传达到‘idHotKey'我们这个指定的值。显而易见,当我们检查RegisterHotKey,我们需要USER32.DLL的基址,但是它可以通过在PEB+2Ch的USER32DispatchRoutine指针来获得标记,直到让我们发现MZ和PE头。
当按Ctrl + SHIFT +SysRq热键被按下时,我们的WM_HOTKEY创建了一个“cmd”的进程并返回到SASWndProc的调用,并完全中断原来函数调用。实际上,只有少数的api调用涉及到这样的工作,更多的获得了窗口驻留和当前桌面,因此我们可以得到一个命令shell甚至能锁定或抢先登陆系统。
工作顺序如下:
GetProcessWindowstation() GetUserObjectInformationA(hWinSta, UOI_NAME) OpenInputDesktop(0, FALSE, 0) GetUserObjectInformationA(hDesktop, UOI_NAME) CloseDesktop() |
一旦我们把构造好的“WindowsStation\Desktop”字符串填充到'lpDesktop'区域里一个启动信息结构中,我们把这个过程叫做
CreateProcessA,并用CloseHand来对它们进行清理,那么我们就已经大功告成。用户现在就会拥有一个系统权限下的command shell。
正如前面文章提到的,这个方法不能应用在Windowsxp或更高版本的WIN操作系统上。当激活Windows产品以后,它就会验证WINLOGON.EXE在内存中的映像,一旦发现被篡改,它就会自动中断该进程,造成蓝屏而最终导致系统崩溃。虽然我们试图用其他的代码来攻击或者更改,以及对其他的目标下钩子,使得我们能够再下钩子以后,用代码骗过验证,这一操作之后,让系统认为这与之前的系统并没有任何变化,并没有对系统中的目标下了钩子。奔着这个目的我们一直使用其他的方法来完成,最终我们发布了SysRq2.0版本。
#p#SysRq2.0版本开始支持Windows2000,xp,以及2003操作系统
同BootRootKit(引导型rootkit)一样,SysRq2.0版本也安装在了INT13h这个硬指令中断上,对启动时的OSLOADER.EXE进行补丁操作,并对其下了钩子,最终达到直接渗透到Windows内核。像SysRq1.0一样,它也是对Winlogon 的SASWndProc进行替换操作,但现在的这个版本的技术实现与前一版本有着很大的区别。我们不能改变WINLOGON.EXE映像,但是我们可以通过对窗口类的注册机制进行攻击,从而来对它进行改变。
USER32.DLL的RegisterClassW 是RegisterClassExWOWW函数的一个封装,经过一番探索后,我们在WIN32K.SYS中发现了一个具有同样作用并且函数名也一样的系统调用的user32函数,它就是NtUserRegisterClassExWOW函数。由于SysRq工作在核心态,可以直接对WIN32K.SYS下钩子,但是我们在执行内核代码时,不能对已经初始化的NTOSKRNL进行操作,我们却可以从进程中SMSS.EXE加载的WIN32K.SYS进行操作。
这是比其他解决方法更有效的办法。当WIN32K.SYS初始化时,它必须调用KeAddSystemServieceTable来为用户以及GDI系统程序提供注册。通过对这个函数进行下钩子操作,并更快更好的把它加载到执行的WIN32K.SYS中,同时我们使用免费的系统服务表(_W32pServiceTable)和相应的参数表 (_W32pArgumentTable)。像SysRq1.0版本一样,使用这个钩子来对NTOSKRNL的头的DOS代码处来执行一个小stub,然后我们拷贝这个重置的SysRq代码到用户共享数据里面来进行恢复并执行该代码。
我们对KeAddSystemServiceTable下钩子主要是通过挂钩NtUserRegisterClassExWOW这个系统调用函数来实现的,因为那个函数没有输出,因此需要一点更复杂的操作。针对这个问题,我们的答案是通过对在W32pServiceTable的代码进行逐个函数扫描,直到找到NtUserRegisterHotkey,这是唯一的选择,因为“FFFF7FF0H”那个最容易辨认的签名是用来检查禁止修改的标记。我们知道NtUserRegisterHotkey总是被应用到四个程序中,为了安全起见,我们还需要让W32pServiceTable这个函数入口点能被写入10h字节的内容,。
我们都知道这个函数位置在系统服务表里,我们对NtUserRegisterClassExWOW函数从前到后逐个字母进行比对搜索。这是找打它的最好的办法,或者取6或者7(18h或者1Ch字节)来决定Windows版本。一旦发现这个函数,我们就替换掉WIN32K系统表的入口并用一个指针指到我们自己的钩子函数处,最终替换掉原先这个函数的指针,并从已恢复运行的KeAddSystemServiceTable中脱掉钩子。
该NtUserRegisterClassExWOW钩子函数并不复杂,它只用来比较这个类的名称(都2个参数与一个unicode字符串指针)和SAS Windows class来完成匹配,它用我们在用户映射的用户共享数据中的那个更换过的SASWndProc代码来替换掉‘lpfnWndProc领域中的WNDCLASSEXW结构,替换完后再把在PEB特定位置的原始函数指针进行存储操作。我们要进行输入验证,毕竟我们不希望发生内核漏洞。
最后,我们最终的目的是获得系统的shell,获得最高的系统权限。
SysRq 2.0有一个明显的缺陷,它只能在支持终端服务系统上的命令行回话中工作——这个原因是由于每个回话都有自己Win32k全局数据内存的拷贝,包括存储_W32pServiceTable的.data区域,可能这就是终端服务能够使用Win32k.sys访问全局变量的原因(感谢eEye工程师Robert Ross指出这一点)SysRq的未来版本中,可能会通过直接钩挂NtUserRegisterClassExWOW代码来克服这一缺陷,这个改进将可以影响到所有的终端服务回话。
#p#总结:
在这篇文章里,我们已经讨论我们怎样实现SysRq,eEye按照需求提供一个基于BootRoot的实用程序系统来实现获得SYSTEM shell的技术细节。为了了解Windows hotkeys是如何工作的,以便使得在任何台式机上都能支持WinLogon 系统进程来完成热键注册,我们进行了相关逆向工程操作。
词汇注释:
1、WinLogon登录管理
Winlogon进程负责管理登录相关的安全性工作,它负责处理用户的登录与注销、启动用户shell、输入口令更改口令、锁定与解锁工作站等。Winlogon进程必须保证其与安全相关操作对其他进程不可见,以免其他进程取得登录密码。
系统初始化时,启动用户程序之前,Winlogon进行特定工作已保障以上的需求。Winlogon进程将创建并打开一个Window Stations,然后设置一个访问控制人口(ACE),该ACE中只包含Winlogon进程的SID,这样就只有Winlogon进程才能访问该Window Stations 。然后winlogon创建桌面,设置其中的winlogon桌面,只有winlogon可以访问,其他进程不能访问该桌面的任何数据和代码;利用这一特性保护口令、锁定桌面等操作的安全。winlogon还会注册安全注意序列(SAS - secure attention sequence)的热键,任何时候按下SAS热键(缺省为ctrl+alt+del),将调用Winlogon,切换到安全桌面,从而使密码捕捉程序不能接收登录密码和更改密码等安全活动。
2、涉及函数解释
NtQueryDirectoryFile
在WINNT里在某些目录中寻找某个文件的方法是枚举它里面所有的文件和它的子目录下的所有文件。文件的枚举是使用NtQueryDirectoryFile函数。
KeAddSystemServiceTable
函数KeAddSystemServiceTable允许Win32.sys和其他设备驱动程序添加系统服务表。除了Win32k.sys服务表外,使用KeAddSystemServiceTable添加的服务表会被同时复制到 KeServiceDescriptorTable和KeServiceDescriptorTableShadow中去。
【51CTO.COM 独家翻译,转载请注明出处及译者!】
【编辑推荐】
原文:Source: Derek Soeder,Software Engineer,http://www.eeye.com/html/resources/newsletters/vice/VI20051104.html
【编辑推荐】