0X00前言
闲来无事一口气看完了南城师傅写的Windows常见提权方法一览,想起最近订阅的文章、HTB靶场和渗透测试的经验,尝试在拓展一下自己的知识边界,于是就有了这下面这篇文章。提权之于红队,相当于川菜之于花椒。在我担任渗透测试工程师的时候发现Windows本地提权方法因为环境问题(域环境与工作组)分为两大类。由于Windows的特殊性,域环境与工作组的攻击方法存在较大的差异,因此我们需要根据渗透测试的实际环境区别对待。
在工作组提权部分中,很大一部分本地提权方法都是通过系统路径环境变量中的可写目录和基于操作系统内存溢出引起的。导致这些威胁的根本原因是常见的企业系统配置错误与企业使用的操作系统版本或系统构建,这使得识别和利用这些问题的门槛降低了不少。
在域环境分中,笔者会介绍红队当中常见的本地提权方法,即在Windows域环境中利用NTLM中继提权和域环境当中的Kerberos协议进行攻击,以提升员工工作站上的权限。
0X01系统路径目录配置问题(工作组)
针对拥有成熟且完善的安全监测方案的企业,想要成功利用漏洞获取高权限,难以避免触发企业的端点和网络监控产品中的检测,这个时候本地提权操作就需要更为隐蔽且可靠的方法。系统路径目录配置问题(工作组)是笔者在渗透测试岗位当中经常使用的Windows本地提权方法,该方法在安装了许多应用程序的多用户系统上非常普遍。
那什么是系统路径目录配置问题呢?答:是由于应用程序或脚本配置不当造成的,这些应用程序或脚本允许用户写入受限目录。例如,如果 Web 应用程序允许用户上传文件,并且该应用程序未正确验证上传文件的目标目录,则攻击者可以上传文件到受限目录。
例如:通过图三的实验发现,在C:\Program Files\test和C:\test中创建两个名为test的文件夹后,创建一个非特权用户,并尝试让非特权用户写入这两个目录。在C:\Program Files目录中创建的目录与C:\目录中创建的目录的继承权限存在显著差异。非特权用户可以写入C:\test\,却无法写入C:\Program Files\test\;即C:\Program Files目录权限默认情况下比C:\目录权限更严苛。
C:\目录(图一):1、默认情况下,Authenticated Users 组具有创建文件和文件夹的权限;2、此权限是可继承的,这意味着它适用于所有未明确拒绝它的已创建目录。
C:\Program Files目录(图二):1、默认情况下,不包含 Authenticated Users 组创建文件和文件夹的权限;2、在“Program Files”中创建的文件夹默认情况下会阻止非特权用户对其进行写入。
图一(图片来源praetorian)
图二(图片来源praetorian)
图三(图片来源praetorian)
0X02DLL侧加载(工作组)
一个常见的DLL侧加载漏洞流程如下:攻击者先修改注册表项以指向恶意 DLL,利用社会工程学(通过电子邮件、社交媒体或其他渠道)接近攻击目标发送恶意DLL,通过话术诱导受害者运行应用程序,当目标运行该应为程序时,应用程序会尝试从注册表项中指定的路径加载 DLL,由于注册表项已被修改,应用程序将加载恶意DLL并执行其代码。(如下图四所示)
图四
在笔者的红队生涯当中,通过这样的方式钓鱼获取目标本地高权限的同时,极大程度上避免了被企业安全设备发现的风险。根据Microsoft的规定我们需要注意的是,DLL侧加载这种行为可能不会引发Windows系统的漏洞告警,因为系统正在执行正确的操作来搜索路径 。但是,如果第三方应用程序安装程序在安装过程中修改了系统路径环境变量并引入了可写路径权限问题,则这可能可能会被Defender监测到并提醒用户。所以如果红队需要掩人耳目,那么微软自家的产品,就是攻击者最感兴趣的目标了。
利用DLL侧加载提权的最直接方法是识别以NT AUTHORITY\SYSTEM身份运行的应用程序服务,该服务尝试加载不存在的DLL (动态链接库) 或尝试执行不存在的可执行文件。服务可能会尝试加载仅存在于桌面操作系统上的 DLL 文件。由于该文件在服务器操作系统上不存在,因此它最终将遍历系统路径,查找该文件。从操作角度来看,对于攻击者来说,最好的情况是非特权用户无需重新启动目标系统即可触发此操作。
图五(图片来源)
例如:图五鱼叉式网络钓鱼攻击所示的电子邮件,其包含一个附件利用Windows ActiveX中的漏洞控件 (CVE-2012-0158) ,单独看来是正常的二进制文件,但组合起来就变成了可执行的恶意文件。如图绿色图标代表看似正常的可执行文件包含DLL侧加载漏洞。Office 2003 Service版本更新后会引入oinfop11.exe。该鱼叉式网络钓鱼攻击加载了一个欺骗性的DLL组件(Oinfo11.ocx)。Oinfo.ocx被加载到内存后,自动解压、加载、解码辅助组件 (Oinfo11.iso)。
0X03Win32k提权(工作组)
Win32k提权是不错的本地提权方法,它的影响范围非常大包括Windows 8、Windows 10和Windows Server中的 Win32k子系统。低权限的用户可以通过利用Win32k提权漏洞,比较容易获得Windows的system权限。该漏洞位于Win32k子系统(即Win32k.sys 内核驱动程序)当中,管理操作系统的窗口管理器、屏幕输出、输入和图形。
发现Win32k子系统只锁定了窗口对象,但未能锁定嵌套菜单对象。这类问题的原因是老版本的代码被复制到新版本的Win32k上造成的。如果攻击者有权限更改系统内存中的特定地址,就会导致菜单对象被篡改或劫持。通过控制菜单对象,攻击者可以获得与启动它的程序相同级别的访问权限(通常情况下是system权限)。Github上的研究人员公布了Win32k提权的poc( Win32kpoc链接),可以将权限提升到系统级别,这这大大降低了该漏洞的利用门槛。该版本的poc除了探索不同的方法来使用释放内存中重新占用的数据来控制第一次写入操作之外,通常不需要新的利用技术。虽然Win32k提权在Windows 11系统版本上看似不可利用,但对企业系统却构成重大风险(如今的企业当中依然以Windows 10作为主力办公系统)。且Win32k提权漏洞很大程度是因为系统泄露的桌面堆句柄地址,如果企业没合理的解决方案,企业的信息安全仍然面临较大的风险,对红队来说是一种不错的本地提权手段。
笔者的研究是在Windows Server 2016 上进行的,通过网上的漏洞分析博客和研究该漏洞补丁,笔者发现了一个相当有趣的问题。漏洞的代码看起来是特定对象的引用计数经历了多次增量(图六),由于win32k代码的古老性,笔者只能通过列举一些早期的源代码注释(图七)增加说服力。Win32k的代码实现仅专注于锁定窗口对象,无意中忽略了锁定嵌套在窗口对象中的菜单对象。那么该窗口中菜单对象的错误引用是如何发生的呢?笔者对漏洞函数上下文进行了分析(图八、图九)。在分析过程中,笔者发现了一个值得注意的问题。虽然传递给 xxxEnableMenuItem() 的菜单通常被锁定在更高级别的函数中,但准确确定在这种特定情况下应保护哪个菜单对象提出了一个需要确定的问题。通过继续深入分析xxxEnableMenuItem函数,笔者仔细检查了菜单对象的潜在处理,发现了一个重要的问题,即MenuItemState函数返回的菜单不仅可以包含窗口内的主菜单,还可以扩展到子菜单,在某些情况下甚至可以扩展为子子菜单。
图六(图片来源)
图七(图片来源)
图八(图片来源)
图九(图片来源)
在利用Win32k提权漏洞时,必须进行初步的评估,以避免在无法绕过的情况,从而节约大量的时间。在分析其poc 或exp时,构建针对此特定漏洞的漏洞利用之前,需要考虑如下两种情况:执行Shellcode\ 使用读写原语修改令牌地址。基于早期的 CVE-2017-0263 和 CVE-2016-0167 漏洞涉及执行shellcode操作。笔记不建议使用这种方法,因为在较高版本的 Windows中执行shellcode行为和安全机制(如 SMEP)存在不小的挑战。在过去的几年当中,公开的poc已经证明了使用读写原语修改令牌地址的优越性。虽然这类漏洞利用在桌面堆内存布局和桌面堆中的读写原语方面显示出了持久性、通用性,但是也存在不小的挑战。在重用UAF(释放后使用)内存时,如何控制 cbwndextra 的值?答:笔者建议将整个利用分为两个不同的问题:1、使用UAF漏洞控制cbwndextra的值;2、在获得cbwndextra值的控制权后建立读写原语的稳定方法。
攻击者触发该漏洞时,系统可能会崩溃。这是因为poc的触发方法消除了系统中重用漏洞的所有其他关联。系统可能仅在 xxxEnableMenuItem 的 MNGetPopupFromMenu() 和 xxxMNUpdateShownMenu() 函数中错误地使用我们控制的内存的窗口对象数据。为了在漏洞触发期间占用释放的菜单对象,可以利用窗口类WNDClass中的窗口名称对象。第一次数据写入发生在此过程中,我们在构建的地址结构中确定合适的位置。这个位置允许我们任意写入数据,即使只是一个字节(可以写入cbwndextra的高位)。
在 xxxRedrawWindow 函数中,我们确定了两种替代方法来处理相邻内存数据的操作。让我们更详细地探讨这些方法:使用 GreCreateRectRgnIndirect 的方法/依靠标志位AND 2运算的方法.一种选择是利用 GreCreateRectRgnIndirect,但它带来了两个挑战。首先,控制cbwndextra的前8位被证明是困难的,因为它在特定的有限条件下仅短暂地变成1。其次,当采用这种方法时,cbwndextra的其他相对偏移量将指向前一个对象的最后8位(无论对象类型如何)。最后8位通常表示堆链表末尾的安全字节,并且不易操纵。考虑到与第一种方法相关的困难,我们选择了另一种方法,该方法涉及使用AND 2运算来操作标志位。然而,控制堆链表末尾的安全字节仍然是一个挑战。为了克服这个障碍,我们修改了策略。我们没有写入窗口对象的cb-extra,而是将写入操作定向到HWNDClass的cb-extra。后者的cb-extra相对于前者的cb-extra有更小的偏移量,允许我们操作前面对象的内存数据,作为xxxRedrawWindow函数中标志检查的参数(图十)。
图十
0X04NTLM中继提权(域环境)
在进入域环境的情况下提权,我们可以利用这样一个方法:NTLM中继提权(参考)。在NTLM中继中,攻击者需要将截获的Net-NTLM Hash重放进行攻击,从而实现对其他机器的控制。对于工作组的机器来说,两台机器的密码需要一致才能成功,但是对于域用户来说,被欺骗用户(发起请求的用户)需要域管理员组里边的用户才可以,NTLM 中继成功后的权限为被欺骗用户的权限。要使得Net-NTLMhash重放重放成功,首先要做的就是获取这个Net-NTLMhash,由于SMB、HTTP、LDAP、MSSQL等协议都可以携带NTLM认证的三类消息,所以只要是使用SMB、HTTP、LDAP、MSSQL等协议来进行NTLM认证的程序,都可以尝试向攻击者发送Net-NTLMhash从而让攻击者截获用户的Net-NTLMhash,也就是说我们可以通过这些协议来进行攻击。
图十一
NTLM的认证过程(如图十一):
当客户端需要访问服务器时,客户端需要输入服务器的用户名和密码进行验证,并且客户端会将服务器的NTLM-Hash值缓存。之后客户端开始向服务器发送TYPE 1 Negotiate 协商消息
服务器收到客户端发送来的 TYPE 1 协商消息后,会取出其中自己能够接受的内容,传入NTLM SSP,得到TYPE 2挑战消息,此 TYPE 2消息中包含了一个由服务端生成的16位随机值,被称为 Challenge。服务器将此challenge保存一份后将TYPE 2消息发送回客户端
客户端收到服务器发来的TYPE 2消息后,读出其中的challenge值,用缓存的服务端密码的NTLM-Hash对其进行加密,并与用户名、challenge等一起组合得到Net-NTLMHash ,最后将 Net-NTLMHash 封装到 TYPE 3 NTLM_AUTH消息中发往服务器
服务器接收到客户端发送来的 TYPE 3 消息后,取出其中的Net NTLM-Hash值,并向域控制器发送针对客户端的验证请求。该请求的内容包含:用户名、原始的 Challenge 和 加密后的Challenge(也就是Net NTLM-Hash)
DC根据用户名取出该帐号的密码哈希值 NTLM-Hash,用密码哈希值 NTLM-Hash 对原始的Challenge进行加密得到Net NTLM-Hash。如果加密后的Challenge和服务器发送的一致,则意味着用户拥有正确的密码,验证通过,否则验证失败。DC将验证结果发给服务器
在NTLM身份验证期间,客户端可以使用其密码加密服务器提供的某些信息来向服务器证明其身份。因此,攻击者必须要做的事情就是让客户端完成其工作,并将消息从客户端传递到服务器,并将回复从服务器传递到客户端。所有客户端要发送到服务器的消息,攻击者都会收到,然后他会将消息发送回真正的服务器,而服务器发送给客户端的所有消息,攻击者也会收到,并且他会按原样将它们转发给客户。事实上,从客户端的角度来看,攻击者和他之间发生了NTLM身份验证,并具有所有必要的条件。客户端在其第一条消息中发送协商请求,攻击者以质询回复该请求。收到此质询后,客户端使用其秘密构建其响应,并最终发送包含加密质询的最后一个身份验证消息。但是攻击者无法通过此交换执行任何操作。从服务器的角度来看,攻击者与其他客户端一样都是客户端。他发送了第一条消息来请求身份验证,服务器以质询作为响应。当攻击者向真实客户端发送相同的质询时,真实客户端用其秘密*加密了该质询,并以有效的进行响应。因此,攻击者可以将此有效响应发送到服务器。从服务器的角度来看,攻击者已经使用受害者的秘密对自己进行了身份验证,但对服务器来说是透明的。它不知道攻击者正在向客户端重播他的消息,以便让客户端给他正确的答案。在这些交换结束时,攻击者在服务器上使用客户端的凭据进行身份验证。
笔者建议各位阅读文章《Gone to the Dogs》中概述了一种通过利用自定义 Windows 锁定屏幕的功能,作为非特权用户获取通过 HTTP 的计算机帐户 NetNTLM 身份验证原语的方法。要成功利用该漏洞,需要满足以下前提条件:1、运行 Windows Server 2012 或更高版本操作系统的域控制器;2、攻击者必须有权访问具有服务主体名称集的用户或计算机帐户对象,或者能够将新计算机添加到域中;3、域控制器不得配置为强制执行 LDAP 签名和 LDAP 通道绑定(默认设置);4、受害计算机必须安装并运行“webclient”服务(Windows 10 上默认安装);5、必须允许用户自定义 Windows 锁定屏幕(默认权限),这些内容值得我们慢慢刨析。
0X05Kerberos协议(域环境)
除此之外,在进入域环境的情况下提权也可以尝试基于资源的约束委派或其他基于Kerberos的攻击的操作。那什么是基于资源的约束委派?答:RBAC 是一种访问控制模型,它通过将权限分配给用户来控制用户对资源的访问。资源可以是任何类型的对象,例如文件、数据库表或应用程序。在很多企业内部,都会有一个专门将机器加入域的账号。如果我们拿到了Account Operators组内用户权限的话,则我们可以拿到除域控外所有机器的system权限。攻击者可以查询域内计算机的 mS-DS-CreatorSID ,这个值代表的是将计算机加⼊到域内的用户,它是具有修改 msDS-AllowedToActOnBehalfOfOtherIdentity 的权限的,如果我们可以拿到那个用户的凭据,就可以控制那 个用户添加到域内的所有的电脑。
添加机器账户,修改msDS-AllowedToActOnBehalfOfOtherIdentity值为机器账户的SID ,然后以机器 账户的身份伪造成administrator申请⼀张访问此机器账户机器的 ticket (类似于白银票据),因为机器账户没 有配置约束性委派,所以这张票据是不可转发的,但是在基于资源的约束性委派中,票据是否可以转发不重要,对之后对 s4u2proxy不影响,最后利用这张ticket去申请访问修改了msDSAllowedToActOnBehalfOfOtherIdentity属性的机器。
图十二
操作员指定了 IP 地址 (192.168.184.144),而不是在与生成的 TGS 票证 (DESKTOP-KOERA35.CONTOSO.LOCAL) 关联的服务主体名称中指定的完整主机名。
图十三(图片来源praetorian)
操作员可能会尝试使用 Rubeus 从主机生成新的信标,以将执行S4U时检索到的TGS票证导入到当前登录会话中。虽然此技术在针对其他主机时有效,但当尝试从同一主机使用WMI执行信标时,似乎不会执行“完整网络登录”。相反,会利用与流程关联的安全令牌。结果如下图所示。尽管“管理员”用户的TGS令牌与其登录会话相关,但辅助信标是作为JSMITH用户生成的。为了避免遇到此问题,我们必须使用SOCKS将Impacket代理到主机来执行完整的网络登录。
图十四(图片来源praetorian)
0X06防御方法
系统路径目录配置问题的修复相对容易,因为只需修改可写目录的权限即可。如果应用程序安装程序通过修改系统路径引入了可写路径漏洞,可以考虑向企业SRC提交报告。
限制对受限目录的写入权限:确保只有授权用户才能写入受限目录
验证上传文件:验证上传到 Web 应用程序或其他系统的文件,以确保它们不会写入受限目录
使用安全编码实践:遵循安全编码实践,以避免应用程序配置不当
使用入侵检测系统 (IDS):部署 IDS 以检测可疑活动,例如尝试写入受限目录
定期打补丁:及时安装安全补丁,以修复已知漏洞
修复DLL测加载攻击和Win32k提权攻击的方法也比较容易,需要企业加强安全设备的监测和更新,通过多样化的拦截手段搭配企业内部的培训,可以大大缓解企业的安全威胁问题。
使用数字签名:为合法DLL文件签名,以防止攻击者使用未签名的恶意DLL
DLL 完整性检查:验证DLL文件的哈希值或签名,以确保它们未被篡改
代码签名验证:验证加载的DLL是否已由受信任的颁发机构签名
网络入侵检测/防御系统 (NIDS/NIPS):检测和阻止网络流量中的恶意 DLL
使用沙盒:将敏感应用程序隔离在沙盒中,以限制攻击者对系统资源的访问。
部署反恶意软件解决方案:检测和阻止利用Win32k漏洞的恶意软件
使用虚拟化:将应用程序和服务虚拟化,以隔离它们免受潜在的攻击
实施网络分段:将网络划分为不同的区域,以限制攻击者在系统上的横向移动
至于针对域环境提权方面的威胁,笔者认为,完善企业安全规划,落实企业安全配置策略是必要的步骤。我们知道基于资源的约束委派 (RBCD) 是一种委派机制,会允许用户或应用程序在不授予对整个资源完全控制权的情况下,访问或操作特定资源的特定部分。这有助于减少特权提升攻击的风险,因为攻击者即使获得了对受限资源的访问权,也无法访问或修改资源的其他部分。防御基于资源的约束委派攻击的措施包括:
实施最少权限原则:仅授予用户和应用程序执行任务所需的最低权限,包括对资源的访问
使用访问控制列表 (ACL):配置 ACL 以指定哪些用户和应用程序可以访问资源的哪些部分
启用审核日志记录:记录对受限资源的访问和修改尝试,以检测可疑活动
使用安全编程实践:遵循安全编码准则,例如输入验证和边界检查,以防止缓冲区溢出和整数溢出等漏洞
0X07结论
从安全技术角度来看,笔者认为Windows本地提权最理想的方法就是系统路径目录配置问题+DLL侧加载。在许多企业环境中,笔者发现通过打点进入企业环境当中,web服务往往是不错的突破口,安全设备对web服务的访问不需要多重身份验证,且企业内员工通常都可以访问该网站,并且使用往往跨越多个部门,web服务的主机通常安装有大量应用程序,笔者发现在这些主机上的系统路径目录配置多多少少会有些问题。由于打点成功后,权限普遍不高,这个时候系统路径目录配置问题+DLL侧加载就是不错的解决方案。
在企业内网获得立足点后,通过区分企业内部环境(工作组\域环境),在横向移动后,使用合适的攻击方式Win32k提权(工作组)和针对NTLM中继提权\Kerberos协议(域环境)进行攻击。以避免被具有成熟检测和响应能力的企业SOC发现。在类似的环境中,不合理的使用权限提升技术和横向移动技术将被企业SOC快速监测到,并被驱逐,导致攻击失败。
最后的最后,在次感谢南城师傅文章的启发,作为一只安全咸鱼,希望大家针对笔者上述文章进行批判,在评论区进行斧正,笔者会保持平常心。毕竟提权涉及的知识点过多,笔者也难免存在疏漏和不足。正可谓,攻击者水平如何与攻击者的知识面和能力休戚相关。Win32k提权和Kerberos协议都不笔者现在可以吃透的。所以,笔者虚心接受匹配,享受学习的过程。
参考文章:
https://www.numencyber.com/cve-2023-29336-win32k-analysis/
https://github.com/KaLendsi/CVE-2022-21882/blob/main/ExploitTest.cpp
https://www.real-sec.com/2022/01/technical-analysis-of-cve-2021-1732/
https://www.freebuf.com/articles/system/338877.html
https://eladshamir.com/2019/08/08/Lock-Screen-LPE.html
https://petri.com/understanding-kerberos-delegation-in-windows-server-active-directory