惠普电脑预装软件多个高危漏洞深入分析

 

预装软件为安全人员带来了独特的攻击面。它并不是由操作系统引入的漏洞,而是由您购买电脑的制造商造成的。一些懂技术的人可能会采取措施,主动删除掉电脑中的烦人软件,但普通消费者往往不会这样做。研究预装软件漏洞是一件十分有趣且有意义的事情,因为它不仅提供了一个全新的攻击视角,而且可以影响成千上万将预装软件保留在机器上的用户。

在过去的一年里,我一直在研究由戴尔(Dell)和联想(Lenovo)等知名电脑制造商开发的预装软件。同时在戴尔的预装软件中发现了一些漏洞包括远程代码执行本地权限提升。然而在挖掘漏洞的过程中我发现这些知名厂商所开发的软件很少甚至没有经过安全审计,导致代码质量十分糟糕且存在大量安全问题。

在本篇文章中,我们将着眼于HP Support Assistant,它是2012年10月之后出售的惠普电脑上的预装软件,运行于Windows 7、Windows 8或Windows 10操作系统。我们将详细分析HP预装软件中的多个高危漏洞,并重点讲解其发现过程和利用手段。

下面是文章的目录,可以帮助大家了解整体的研究架构。无论如何,我强烈推荐读者阅读第一和第二章节内容。因为这俩节主要介绍了一些重要的基础知识,对理解大多数漏洞来说非常有帮助。

背景知识

在理解每个漏洞的机理之前,我们需要知道HP Support Assistant在概念上的运行流程。除了远程代码执行漏洞之外,本节将记录了解文章中每个漏洞所需要的相关背景知识。

dnSpy中打开目标的一些二进制文件,发现他们是经过SmartAssembly混淆处理过的。然而这种混淆效果十分有限,我们可以通过 de4dot轻松将其去混淆。

在HP Support Assistant启动的同时,会开启一个“服务接口”,向客户端提供250多个不同的功能。接口通过 WCF Net Named Pipe访问本地系统。客户端通过管道net.pipe://localhost/HPSupportSolutionsFramework/HPSA与接口通信。

用于与客户端通信的管道不止一个。客户端必须先经过验证,才能调用目标接口中的任意方法。为了实现验证过程,客户端将调用StartClientSession方法创建session,并将一个随机字符串作为第一个参数发送给服务端。服务端随即接收到此随机字符串,并为客户端创建俩个新的管道。

第一个管道名为\.pipeSend_HPSA_[random string],第二个管道名为\.pipeReceive_HPSA_[random string]。我们从这俩个管道的名称中也能知道他们的功能,分别为发送和接收消息。

在创建管道成功后,客户端会向服务端发送字符串Validate。当服务端通过管道接收到除close之外的任何消息时,它都会自动开始验证过程—而不管发送过来的消息内容是什么(“abcd”同样会触发验证)。

服务首先通过在管道句柄上使用GetNamedPipeClientProcessId方法获取与它通信的进程的进程ID。而后,服务获取进程的完整映像文件名(路径)以进行验证。

验证的第一步是确保C# Process对象的“main module”属性与GetProcessImageFileName返回的进程完整路径相同。

第二步验证是要求进程文件不仅被签名过且它的证书对象必须包含 hewlett-packard, hewlett packard或是 o=hp inc

接下来按照以下步骤来检查进程的完整路径:

  1. 路径为绝对路径(不是相对路径)。
  2. 路径不以开头。
  3. 路径不包含…。
  4. 路径不包含.。
  5. 路径不能再解析、再拼接。

客户端验证的最后一步是检查每个父进程是否通过前面的验证步骤且进程路径是以下面的路径开头的:

  1. C:Program Files[ (x86)]
  2. C:Program Files[ (x86)]Hewlett PackardHP Support Framework
  3. C:Program Files[ (x86)]Hewlett PackardHP Support Solutions
  4. C:Windows (除了C:WindowsTemp)

如果您通过了所有检查,那么您的client session将被添加到有效客户端列表中。服务端生成一个4字节的整数作为client ID,并返回给客户端。客户端可以通过调用接口方法GetClientToken并向服务端发送之前接收到的client ID来获得调用“受保护”方法所需的令牌。

如果您的进程文件名是以下之一:

  1. C:Program Files[ (x86)]Hewlett PackardHP Support FrameworkHPSF.exe
  2. C:Program Files[ (x86)]Hewlett PackardHP Support FrameworkResourcesProductConfig.exe
  3. C:Program Files[ (x86)]Hewlett PackardHP Support FrameworkResourcesHPSFViewer.exe

那么,刚刚获得的令牌(token)就会添加到trusted tokens列表中。利用此令牌,客户端就可以调用某些受保护的方法。

在进入下一节之前,我们需要知道另一个重要的设计概念。它对于我们理解本篇中出现的几乎所有的漏洞都很重要。在大多数服务端提供的方法中,通常需要通过一个名为ActionItemBase的结构来接收相关参数,从而执行特定的操作。这个结构为客户端设置的各种方法提供了几个预定义的属性。这就意味着每当您看到对action item base属性的引用时,就应该知道此属性同样可以由攻击者完全控制。

在下面的章节中,我们将深入分析漏洞的机理以及每个漏洞的发现/利用过程。

 

前提条件

在进行特定的漏洞利用之前,能够调用受保护的(protected)服务端方法是我们的首要任务。因为WCF服务中的大多数函数都是“受保护的”,所以我们需要能够调用这些函数来最大化潜在的攻击面。在上一章节中我们知道了正常调用受保护方法的过程,下面我们就“如何绕过检查”这一问题做相关讨论。

惠普面临的真正问题其实是产品在设计层面上的不安全性。当然我认为有一些缓解措施还是有用的,比如上一节提到的完整性校验,然而也只是亡羊补牢。因为核心组件(如HP Web Product Detection)依赖于对服务端的访问,并在非特权的上下文中运行。按照当前HP服务端的设计方式,该服务必须能够接收来自非特权进程的消息。然而只要非特权进程能够与服务通信,我们(第三方攻击者)就总有一种方法可以与服务通信。

我的首选方式是添加HP.SupportFramework.ServiceManager.dll二进制文件作为我的C# POC payload的引用。重写服务的整个客户端部分会浪费大量的时间,而且也没有必要。因为针对二进制文件本身并没有做相关的检查,即使有也没有关系,因为它是由客户端方完全控制的DLL文件。重要的检查其实是在处理客户端传入连接的服务端处。

我们需要绕过的第一处检查是在前一章节中的第二步也就是服务端会检测我们的二进制文件是否经过HP证书签名。绕过这层检查非常容易,我们可以假冒是HP的二进制文件。想要实现这个目标有很多方法,比如说 Process Doppleganging。但我在这里采取的方法是将一个经过HP签名过的文件运行并悬挂,而后注入我的恶意C# DLL。这给了我来自HP程序的上下文环境,因为我是从它的实际路径中启动了这一签名过的二进制文件(Program Files x86…)。

在上一章节中我们也提到了对客户端父进程的检查,为了绕过这一检查,我打开了到Windows资源管理器进程的PROCESS_CREATE_PROCESS句柄,并利用PROC_THREAD_ATTRIBUTE_PARENT_PROCESS属性来欺骗父进程。

通过上述操作我们绕过了一般情况下的检测措施,从而最大化了对服务端的攻击面。

 

本地权限提升漏洞

发现:本地权限提升#1~#3

在进行漏洞利用之前,我们需要仔细查看每个漏洞所处位置的代码来加深理解。

我们从“受保护的”服务端方法InstallSoftPaq开始说起。我不知道“SoftPaq”代表的是什么含义,也许是software package?不管怎么说,此方法用于安装HP更新和软件。

该方法有三个参数。首先是一个ActionItemBase对象,它用来指定安装过程中的一些细节。第二个是一个整数,它指定将要启动的安装程序的超时时间。第三个是一个布尔值,指示是否直接安装,不过它几乎不起任何作用,因为服务端方法使用ActionItemBase来决定它是否直接安装。

该方法首先检查action item base的ExecutableName属性是否为空。如果是,则通过连接action item base的SPName属性和.exe作为可执行程序名。

如果action item base的SilentInstallString属性不为空,则将采取不同的方式来执行安装程序,这种方式称之为“静默安装”(silent install),而另一种更一般的方式我们可以称为“直接安装”。

静默安装

在静默安装的情况下,服务端会首先检查在C:WindowsTEMP目录下是否存在Extract.exe。如果文件确实存在,它会将此文件的长度与HP安装目录中的”受信任“的Extract.exe进行比较。如果长度不匹配,服务端将把”受信任“的Extract.exe(即自己目录中的)复制到临时目录下。

接下来,方法将检测C:WindowsTEMP + ExecutableName这样一个路径所指向的文件是否存在 。如果该文件确实存在,则检测该文件是否由HP签名过(有关验证的详细描述,请参阅第一章节)。

如果action item base的SilentInstallString属性包含有SPName + .exe,那么新进程的当前目录为临时目录(即C:WindowsTEMP)。否则,将新进程的当前目录设置为C:swsetup + ExecutableName

此外如果是后者(也就是不包含),该服务将启动之前复制的Extract.exe二进制文件,并尝试将下载内容解压到:当前目录 + ExecutableName(删除后缀.exe)。如果此目录已存在,则在执行之前将其删除。在Extract.exe执行结束后,子方法将返回true。如果提取过程方法返回false(通常是由于发生了异常),下载过程便会停止。

在确定好当前目录并解压下载内容之后,该方法使用双引号作为分隔符将SilentInstallString属性拆分为一个数组。

如果拆分后的数组长度小于2,安装过程将停止并抛出错误,表明SilentInstallString无效。否则,该方法将检查拆分数组的第二个元素(索引1)是否包含.exe、.msi或.cmd。如果没有,则停止安装。

之后该方法将获取第二个元素,并将其与之前解析的当前目录+可执行文件名(除去.exe) + + 第二个元素连接起来。例如,如果第二个元素是cat.exe,而ExecutableName属性值为cats,那么解析后的路径就是:当前目录+ catscat.exe。如果此解析路径不存在,则停止安装。

如果通过了前面的检查,并且存在解析路径,则执行解析路径所指向的二进制文件。SilentInstallString作为参数传递给二进制文件,但其中的解析路径将删除。安装超时(分钟)时限由传递给InstallSoftPaq的第二个参数决定。

当二进制文件执行完毕或由于超时而终止时,该方法将返回执行状态并结束。

直接安装

如果SilentInstallString为空,则执行StartInstallSoftpaqsDirectly方法。

方法将进行检查,确保 Path.Combine方法所返回的路径确实有文件存在(C:WindowsTEMPExecutableName为传入参数)。如果文件不存在,安装将停止并返回failure。如果该文件确实存在,则检测该文件是否由HP签名过(有关验证的详细描述,请参阅第一章节)。

最后,该方法将在当前时间下创建一个新的计划任务。将任务设置为由管理员组执行。一旦管理员成功登录,则立即执行指定的程序。

利用:本地权限提升 #1~#3


本地权限提升漏洞 #1

第一个本地权限提升漏洞发生在静默安装条件下的服务端方法InstallSoftPaq处。在上节中我们有这样的描述:

在静默安装的情况下,服务端会首先检查在C:WindowsTEMP目录下是否存在Extract.exe。如果文件确实存在,它会将此文件的长度与HP安装目录中的”受信任“的Extract.exe进行比较。如果长度不匹配,服务端将把”受信任“的Extract.exe(即自己目录中的)复制到临时目录下。

这就是问题所在。无特权的用户可以写入C:WindowsTEMP(但不能枚举目录thanks Matt Graeber!)。这允许非特权攻击者编写恶意二进制文件,将其命名为Extract.exe,而后写入到C:WindowsTEMP,并在文件里添加空字节,直到其大小足以匹配惠普安装目录中的Extract.exe

下面是攻击者执行攻击过程中需要action item base满足的条件:

  1. action item base的SilentInstallString属性必须有值不能是空值。
  2. 临时目录路径(C:WindowsTEMP) + ExecutableName所指向的文件必须是真实存在的。
  3. 文件必须经过HP签名。
  4. action item base的SilentInstallString属性不能包含SPName + .exe

如果上述条件都能满足,那么在服务端方法启动解压程序(Extract.exe)时,我们的恶意文件就会以NT AUTHORITYSYSTEM权限启动,从而成功实现本地提权。

本地权限提升漏洞 #2

第二个本地权限提升漏洞同样发生在静默安装条件下的服务端方法InstallSoftPaq处。我们来回顾一下,为了让InstallSoftPaq方法启动我们的恶意二进制代码,需要满足哪些条件:

  1. action item base的SilentInstallString属性必须有值不能是空值。
  2. 临时目录路径(C:WindowsTEMP) + ExecutableName所指向的文件必须是真实存在的。
  3. 文件必须经过HP签名。
  4. 如果SilentInstallString属性中包含SPName + .exe,那么当前目录为C:WindowsTEMP。否则,当前目录为 C:swsetup + ExecutableName(删除后缀.exe)。
  5. action item base的SilentInstallString属性至少要有一个双引号(用作分隔符)。
  6. action item base的SilentInstallString属性在第一个双引号之后必须要有一个有效的可执行文件名(类似于cat.exe这种)。
  7. 文件名必须包含.exe或是.msi。
  8. 文件的当前目录 + +文件名必须存在。

如果这些要求统统满足,该方法便会执行当前目录 + + 文件名所指向的二进制文件。

在以上这些要求当中我们需要关注的是:我们可以使“当前目录”(执行二进制文件的地方)位于一个可以用低权限创建的目录路径中。任何用户都可以在C:下创建目录,因此,我们可以创建路径C:swsetup,并将当前目录设置为该路径。

同时需要注意ExecutableName删除掉了.exe后缀且此目录是在C:swsetup中创建的。例如,如果我们给ExecutableName的值为dog.exe。那么我们的当前目录为C:swsetupdog。如果选择利用这种方法提权,那么唯一的要求就是在C:WindowsTEMP下存在同样的ExecutableName。(这样是保证通过条件2)

因为无特权用户可以写入C:WindowsTEMP目录,因而我们可以在C:WindowsTEMP下编写一个dog.exe,并通过前几个检查(签名、父进程检查)。最重要的是,临时目录中的二进制文件不需要与C:swsetupdog目录中的二进制文件相同。这意味着我们可以将已签名的HP二进制文件放在临时目录中,而将恶意二进制文件放在swsetup目录中。

将swsetup作为当前目录的一个副作用是:方法会尝试“解压”恶意文件。在解压文件的过程中,方法首先会检查swsetup目录是否存在。如果存在,会将其删除。这对我们来说其实是一件好事,因为我们可以借此判断函数何时执行,从而使得我们可以重新创建目录后快速复制恶意二进制文件。

在尝试解压过程完成且成功重新放置恶意二进制文件之后,该方法将通过获取SilentInstallString参数中第一个双引号字符后的字符串来获取要执行的文件名。也就是说如果我们将参数设置为“BadBinary”,执行的完整路径将是当前目录+ BadBinary.exe。由于当前目录是C:swsetupdog,所以指向恶意二进制文件的完整路径就变成了C:swsetupdogBadBinary.exe。这里我们需要注意对路径指向文件的唯一检查是检查它存不存在,没有签名检查。

一旦确定了最终路径,该方法就会启动最终路径指向的二进制文件。也就意味着我们的恶意二进制代码被成功执行。重新梳理一下攻击过程:

  1. C:WindowsTEMP下放置一个经过HP签名过的二进制文件dog.exe。
  2. 创建目录: C:swsetupdog
  3. 将action item base的ExecutableName属性值设为dog.exeSPName属性值设置为除了BadBinary的其他值,SilentInstallString属性值设为a"BadBinary.exe
  4. 监控C:swsetupdog目录直到其被删除。
  5. 一旦检测到dog目录被删除,立刻重新创建并把我们的BadBinary.exe复制到它下边。
  6. BadBinary.exe成功以高权限运行。

译者小结

这里对于第二种本地提权方法作者描述的比较绕。对于第一种提权方法是利用了在C:WindowsTEMPExtract.exe会以高权限执行,故而将恶意二进制文件替换掉它,并绕过像文件长度检测,签名检测,父进程检测等,从而达到提权的目的。第二种提权方法是利用了在C:swsetup + ExecutableName下的可执行文件会以高权限运行,因而将恶意二进制文件替换掉它。但是在这个过程中要精心构造ActionItemBase中的相关参数,保证上述中的要求能够一一满足,作者叙述中的大部分内容都是在做到这一点,譬如说在C:WindowsTEMP下放置一个经过HP签名过的二进制文件dog.exe且将ActionItemBase中的ExecutableName设置为dog.exe其实是为了绕过要求中的2、3条,SPName属性值设置是为了让当前目录为C:swsetupdog等。我们以这种视角来看作者的描述会更清晰一些。

本地权限提升 #3

下一个本地权限提升漏洞发生在之前提到的InstallSoftpaqInstallSoftpaqDirectly处,也就是前述中的直接安装方法。

直接安装方法与静默安装在一开始有些类似,都是对C:WindowsTEMPExecutableName属性值进行连接,而后对路径指向的文件进行签名验证。这里稍稍有点不同的是,该方法使用 Path.Combine,而不是使用“+”操作符来对两个字符串进行拼接。使用Path.Combine连接路径字符串是一种安全的方法。

Path.Combine之所以比通过“+”拼接路径字符串安全,就是因为它可以检测不合法字符。一旦检测到不合法字符,立刻抛出异常,从而阻止大多数的路径操作攻击。

对于攻击者来说这也就意味着我们的ExecutableName属性不能脱离 C:WindowsTEMP目录,因为/是不合法的字符之一。

但我们同样可以找到办法绕过,因为无特权进程可以直接向C:WindowsTEMP目录下写入文件。这也就是说可以将一个二进制文件直接放入临时目录中,这样InstallSoftpaqDirectly方法最终就会将其执行。这里我们需要解决一个小问题,就是HP证书验证。

要怎么绕过这一验证呢?我们可以将一个合法的HP二进制文件放入临时目录中执行,但是我们需要以某种方式劫持它的上下文环境。在本篇文章的开头,概述了如何通过注入一个已签名的HP二进制文件来进入它的上下文,但这在这里是行不通的,因为是由服务端唯一创建的进程。但我们可以通过简单的DLL劫持实现这一点。

DLL劫持就是说我们将二进制文件导入的动态加载库放入已签名二进制文件的当前目录中。加载动态链接库时,Windows首先搜索的位置之一是当前目录。当签名的二进制文件试图加载该库时,它将加载我们的恶意DLL,从而使得我们能够在其上下文中执行。

我从大量的HP签名二进制文件中随机挑选了一些。对于此次攻击,我选择的是HPWPD.exe,它是HP的“Web Products Detection”二进制文件。找到要劫持的候选DLL有一种简单的方法,就是去找到“缺失”的库。我们可以通过 Procmon找到“缺失”的动态加载库。

通过三个简单的过滤,并运行二进制文件,我们就可以轻松地确定“缺失”的库。

现在我们有了几个候选的DLL。C:VM是我测试虚拟机上的目录。因为第一个路径不在当前目录中,故而忽略掉它。这些CreateFile操作中的每一个操作通常都意味着二进制文件试图加载一个动态链接库并检查当前目录。出于攻击的目的,我们使用第一个看到的RichEd20.dll。

要执行这种本地权限提升攻击,我们所要做的就是编写一个恶意DLL,并将其命名为RichEd20.dll,将其引入C:WindowsTEMP下的二进制文件。而后将HPWPD.exe二进制文件名作为ExecutableName属性值传递给服务端。一旦服务端调用了直接安装方法,该方法将以SYSTEM权限将其启动,然后该二进制文件将加载我们的恶意DLL,从而实现权限升级。

发现:本地权限提升 #4

接下来我们要讨论的一个“受保护的”方法是DownloadSoftPaq

首先,此方法接受3个参数,一个 是action item base、一个是名为MD5URL的字符串以及一个布尔值(该布尔值指示是否常规安装)。对于攻击方而言,可以完全控制这三个参数。

如果isManualInstall参数值为true,且action item base的UrlResultUI属性非空,那么我们将UrlResultUI的值作为下载URL。否则,我们将action item base的UrlResult属性值作为下载URL。如果下载URL不是以http开头的,则该方法将强制下载URL为http://开头。

上图代码中的检测十分有趣。该方法将确保指定下载URL的主机以.hp.com.hpicorp.net结尾。如果此布尔值为假,下载将暂停。

另一个检测是向下载URL发出HEAD请求,并确保响应的 Content-Length大于0。如果返回的Content-Length不符合要求,下载将停止。

下载位置是:C:WindowsTEMP + ExecutableName。该方法并没有使用安全的路径拼接函数,而是使用原始的字符串拼接方式。在检测到网络连接畅通,下载URL的content length大于0,且URL为“有效”,下载便开始启动。

如果action item base的ActionType属性等于“PrinterDriver”,那么方法会确保下载文件的MD5散列值等于CheckSum属性中指定的散列值或从HP的CDN服务器获得的散列值。

在文件完成下载之后,将完成最后的验证步骤。VerifyDownloadSignature方法将检查下载的文件是否已签名。如果文件没有经过正确签名,且shouldDelete参数为真,则该方法将删除下载的文件并返回下载失败。

利用 :本地权限提升 #4

下面我们来介绍如何通过滥用DownloadSoftPaq来下载恶意文件到任意目录下。

一步一步分析,首先第一个要解决的挑战就是下载URL。目前对URL进行的惟一真正意义的检查是确保URLhttp://[this part]/something是以.hp.com或是.hpicorp.net结尾。正如上面图中关于此处检查的代码所示,该方法采用了一种安全的方式,即使用C#的URI类从URL中获取host,而不是试图自己写代码去解析它。这也就意味着除非我们能在C#的host解析中找到“0 day”,否则我们就得用另一种方法来解决这个问题。

好的,现在目标已经确定了,我们的下载URL需要以HP域结尾。DNS劫持也许是一种可行方法,但由于我们要解决的是一个本地权限提升的bug,DNS劫持并不合适。我们知道C#会自动跟随重定向URL,所以如果能在无数HP网站中发现了一个开放式重定向(open redirect)漏洞,问题不就迎刃而解了吗?下面跟大家分享一下我在搜索web漏洞上的一个小技巧。

有时候我在研究当中需要利用到一些漏洞,像开放式重定向或者是XSS等。但苦于耗费精力时间,这个时候我会在一个叫做 “OpenBugBounty”的网站上搜索。在OpenBugBounty上,研究人员可以提交关于任何网站的web bug。OpenBugBounty将接收这些报告,并向受影响的域名所有者发送邮件。在提交之日起90天之后,这些报告将公之于众。

利用以下方式在OpenBugBounty中搜索open redirect:

好的 我们看到了一些可选项,来看看第一个:

可以看到正是我们想要的一个未修复的bug。我们可以通过访问这样一个地址 https://ers.rssx.hp.com/ers/redirect?targetUrl=https://google.com 来测试开放式重定向漏洞是否有效。感谢 TvM(在我重新提交了这个开放式重定向漏洞后至今已有5个月,但是官方依然没有修复…)

重回正题,这个开放式重定向漏洞以.hp.com结尾,完美契合我们的攻击需求。接下来只需要把下载URL重定向到我们的服务器上,就可以操控受害者下载任何文件了。

下一个需要解决的挑战是将文件放置在任意位置。想要做到这一点并不难,因为HP没有使用安全的路径拼接方法。我们可以将action item base的ExecutableName属性设置为相对路径,比如../../something,以此来控制文件的下载位置。

只要我们给ActionType属性传递的值不是“ PrinterDriver”,就不必担心有MD5验证这个步骤。

最后的检查是对下载文件签名的验证。我们来快速查看一下相关代码。

我们在上一节中说到:

如果文件没有经过正确签名,且shouldDelete参数为真,则该方法将删除下载文件并返回下载失败。

shouldDelete是传递给VerifyDownloadSignature方法的第一个参数。我并不想在上一节中指出它的问题,但是很滑稽,因为正如您在上面的图片中看到的那样,VerifyDownloadSignature的第一个参数一直都是false。也就是说由于shouldDelete的值一直是false,所以即使下载的文件未能通过签名验证,它也不会被删除。我真的不知道该怎么解释这个问题……也许是惠普开发人员的失误。不管是什么原因,至少它让我们的bypass过程更加丝滑~

最后,我们可以将任意恶意文件放在我们想要的系统进程上下文中的任何位置,从而实现权限升级。

发现:本地权限提升 #5

下一个我们要讨论的“受保护的”方法是CreateInstantTask

该方法有三个参数。首先是字符串参数updatePath,表示更新二进制文件的路径,然后是更新文件的名称,最后是传递给更新二进制文件的参数。

验证的第一步是检查更新二进制文件的路径。

验证方法将首先确保更新二进制文件路径是绝对路径,不会导致路径再解析或者再拼接。

下一步是检查更新二进制文件路径的绝对路径是以下述白名单中的基本路径开头的(64位系统):C:Program Files (x86)Hewlett PackardHP Support FrameworkC:Program Files (x86)Hewlett PackardHP Support Solutions

因为我们的路径必须是绝对路径同时又必须从一个到HP的程序文件目录路径开始。因而更新路径实际上必须在它们的安装文件夹中。

最后的验证是确保更新路径指向的二进制文件经过HP签名。

如果所有这些检查均通过,则该方法将创建计划任务,并以管理员权限立即执行。一旦有管理员登录,负责更新的二进制文件将立即执行。

利用:本地权限提升 #5

在这一处问题上我想了很久,已知更新文件路径必须在HP文件夹下。那我们还能做些什么呢?在经过一段时间的头脑风暴后,我有了一些思路。

有一个让我感兴趣的地方是:我们可以在HP的安装目录下带着任意参数启动任意程序,那么HP的安装目录里都有些什么呢?是不是会有让我们更进一步的地方呢?

在白名单目录下我找到了一些有趣的二进制文件,像Extract.exe以及unzip.exe。在老版本中,我们可以使用unzip.exe,但是新版本里实现了签名检查。在我尝试正常使用Extract.exe时,发现它居然无法工作。于是我继续查看了在dnSpy中的几个二进制文件,发现了一个有趣的文件HPSF_Utils.exe

当这个二进制文件被传入encrypt或者decrypt参数并启动时,下方的方法便会执行:

如果我们将“encrypt”作为第一个参数传入,那么该方法将接受第二个参数,将其读入并加密,然后将其写入到第三个参数指定的文件中。如果我们传递“decrypt”,它将执行相同的操作,只是读取的是加密文件并将解密后的文件写入第三个参数指向的路径下。

由此便得到了第5个权限提升的漏洞。我们可以滥用解密(decrypt)功能,将恶意文件写入系统中的任何地方,从而实现特权升级。

 

任意文件删除漏洞

发现:任意文件删除

我们从任意文件删除开始说起,尽管任意文件删除可能并不会导致本地权限提升问题,但我觉得这仍然是一个非常严重的漏洞,值得向厂商去报告。因为攻击者可以利用这一漏洞大量破坏装有HP软件的用户电脑。

下面我们要讨论的“受保护的”方法是LogSoftPaqResults。因为只需要了解跟漏洞发生有关的代码,所以这里我们只看前面几行就够了:

在该方法被调用时,它会使用不安全的路径拼接方式去拼接C:WindowsTEMPExecutableName。如果该路径指向的文件存在,且它未经过HP签名,则方法会将其删除。

利用:任意文件删除 #1

我想大家也都想到了,这是一个非常简单的bug。因为HP使用了不安全的路径拼接方式,我们可以通过相对路径轻松脱离 C:WindowsTEMP目录(像../),从而到达任何我们想要去的文件位置。

如果文件存在且没有经过HP签名,那么因为方法运行在系统进程的上下文中,故它有权利将此文件删除。这种漏洞的危害是非常严重的,试想我将你的整个system32目录删除掉,那么你的计算机也就跟着gg了。

利用:任意文件删除 #2

这个漏洞比上一个还要简单,所以我就不再单列出一个发现章节了。单刀直入,看一看发生问题的“受保护的”方法UncompressCabFile。在该方法被调用时,会进行以下检查:

  1. cabFilePath(指向要解压的文件)是否存在。
  2. cabFilePath所指向的文件是否经过HP签名。
  3. 我们的令牌是否已经添加到受信任列表以及cabFilePath是否包含一些预定义的路径。

如果不满足上述条件,方法会删除掉cabFilePath指向的文件。

如果我们想要删除一个文件,只需要构造cabFilePath使其指向目标文件。从而在方法被成功调用时会以SYSTEM权限删除文件。

 

远程代码执行漏洞

发现

在本篇文章的大部分内容中,我们已经介绍了多个关于HP服务中的漏洞。在本节中,我们将探索另一个HP二进制文件。第二章节中介绍的基础知识内容将不再适用于此bug。

在寻找RCE漏洞的过程中,首先要做的是检查已注册的URL协议。虽然我们可以在regedit中手动搜索,但可以选择使用 URLProtocolView工具。它可以轻松地枚举现有的已注册的URL协议及其执行的命令。

按名字排序后向下滚动到“hp”,可以看到有很多协议:

第一个看起来有点意思,因为它的名字是“HP Download and Install Assistant”,也许能帮助我实现远程代码执行。我们在dnSpy中打开它。

发现程序要做的第一件事是将传递的命令行参数给到它的自定义参数解析器中。下面是我们可以传入这个程序的参数:

  1. remotefile:要下载的远程文件的URL
  2. filetitle:远程文件名
  3. source:启动点或是启动源
  4. caller:调用进程名
  5. cc:国家名
  6. lc:语言名

解析出参数后,程序开始构建“下载请求”。该方法将读取下载历史记录,以查看文件是否已经下载。如果之前没有下载,则该方法将下载请求添加到下载请求XML文件中。

创建请求后,程序将“处理”历史文件中的XML数据。对于每个挂起的下载请求,创建一个下载器。

在创建过程中,将进行第一次完整性校验。下载器的构造函数首先提取remotefile参数中指定的文件名。它通过在remotefile参数中查找字符/的最后一个索引来解析名称,然后获取从该索引开始到字符串末尾的子字符串。例如,如果我们为remotefile传入https://example.com/test.txt ,该方法将解析出text.txt。

如果文件名包含.,则开始对扩展名进行验证。首先,该方法通过获取名称中最后一个.之后的所有内容来解析文件扩展名。如果扩展名是exemsimspmsup2,那么下载内容将被标记为安装程序。否则,扩展必须是其他70个白名单扩展中的一个。如果扩展不是其中之一,下载任务将被取消。

在验证文件扩展名之后,将开始创建完整的文件路径。

首先,如果lc参数值是ar(阿拉伯语)、he(希伯来语)、ru(俄语)、zh(汉语)、ko(韩语)、th(泰国语)或ja(日语)之一,那么您的文件名就是从URL中提取的文件名。否则,您的文件名是filetitle参数值,’ – ‘,来自URL的文件名的组合。最后,该方法将用空格替换掉路径中出现的Path.GetInvalidFileNameChars中规定的所有不合法字符。

之后将这个过滤后的文件名与用户当前的下载目录和 HP Downloads组合在一起构成了完整路径。

下一个相关步骤发生在开始下载部分,这是第二个完整性检验的地方。首先,远程文件URL参数必须以https开头。接下来,为远程文件URL创建一个URI对象。此URI的host必须以.hp.com.hpicorp.net结尾。只有通过这些检查,下载才会开始。

在下载完成后,另一项检查也随之完成。在下载被标记为下载完成之前,程序将验证下载的内容。首先检查下载的内容是否是安装程序,安装程序是在文件名解析阶段设置的。如果下载的是安装程序,那么该方法将验证下载文件的证书。如果文件不是安装程序,则不进行检查。

方法通过在二进制文件上调用 WinVerifyTrust检查它是否与二进制文件匹配,这也是验证文件证书的第一步。同时注意方法不关心证书是否被吊销。下一项检查首先提取证书的subject ,而后检查其是否包含hewlett-packard, hewlett packard, 或是 o=hp inc

当用户按下窗体上的open或install按钮时,将再次执行相同的检查,然后使用C#的Process.Start启动文件(没有参数传递)。

利用

有很多种方法可以利用此处产生的漏洞,我会在下一节中着重向大家介绍不同攻击的变体以及他们各自的优缺点。这里我们先从各种变体都需要绕过的一个检查—URL检查说起。

如前所述,远程URL需要以.hp.com.hpicorp.net结尾。因为验证方法使用安全可靠的C# URI解析器来验证URL的host,所以我们的host必须包含.hp.com.hpicorp.net

文章前边提到的一个漏洞中也面临了同样的问题。如果你还没有读过这部分内容,我强烈建议你去了解一下,我们从惠普的一个网站——https://ers.rssx.hp.com/ers/redirect?targetUrl=https://google.com 中得到了一个开放式重定向漏洞。

回到验证方法,开放式重定向漏洞发生在ers.rssx.hp.com,它是以hp.com结尾的,完美契合攻击需求。我们可以利用它将请求重定向到我们的服务器,从而使得受害者下载并执行设计好的恶意文件。

这一段总的来说总结了利用此远程代码执行漏洞的一般性方法,下面我们来看一看关于它的各种变种。

远程代码执行变种 #1

首先我们来看一看攻击者可以让受害者下载什么类型的文件,这里需要保证下载的文件不会让惠普认为它是一个安装程序(只有安装程序会被验证签名)。

看一看白名单扩展名,我们能够选择的其实也就是zip。一个理想的攻击场景是网站告诉你需要一个关键的更新,这时下载程序突然弹出惠普的logo,接着受害者打开zip压缩包,并执行其中的二进制文件。

这里我们需要利用之前提到的开放式重定向漏洞,将恶意文件放在我们的web服务器中,而后将指向此zip恶意文件的URL添加到开放重定向URL尾部。

当受害者访问恶意URL时,hpdia://协议随之加载,下载任务开始。

这种方式的RCE需要用户交互的地方比较多,需要用户点击俩次。但我仍然认为这是一种有效的攻击方法,因为HP下载程序看起来相当正规,很容易骗过没有防范的用户。

远程代码执行变种 #2

这种方法比上一个更有趣,首先我们让程序下载一个DLL文件(不是安装程序),而后安装一个经过HP签名的二进制文件并引入此DLL。

和第三个本地权限提升漏洞中描述的DLL劫持类似,我们可以利用任何经过签名的二进制文件来导入一个不存在的DLL。通过将我们的恶意DLL命名为“缺失”的DLL名称,签名过的程序将最终加载此恶意DLL,从而实现远程代码执行。

这里需要注意的是:我们必须将下载器的语言参数设为阿拉伯语、希伯来语、俄语、汉语、韩语、泰语或日语之一。原因是因为执行DLL劫持需要更改文件名使其与“缺失”的DLL文件名相同。

回想一下CreateLocalFilename方法,如果我们传递的语言参数不是这些语言之一,那么实际的文件名里将有' - '。如果我们的语言参数值是上述语言之一,那么文件名将是在远程URL中指定的文件名。

如果你的目标受害者所在国家普遍使用这些语言,我认为这是最好的攻击路方法。如果语言不一致,那失败的可能性会大大增加。

在受害者按下打开或安装按钮的时候,签名过的二进制文件将启动并加载我们的恶意DLL。如果你的目标受害者是在一个英语国家或者只说英语,成功几率会大幅下降。

你可以让网站提供一个关键的更新指南,告诉用户按下什么按钮来更新他们的系统,但我不确定用户是否在不是母语的软件中还能按照说明按下按钮或是打开zip文件。

总之,这是一次点击就可以完成的攻击方法,如果应用在上述语言的国家中,会起到非常好的效果。攻击者也可以在检测到受害者的语言符合要求后使用此变体。检测方法可以是在HTTP头中看到受害者的语言,也可以通过javascript中的“navigator language”

远程代码执行变种 #3

最后一种变体对攻击者的要求较高,可能更适用于APT。

回顾一下,如果下载的文件的扩展名被认定为安装程序,那么将对我们的二进制文件进一步检查,检查其是否有HP的签名。我们来看一下签名检测部分的代码:

该方法接受一个文件名,首先验证它是否是有效的证书。这意味着从合法的HP二进制文件中获取的证书将无法工作,因为它与别的二进制文件并不匹配。

在任何情况下,证书如果要被视为HP证书,其主体必须包含hewlett-packard、hewlett packard或o=HP inc。这其实是一个有疏漏的检查,特别是在小写转换部分。以下是一些我编出的公司或组织名,它们的证书都能通过检查:

  1. [something]hewlett PackArd[someting]
  2. HP Inc[something]

攻击者所需要做的就是创建一个包含这些单词的组织,然后他们就可以获得该公司的证书。接下来,他们就可以为任何安装了HP bloatware的用户提供一键RCE。

 

POC

本地权限提升漏洞:

演示视频地址

远程代码执行漏洞:

演示视频地址

 

漏洞修补情况

在我向惠普提交漏洞报告三个月后,他们就完成了初始的补丁修复。没有想到他们能在如此合理的时间内修补10个漏洞,看来他们似乎拥有一个认真对待安全漏洞的负责任的组织。这与我去年对戴尔bloatware中远程代码执行的研究形成了对比,当时他们花了6个月的时间为一个漏洞发布补丁,速度非常慢。

在下一节中,我会向大家介绍惠普没有修补或修补得不够彻底的漏洞。这里是补丁修复的整体情况:

  1. Local Privilege Escalation #1 – 修复✔
  2. Local Privilege Escalation #2 – 未修复❌
  3. Local Privilege Escalation #3 – 未修复❌
  4. Local Privilege Escalation #4 – “修复” ? (不够彻底)
  5. Local Privilege Escalation #5 – 未修复❌
  6. Arbitrary File Deletion #1 – 修复✔
  7. Arbitrary File Deletion #2 – 修复✔
  8. Remote Code Execution Variant #1 – 修复✔
  9. Remote Code Execution Variant #2 – 修复✔
  10. Remote Code Execution Variant #3 – 修复✔

 

漏洞再发现

新补丁,新漏洞

惠普方面针对漏洞做出的调整主要为:从硬编码的“C:WindowsTEMP”临时目录转变成依赖于客户端提供的action item base来指定临时目录。

我不确定HP PSIRT有没有核查补丁,或者说HP普遍缺少代码审计流程,这种转换实际上引入了一个新的漏洞。现在的情况是:不再使用可信的硬编码字符串作为临时目录……而去依赖不可信的客户端提供的数据(the action item base)。

这样一来,我的无特权进程便可以决定临时目录位置,而不是由特权进程服务端来决定的,这会导致非常严重的后果,这也是该补丁中最令人震惊的地方。惠普不仅留下了一些未修补的漏洞,而且还让一些代码变得更糟。此次更改主要影响本地特权提升漏洞。

本地特权提升漏洞 #2

看一看新的InstallSoftpaqsSilently方法,主要的更改如下:

  1. ActionItemBase指定临时目录位置。
  2. 出现了更多不安全的路径拼接(使用path1 + path2,而不使用Path.Combine)。
  3. 在由临时目录和可执行文件名称指定的路径上出现了更多验证(即检查相对路径转义符,NTFS重新解析点,根目录路径等)。

总结一下在打了补丁之后仍然存在的核心问题:

  1. 该方法的文件执行路径仍然使用未经验证的不可信的客户端输入。具体来说,该方法通过双引号分隔SilentInstallString属性,并利用该数组中的值作为二进制文件的名称。除了简单地检查文件是否存在之外,不验证此二进制名称。因而无法保证它不是攻击者的恶意二进制代码。
  2. 持续使用不安全的路径拼接。例如,HP始终使用“+”操作符连接路径,而不使用 Path.Combine之类的安全连接方法。

现在,如果想成功调用InstallSoftpaqsSilently方法,必须满足以下几点要求:

  1. TempDirectory + ExecutableName指向的文件必须存在。
  2. TempDirectory + ExecutableName指向的文件必须经过HP签名。
  3. TempDirectory + ExecutableName构成的路径必须通过VerifyHPPath函数的检测:a.不能包含相对路径。b.路径必须从根目录开始。c.路径不能再拼接。d.路径必须以HP路径开始。
  4. SilentInstallString属性必须包含SPName + .exe
  5. 目录名现在是TempDirectory + swsetup + ExecutableName(去掉.exe后缀)
  6. 可执行文件的名称在SilentInstallString属性中指定,以双引号分隔。如果分割的大小大于1,则取第二个元素作为EXE名称,否则取第一个元素。
  7. 可执行文件名不能包含.exe或.msi,否则,目录名+ +可执行文件名路径指向的文件必须存在。
  8. 函数执行目录名+ +可执行名处指向的二进制文件。

攻击的要点在第6步和第7步。我们可以通过为SilentInstallString属性构造一个恶意的payload来控制可执行文件的名称,该payload可以让TempDirectory属性中传递的合法目录转义到攻击者控制的目录中。

例如,如果我们传递:

  1. C:Program Files (x86)Hewlett-PackardHP Support FrameworkTempDirectory
  2. HPSF.exeExecutableName
  3. malwareSPName
  4. "..........Malwaremalware.exeSilentInstallString

服务端最终就会以SYSTEM权限执行C:Program Files (x86)Hewlett-PackardHP Support FrameworkswsetupHPSF..........Malwaremalware.exe处的可执行文件,也就是C:Malwaremalware.exe处的由攻击者控制的恶意文件。

本地权限提升 #3

新的InstallSoftpaqsDirectly方法的主要变化如下:

  1. ActionItemBase指定临时目录。
  2. 无效二进制文件不再删除。

这里利用方式和之前全都一样,只是提供给TempDirectory的是我们自己指定的路径…

本地权限提升 #4

新的DownloadSoftpaq方法的主要变化如下:

  1. 下载URL不能具有查询参数。

核心问题在修补后仍然存在:

  1. 对下载URL安全性的验证。
  2. 对下载二进制文件安全性的验证。
  3. ers.rssx.hp.com上的开放式重定向漏洞。

首先,HP仍然只对host名进行检测,以确保host名在白名单中。然而,这并没有解决这个检测本身是存在问题的这一核心问题。

检查host名的方法存在问题,因而存在中间人攻击利用的风险(详情看RCE #3),存在其他开放式重定向利用的风险,以及基于虚拟主机攻击的风险(重定向到127.0.0.1的子域)。

因为HP程序传递了一个常量false作为参数(详情参考本地权限提升漏洞),导致即使文件没有经过HP签名,仍然不会被删除。

目前来看还没有成功利用的例子,但我相信之后一定会出现exp。

本地权限提升 #5

这个漏洞没有修复,按照原有的利用方式就可以成功提权。

 

时间线

10/05/2019 – 向HP发送漏洞报告.

10/08/2019 – HP PSIRT 回应接收并着手处理.

12/19/2019 – HP PSIRT 更新并声明已修复漏洞。

01/01/2020 – 向惠普发送未修复漏洞报告.

01/06/2020 – HP PSIRT 回应接收.

02/05/2020 – HP PSIRT 计划3月第一周出新的补丁.

03/09/2020 – HP PSIRT 由于疫情影响修复计划推迟到3月21.

 

用户建议

如果您想知道需要做什么来确保您的HP电脑不受这些漏洞的影响,那么确保它是最新版本或者已被删除卸载。默认情况下,HP Support Assistant没有自动更新功能,除非您明确选择开启自动更新。需要注意的是,因为惠普还没有修复三个本地特权提升漏洞,所以即使您拥有最新版本的软件,仍然会受到影响,除非您完全从电脑中删除掉它(选项1)。

选项 1:卸载

为了避免遭到本文中描述的攻击以及将来出现的漏洞,最好的解决方法就是完全删除软件。但并不是所有用户都会这样做,特别是当您依赖于软件提供的更新功能时。然而,删除软件可以确保您不受应用程序中可能存在的任何其他漏洞的影响。

对于大多数Windows用户,您可以使用Windows控制面板中的“添加或删除程序”组件来卸载服务。有两款软件需要卸载,一款叫做“HP Support Assistant”,另一款叫做“HP Support Solutions Framework”。

选项 2:更新

另一个选择是将软件更新到最新版本。在最新的更新中修复了除三个本地特权提升漏洞之外的其他漏洞。

更新应用程序有两种方法,推荐的方法是在开始菜单中打开“HP Support Assistant”,点击右上角的“About”,然后按“Check for latest version”。另一种更新方法是安装惠普网站上的最新版本。

(完)