译文声明
本文是翻译文章,文章原作者Oscar Mallo,文章来源:www.tarlogic.com
原文地址:https://www.tarlogic.com/en/blog/abusing-seloaddriverprivilege-for-privilege-escalation/
译文仅供参考,具体内容表达以及含义原文为准
0x01-序言
众所周知,在Windows操作系统中,在没有管理权限的情况下向用户帐户分配某些权限会导致本地权限提升攻击。虽然微软的文档.aspx#Consideraciones_sobre_seguridad)非常清楚,但在几个不同的阶段中,我们发现了分配给普通用户的特权分配策略,我们能够利用这些策略来完全控制一个系统。
今天我们将分析与“Load and unload device drivers(加载和卸载设备驱动程序)”策略相关的影响,该策略指定允许动态加载设备驱动程序的用户。在非特权用户的上下文中激活此策略意味着在内核空间执行代码的可能性很大。
虽然这是一种众所周知的技术,但在post-exploitation和权限提升工具中找不到,也没有执行自动利用的工具。
0x02-SeLoadDriverPrivilege和访问令牌(access token)
“Load and unload device drivers”策略可以通过以下路径从本地组策略编辑器(gpedit.msc)访问:“Computer configuration-> Windows settings-> Security Settings -> User Rights Assignment”。
考虑到其含义,此策略的默认值仅包括“administrators”和“print operators”组。下表是文档的默认值:
注意:看上去print operators组似乎是完全无害的,但是它能够加载域控制器中的设备驱动程序以及管理活动目录中的打印机类型对象。此外,该组具有在域控制器中验证自身的功能,因此验证该组中用户的成员身份具有特殊意义。
此策略的设置允许激活用户访问令牌中的“SeLoadDriverPrivilge”,从而允许加载设备控制器。
访问令牌(access token)是一种对象类型,描述进程或线程的安全上下文,并分配给系统中创建的所有进程。除这些外,它还指定标识用户帐户的SID(安全标识符/Security identifier)、不同组的成员的SID,以及分配给用户或他所属的组的特权列表。这些信息在操作系统的访问控制模型中是必不可少的,并且每当您尝试访问系统中的任何可安全对象时,都会验证这些信息。
为了理解开发过程(稍后将解释),有必要考虑到从Windows Vista开始,操作系统实现了一种名为“用户帐户控制”(UAC)的特权分离技术。总之,这种安全措施基于“最小特权原则(minimum privilege principle)”,通过使用‘受限访问令牌(restricted access tokens)’来限制用户某些进程的权限,令牌省略了分配给用户的某些特权。
考虑到这些信息,我们将分析此特权的利用过程,以便在没有管理权限的情况下从用户帐户加载驱动程序。
0x03-开发利用
获取具有无限制访问令牌的shell
为了获得不受限制的访问令牌,我们有以下选项:
- 利用“以Run as administrator/管理员身份运行”功能来提升用户发起的任何进程的权限。在非管理员的用户上下文中使用这些机制将允许不受限制地获取令牌。
-
利用提权工具。这个工具允许启动提权的进程:
elevate.exe -c cmd.exe
- 编译包含清单的应用程序,以指示使用无限制令牌,该令牌将在启动时触发UAC提示符。
- 使用一些技术绕过UAC。
SeLoadDriverPrivilege特权激活
一旦我们有了一个不受限制的令牌,我们就会注意到,默认情况下,SeLoadDriverPrivilge在访问令牌上的用户特权列表中是可用的,但默认情况下是禁用的。为了利用这一特权,有必要明确地激活它。为了实现这一点,我们必须执行以下步骤。首先,我们需要使用LookupPrivilegeValue() API获取特权的引用。在此之后,可以使用调整TokenPriviliges()函数来激活特权。
TOKEN_PRIVILEGES tp;
LUID luid; if (!LookupPrivilegeValue( NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid)) // receives LUID of privilege {
printf("LookupPrivilegeValue error: %un", GetLastError()); return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid; if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. if (!AdjustTokenPrivileges(
hToken, FALSE,
&tp, sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
printf("AdjustTokenPrivileges error: %un", GetLastError()); return FALSE;
}
驱动程序
可以使用Windows NTLoadDriver API从用户空间加载驱动程序,其格式详细如下:
NTSTATUS NTLoadDriver(
_In_ PUNICODE_STRING DriverServiceName
);
此函数作为唯一的输入参数DriverServiceName,它是一个指向Unicode格式字符串的指针,该字符串指定定义驱动程序配置的注册表项:
RegistryMachineSystemCurrentControlSetServicesDriverName
在DriverName键下,可以定义不同的配置参数。最相关的是:
- ImagePath:REG_EXTEXSZ类型值,指定驱动程序路径。在这种情况下,路径应该是一个具有非特权用户修改权限的目录.
- Type:指示服务类型的REG_WORD类型的值。就我们的目的而言,应该将该值定义为SERVICE_KERNEL_DRIVER (0x00000001)。
要记住的一件事是,传递给NTLoadDriver的注册表项默认位于HKLM键(HKEY_LOCAL_MACHINE)下,后者只定义对管理员组的修改权限。尽管文档表明使用了密钥“Registry Machine System CurrentControlSet Services”,但NTLoadDriver API并不限制HKCU(HKEY_Current_USER)密钥下的路径,这些路径可以由非特权用户修改。
考虑到这种情况,在调用NTLoadDriver API时,可以在HKCU(HKEY_CURRENT_USER)下使用注册表项,指定遵循以下格式的路径:
RegistryUser{NON_PRIVILEGED_USER_SID}
可以通过使用GetTokenInformation API以编程方式获取帐户的SID值,该API允许用户获取他的访问令牌信息。或者,可以使用“whoami/all”命令或通过以下PowerShell指令来查询SID:
(New-Object System.Security.Principal.NTAccount("NOMBRE_CUENTA_USUARIO")).Translate([System.Security.Principal.SecurityIdentifier]).value # En el contexto de un usuario del dominio. Get-ADUser -Identity ' NOMBRE_CUENTA_USUARIO ' | select SID
0x04-PoC
为了利用驱动程序加载特权,我们创建了一个PoC应用程序,以便自动化上面描述的过程。
起点是非特权用户(测试),已为其分配了特权“Load and unload device drivers”。
正如前面所讨论的,最初将为用户分配一个受限令牌,该令牌不包括SeLoadDriverPrivilge特权。
如果你有一个交互会话,可以通过接受UAC提示符来实现提权,否则你应该使用一些UAC绕过技术。
在这种具体情况下,我们假设系统中有一个交互式会话。通过使用工具提权,可以在接受UAC提示符后生成一个具有关联的无限制令牌的新终端。
可以看到,“SeLoadDriverPrivilge”特权存在于用户的访问令牌中,但是它是禁用的。
此时,我们可以使用PoC工具EOPLOADDRIVER(https://github.com/TarlogicSecurity/EoPLoadDriver/),它将允许我们:
- 启用“SeLoadDriverPrivilege”特权
- 在HKEY_FRENT_USER(HKCU)下创建注册表项并设置驱动程序配置设置
- 执行NTLoadDriver函数,指定以前创建的注册表项
可以通过一下命令调用该工具:
EOPLOADDRIVER.exe RegistryKey DriverImagePath
RegistryKey参数指定在HKCU下创建的注册表项(“RegistryUser{NOTERENCEL_USER_SID}”),而DriverImagePath指定文件系统中驱动程序的位置。
可以使用DriverView工具验证驱动程序已成功加载。
0x05-利用
一旦我们能够从非特权用户帐户加载驱动程序,下一步就是识别一个签名驱动程序,该驱动程序存在允许提升特权的漏洞。
对于本例,我们选择了驱动程序Capcom.sys(SHA1:c1d5cf8c43e7679b782630e93f5e6420ca1749a7),它具有允许从用户空间中定义的函数在内核空间执行代码的“功能”。
这个驱动有不同的功能:
- Tandasat ExploitCapcom-https:/github.com/tandasat/utiitCapcom-此漏洞可使您获得Shell AS System
- PuppetStrings by zerosum0x0 -https:/github.com/zerosum0x0/pupPetstring-允许隐藏正在运行的进程
按照上述步骤,我们需要生成一个提权的命令行终端,以获得不受限制的令牌。之后,我们可以执行PoC工具(EOPLOADDRIVER)来启用SeLoadDriverPrivilge并加载所选的驱动程序,如下所示:
一旦加载了驱动程序,就可以运行任何所需的漏洞攻击。下图显示了使用Tandasat的利用“ExploitCapcom”获得SYSTEM终端。
0x06-结论
我们已经能够验证“Load and unload device drivers”特权的设置如何支持内核中驱动程序的动态加载。尽管默认情况下Windows禁止无签名驱动程序,但已在签名驱动程序中发现了多个漏洞,可利用这些漏洞攻击系统。
注意:
所有测试都是在Windows 10版本1708环境中执行的。
从Windows 10版本1803开始,NTLoadDriver似乎禁止引用HKEY_Current_USER下的注册表项。