James Forshaw发现的DiagHub DLL loading技术已经非常有名了。每当你在windows或一些第三方软件中发现SYSTEM权限的任意文件写漏洞时,你就可以用这一招来造成任意代码执行,而且不用重启。不幸的是这种方法在Windows 10 build 1903年被微软禁止。Andrea Pierini在Twitter上简单地提到了这一点。在这里,我想和大家分享一下我在最新版本的Windows上寻找DLL劫持弱点时发现的另一种加载dll的方法。
在本文的开头,我想澄清一下:
这不算是一个漏洞。正如我们所看到的,为了能够使用这个技巧,必须先将一个专门制作的DLL植入C:WindowsSystem32文件夹,当然只有 “SYSTEM” 权限的用户才能做到这一点。
作为出发点,我决定寻找一些简单的例子,例如在以NT AUTHORITY/System运行的服务中的DLL劫持。我的想法是监控那些可以被普通用户启动或至少是 “影响 “的服务。为此,我做了一个非常简单的PowerShell脚本,每秒钟检查一个服务是否被启动或停止。
在Windows操作系统的后台,运行Process Monitor来记录文件系统操作。我简单地配置了一个过滤器,只显示涉及*.dll文件的操作,如果找不到的话就返回NAME NOT FOUND错误代码。然后,我试着在没有管理员权限的情况下,一次一次地启动所有能启动的服务。不幸的是,我没有用这个方法发现任何东西。不过我发现了以下内容。
当 “Process Monitor “在后台运行时,它捕捉到一些定期打开 “windowscoredeviceinfo.dll “文件的操作。频率不一,可能每小时发生一次,也可能每30分钟发生一次。事件的属性显示了该进程的命令行。通过查看事件的属性能够发现这个操作是由命令行”C:WINDOWS/System32/svchost.exe -k utcsvc -p”造成的。
知道了这一点,就可以很容易地通过以下PowerShell命令找到相应的服务,例如。在这种情况下,可以用DiagTrack命令。
注:我也可以使用进程的PID,例如在任务管理器中查找它。这种方法的缺点是,你不知道它什么时候启动,在你检查的时候,进程可能并不在运行。
“Process Monitor “中的事件属性显示了一些关于DLL如何被加载的信息。Stack选项卡显示了导致这次操作的调用列表。在这里,我们可以看到,初始调用是从diagtrack.dll进行的。DiagTrack服务加载了FlightSettings.dll文件,它又使用了dcntel.dll中的GetCensusRegistryLocation()方法,最后,使用标准的LoadLibraryEx()WinApi调用加载了windowscoredeviceinfo.dll。
为了确定我的方向是正确的,我在IDA中打开最后一个DLL,并查找windowscoredeviceinfo.dll的出现。Strings标签页中能够清楚的找到这个dll的名字。
注意:你必须配置视图以包含unicode字符串,这不是IDA的默认设置……。
然后,我们可以直接到它在.rdata部分的位置,寻找Xrefs。在这种情况下,只有一个。这个字符串确实是在QueryWCOSDeviceInformation()方法中使用的。好吧,说明我们的路线是对的!
IDA生成的伪代码非常清晰。我们发现前面看到的Process Monitor的LoadLibraryEx("windowscoredeviceinfo.dll")调用。然后,如果库被成功加载,就会进行下面的调用。GetProcAddress("QueryDeviceInformation"),也就是说这个函数应该是windowscoredeviceinfo.dll的导出函数。
我们来总结一下情况。目前,我们知道以下几点。
这是一个好的开始,但我缺少一些关键要素。例如,我不知道这个 “任务 “是如何运行的。我甚至不知道作为一个普通用户是否能够触发它。所以,与其在不知道自己在寻找什么的情况下就开始对服务进行逆向工程,我决定创建一个PoC DLL,并验证我是否真的能以NT AUTHORITYSystem的方式获得任意代码执行。
这个PoC DLL的目标非常简单。我想记录一些关于加载它的进程的关键信息:命令行、当前用户名、PID和PPID。所有这些都会被记录到C:tempdll.log中。
首先,我想出了以下代码。TrackCall()函数负责收集和记录信息。它会被DllMain()和QueryDeviceInformation()执行,用来跟踪它的调用者。
#include <Windows.h>
#include <IOStream>
#include <Lmcons.h> // UNLEN + GetUserName
#include <tlhelp32.h> // CreateToolhelp32Snapshot()
int TrackCall(const wchar_t * callingFrom)
{
WCHAR strSt[4096], strUsername[UNLEN + 1];
WCHAR * strComandLine;
SYSTEMTIME st;
HANDLE hFile, hToolhelpSnapshot;
PROCESSENTRY32 stProcessEntry;
Dword dwPcbBuffer = UNLEN, dwBytesWritten, dwProcessId, dwParentProcessId;
BOOL bResult;
strComandLine = GetCommandLine(); // Get Command line of the current process
bResult = GetUserName(strUsername, &dwPcbBuffer); // Get current user name
dwProcessId = GetCurrentProcessId(); // Get PID
// Get PPID
hToolhelpSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAppROCESS, 0);
stProcessEntry = { 0 };
stProcessEntry.dwSize = sizeof(PROCESSENTRY32);
dwParentProcessId = 0;
if (Process32First(hToolhelpSnapshot, &stProcessEntry)) {
do {
if (stProcessEntry.th32ProcessID == dwProcessId) {
dwParentProcessId = stProcessEntry.th32ParentProcessID;
break;
}
} while (Process32Next(hToolhelpSnapshot, &stProcessEntry));
}
CloseHandle(hToolhelpSnapshot);
// Create log entry
GetLocalTime(&st);
wsprintfW(strSt, L"[%.2u:%.2u:%.2u] - PID=%d - PPID=%d - USER='%s' - CMD='%s' - METHOD='%s'n", st.wHour, st.wMinute, st.wSecond, dwProcessId, dwParentProcessId, strUsername, strComandLine, callingFrom);
// Save to log file
hFile = CreateFile(L"C:Tempdll.log", FILE_APPEND_DATA, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
bResult = WriteFile(hFile, strSt, lstrlenW(strSt)*sizeof(WCHAR), &dwBytesWritten, NULL);
CloseHandle(hFile);
return S_OK;
}
HRESULT __stdcall QueryDeviceInformation()
{
TrackCall(L"QueryDeviceInformation()");
return S_OK;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
TrackCall(L"DllMain()");
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
然后,作为管理员,我把这个DLL复制到C:/Windows/System32,然后,等待它被调用…
过了一会儿,windowscoredeviceinfo.dll的调用终于出现在Process Monitor中。所有的CreateFile操作都成功返回。但是,在C:temp中没有创建日志文件的迹象。当然,这意味着DLL没有被正确加载,问题是:为什么?我猜测,我创建的DLL没有导出进程所需的所有函数。
此时,我不知道该如何继续,但是,当我回到Process Monitor时,我看到了一些以前没有看到的事件。
文件windowscoredeviceinfo.dll也被一个名为usocoreworker.exe的进程加载,这个进程的权限为NT AUTHORITY/System。这时,一些信息被记录到C:Tempdll.log中,这说明代码此时已经正确执行了。
这看起来更有希望,所以我决定抛开DiagTrack服务,看看这个新目标。
我们又回到了起点。我们需要找出usocoreworker.exe进程是如何创建的。为此,我们可以寻找与写入日志文件的PPID对应的进程。根据任务管理器,它是svchost.exe的一个实例,就像大多数Windows服务一样,所以PPID对我们的帮助不大。
对应的服务是 BrokerInfrastructure,它的描述是 “处理后台任务”。好吧,这也没什么用……。
让我们来看看我们可以从Process Monitor 中找到什么。访问与这个进程相关的事件的属性,然后,进入Stack选项卡,会显示以下内容。我们可以看到,有很多对rpcrt4.dll和combase.dll的引用。这可能意味着这个进程是由一个COM相关的RPC调用触发的。如果是这样的话,根据远程对象和接口的权限,也可以作为普通用户触发。
注。COM用于进程间通信(IPC)。因此,它可以为低权限的进程提供运行高权限操作的能力。
从二进制文件的属性来看,我们可以看到如下描述:USO Core Worker Process。
根据前几个要素,我试图在谷歌上找到更多信息。第一个结果把我引向answsers.microsoft.com上的一个帖子。根据其中一条信息,我发现这个文件与 “Update Orchestrator Service “有关。
细细研究,我发现了这个非常有趣的关于 “USO客户端 “的非官方文档。首先,我们了解到 “USO “是 “Update Session Orchestrator “的缩写。
我们还了解到,”USO客户端”(usoclient.exe)是取代 “WUAUCLT “的工具,而WUAUCLT在以前的Windows版本中是用来管理Windows更新的。事实上,这个工具已经被Windows系统管理员所熟知,因为虽然它不被微软官方支持,但它能让他们自动完成更新管理过程。
注:他们甚至引用了TechNet上微软员工的回复,其中说你不应该直接运行这个工具。这越来越有趣了。我们喜欢做我们不应该做的事情,不是吗?
文档中列出了所有你可以使用的选项。所以,我试着玩玩usoclient命令,看看是否能触发我之前观察到的相同行为。从StartScan开始,根据描述,它会触发一个简单地获取可用更新的检查。
我像往常一样在后台运行 Process Monitor,运行命令,成了。
通过一个简单的命令,我们能够让Update Orchestrator服务以NT AUTHORITY/System的方式运行任意代码。这种方法的另一个好处是,我们可以在DllMain之外运行我们的代码(即在加载器锁之外)。
注意:根据微软的说法,应避免在DllMain中运行代码,因为它可能会导致应用程序死锁。更多信息查看这里。
然而,这种技术也有一些缺点。
依赖于usoclient工具而不了解其工作原理也是我不喜欢这种技术的地方。所以,我对客户端和服务进行了逆向工程,以便制作一个可以在未来项目中重复使用的开源工具。UsoDllLoader。我将在本文的第二部分尝试解释这个过程。敬请期待
本文翻译自 itm4n.github.io, 原文链接 。如若转载请注明出处。