通过进程创建模拟技术挖掘本地提权漏洞

secM  684天前

index.jpg

引言

在过去几个月中,我们一直与Microsoft安全响应中心(MSRC)保持密切合作,以协助其修复我们在Windows平台上发现的多个本地提权(LPE)漏洞。在这些漏洞中,最广为人知的就是在Windows网络连接助手(NCA)中发现的本地提权漏洞了,不过,该漏洞仅影响Microsoft Windows 10的企业版和教育版。谷歌Project Zero的安全研究员James Forshaw曾经以POC代码的形式向微软报告过这个LPE漏洞的危害程度。然而,MSRC当时的答复是不予修复,因为他们认为Windows系统不会受到这种攻击的影响。

在这篇文章中,我们将为读者详细介绍如何使用简单的漏洞建模技术来发现这种易受攻击的LPE代码模式。

进程创建模拟漏洞的建模方法

关于这个逻辑漏洞的介绍,大家可以在网络上面自行搜索,这里就不再赘述。相反,这里探讨的是如何识别这种常见的漏洞模式。幸运的是,James已经公布了一个简单的POC,我们完全可以从它开始着手进行研究。

该POC为我们提供了一个简单的远程过程调用(RPC)客户端和一个服务器应用程序,用于演示进程创建模拟是如何导致权限提升漏洞的。简而言之,当RPC服务器在不使用显式令牌的情况下模拟客户端并生成进程时,就会导致权限提升漏洞。为了帮助读者了解该漏洞的成因,我们提供了相应的代码:

extern "C" void TestCreateProcess(handle_t hBindingint level)

{

   if (RpcImpersonateClient(hBinding) == 0)

   {

      STARTUPINFO startInfo = { 0 };

      PROCESS_INFORMATION procInfo = { 0 };

      HANDLE hToken = nullptr;

      HANDLE hDuplicateToken = nullptr;

 

      startInfo.cb = sizeof(startInfo);

      WCHAR cmdline[] = L"c:\\windows\\notepad.exe";

      if (level == Medium// -- (1)

      {

         // Open and create client impersonated token

         if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE | TOKEN_QUERYtrue, &hToken))

         {

            printf("Error OpenThreadToken: 0x%x\n"GetLastError());

         }

         else

         {

            if (!DuplicateTokenEx(hTokenMAXIMUM_ALLOWEDnullptrSecurityImpersonationTokenPrimary, &hDuplicateToken))

              {

                 printf("Error duplicate token (0x%x)\n"GetLastError());

              }

              else

              {

                 LPVOID lpEnv;

                 // Create interactive process

                 startInfo.lpDesktop = _T("winsta0\\default");

                 if (!CreateEnvironmentBlock(&lpEnvhDuplicateTokenfalse))

                 {

                  printf("Error create env block (0x%x)\n"GetLastError());

                 }

                 else

                 {

                    if (!CreateProcessAsUser(hDuplicateTokencmdlinecmdlinenullptrnullptrfalse, 0, nullptrnullptr, &startInfo, &procInfo))

                    {

                       printf("Error CreateProcessAsUser (0x%x)\n"GetLastError());

                    }

                    else

                  {

                         printf("[+] CreateProcessAsUser: %d\n"procInfo.dwProcessId);

                    }

                    DestroyEnvironmentBlock(lpEnv);

                 }

                 CloseHandle(hDuplicateToken);

                 CloseHandle(hToken);

              }

           }

        }

        else // -- (2)

        {

           if (CreateProcess(cmdlinecmdlinenullptrnullptrFALSE, 0, nullptrnullptr, &startInfo, &procInfo))

           {

              printf("[+] CreateProcess: %d\n"procInfo.dwProcessId);

                CloseHandle(procInfo.hThread);

              CloseHandle(procInfo.hProcess);

           }

           else

           {

              printf("Error creating process: %d\n"GetLastError());

           }

        }

 

        RpcRevertToSelf();

    }

    else

    {

       printf("Error impersonating user\n");

    }

}

上面的代码是典型的RPC服务器,由于客户端是由服务器模拟的,导致进程创建例程存在提权漏洞

正如您在TestCreateProcess()函数的序言中所看到的,服务器首先会模拟调用者进程——通常情况下,这个RPC API是由权限较低的客户端应用程序调用的,因为剩余的操作将在调用者环境中执行。

标记(1)所在行的代码表示调用者希望以中等完整性级别运行notepad.exe。实际上,这个分支语句用于演示服务器在模拟客户端时创建外部进程的正确方法。

有趣的部分位于标记(2)所在行的代码,它向我们展示了服务器在模拟调用者的情况下创建进程的错误方法。从标记(2)处开始的代码只是调用CreateProcess()接口。但是问题在于,由于进程创建API的调用者是服务器,因此,新进程将从服务器而非客户端那里继承其令牌,而不管其模拟状态如何。

如果结合臭名昭著的符号链接攻击,攻击者就可以劫持用户-驱动器映射,从而将默认的根驱动器(例如C:)重定向到攻击者控制的任意位置。这样,服务器进程就可以通过CreateProcess()应用编程接口以提升后的权限运行一个伪造的目标可执行文件(就本例而言为notepad.exe)。

利用上面介绍的信息,我们可以在Windows中寻找具有类似模式的漏洞代码。一般来说,这种方法与前一篇文章中介绍的方法非常相似。就这里来说,我们需要找到在服务器模拟客户端的情况下,可以通过CreateProcess或ShellExecute接口运行外部进程的Windows组件。如果您有使用Python编写静态PE解析器脚本的经验的话,那么这些任务应该不在话下。否则的话,也可以编写IDA脚本来完成这些任务。在本文中,我们将这项任务作为练习留给读者。

通过这些脚本,我们将获得一个由可能容易受到进程创建劫持攻击的Windows组件组成的候选名单。在那些入围的Windows组件中,我们发现两个存在这种漏洞代码模式的Windows服务——即NcaSvc.dll和rascustom.dll。

CVE-2019-1287:网络连接助手服务漏洞

Network Connectivity Assistant不仅可以用于查看当前连接状态,而且还可以用来收集有助于排除DirectAccess连接故障的详细信息。NCA服务是从Windows 8开始与客户端操作系统集成的。

Figure 1: Windows 10 Enterprise with DirectAccess Enabled

图1:启用了DirectAccess功能的Windows 10企业版

DirectAccess是Windows 10企业版和教育版提供的一种功能。我们可以将该功能看作为一种VPN技术,可在客户端计算机连接到Internet时为其提供Intranet连接。仅当客户端计算机从Windows Server连接到域控制器时,才能启用DirectAccess功能。换句话说,计算机连接到Internet后,DirectAccess连接就会立即自动连接。

实际上,NCA向客户端应用程序会公开一批RPC接口。在我们的调查过程中,我们发现其中的一个RPC API,即Rpc_NcaExecuteAndCaptureLogs,很容易受到进程创建劫持的影响,因为模拟的客户端在客户端计算机上收集网络诊断信息时,会调用外部可执行文件powershell.exe。实际上,Rpc_NcaExecuteAndCaptureLogs会运行多个PowerShell可执行文件来收集网络诊断信息;其中一个PowerShell可执行文件,不仅会通过模拟令牌调用CreateProcessAsUser,还会调用CreateProcess。

对于这个漏洞来说,我们是在NcascriptLogsStartProcess函数中为后者创建进程的代码中发现的。我们不清楚创建这两个进程时为什么使用了完全相同的命令行参数;也许为了收集到更全面的诊断信息,工程师选择了在没有模拟令牌的情况下运行具有管理员权限的PowerShell命令。

需要说明的是,该漏洞现已修复,并赋予了相应的编号,即CVE-2019-1287

Figure 2: Exploiting DirectAccess Local Privilege Escalation through fake PowerShell

图2:通过伪造的PowerShell利用DirectAccess功能实现本地提权

远程访问连接管理器的自定义协议引擎

远程访问连接管理器是一项Windows服务,负责管理从客户端计算机到Internet或其他远程网络的拨号和VPN连接,而rascustom.dll则是该服务的核心组件。默认情况下,Windows 10系统将启用该服务。

有趣的是,我们在rascustom.dll中找到了与NcaSvc.dll中相同的代码模式。换句话说,如果我们可以利用这里的LPE漏洞,那么危害会更大,因为所有Windows 10都会受到影响。但是,没有管理员权限的用户无法调用由rascustom.dll公开的RPC应用程序接口Rpc_VpnProEngExecuteAndCaptureLogs。通常,RPC服务器在允许调用者访问该API的主要操作之前,会在RPC API函数的入口点执行调用者访问权限检查。我们没有深入研究绕过访问权限检查的方法,因为我们认为难度太大了,但只要访问权限检查到位且不可篡改,我们就可以任务该服务不会受到进程创建劫持的影响。

小结

在本文中,我们为读者详细介绍了如何利用建模技术挖掘具有相同模式的漏洞代码,希望能够对大家的工作和学习有所帮助。此外,对于本文中介绍的漏洞的详细信息,请参阅微软发表的2019年9月安全公告

本文由secM整理并翻译,不代表白帽汇任何观点和立场
来源:https://www.fortinet.com/blog/threat-research/another-local-privilege-escalation-lpe-vulnerability.html

最新评论

昵称
邮箱
提交评论