Tuesday, April 12, 2016

Active Directory ACL's explained

In a previous post on decoding AD ACL's, I provided some code which took BSonPosh's get-adacl output and decoded the SID's and GUID's to help provide more readable output.  The example of this is below, however you may find some of the other fields to be a bit confusing.  So I created a few different types of test permissions on an OU to show how they are reflected in the Powershell output of these two commands.


Permission set in GUI: "Apply to: All Descendant objects, create/delete Conference Site objects"

ActiveDirectoryRights :  CreateChild, DeleteChild
InheritanceType       :     Descendents
ObjectType            :      msExchConferenceContainer
InheritedObjectType   :   00000000-0000-0000-0000-000000000000
ObjectFlags           :       ObjectAceTypePresent
AccessControlType     : Allow
IdentityReference     :    TEST.LOCAL\Nathan
IsInherited           :        False
InheritanceFlags      :    ContainerInherit
PropagationFlags      :   InheritOnly

Permission set in GUI:  "Apply to: This object and all descendant objects, create/delete Contact objects"

ActiveDirectoryRights : CreateChild, DeleteChild
InheritanceType       :    All
ObjectType            :     contact
InheritedObjectType   : 00000000-0000-0000-0000-000000000000
ObjectFlags           :      ObjectAceTypePresent
AccessControlType     : Allow
IdentityReference     :     TEST.LOCAL\Nathan
IsInherited           :        False
InheritanceFlags      :    ContainerInherit
PropagationFlags      :   None

Permission set in GUI:  "Apply to: This object only, create/delete Computer Objects"

ActiveDirectoryRights : CreateChild, DeleteChild
InheritanceType       :    None
ObjectType            :     computer
InheritedObjectType   : 00000000-0000-0000-0000-000000000000
ObjectFlags           :     ObjectAceTypePresent
AccessControlType     : Allow
IdentityReference     :    TEST.LOCAL\Nathan
IsInherited           :        False
InheritanceFlags      :    None
PropagationFlags      :  None

Permission set in GUI:   "Apply to: Descendent Computer objects, Modify Owner"

ActiveDirectoryRights : WriteOwner
InheritanceType       :    Descendents
ObjectType            :     00000000-0000-0000-0000-000000000000
InheritedObjectType   : computer
ObjectFlags           :      InheritedObjectAceTypePresent
AccessControlType     : Allow
IdentityReference     :    BHI-MASTER\adminlinlnat
IsInherited           :        False
InheritanceFlags      :    ContainerInherit
PropagationFlags      :  InheritOnly

InheritedObjectType:  Notice this will be all zero's when the permission is for creating a child object in a container.  When it is permissions being set on a specific type of child objects, then it will be set that that object type, and the ObjectType value will be all zero's.  When setting a permission on a specific property of a specific type of child object, you will get both fields filled in with the ObjectType being the specified property, and InheritedObjectType being the AD object's type.

PropagationFlags: InheritOnly exists when applying to something other than the current OU.  (https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.propagationflags(v=vs.110).aspx)

InheritenceFlags: ContainerInherit when applying to anything below the current level, ObjectInherit when applying to child objects (https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.inheritanceflags(v=vs.110).aspx)

InheritanceType: All (everything from this level down), Descendents (children and descendants, not the current object), None (current level only)   (https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectorysecurityinheritance(v=vs.110).aspx)

Wednesday, December 16, 2015

Tis the season for vacation clearing (and password expiration)

As we approach the end of the year, along with its holidays, its common for many employees to take leave for long periods of time.  So as a gift to helpdesks everywhere, often they would request to know who will have their password expire during the peak holiday times (to prepare for the support calls).  To build a list like this is quite easy with powershell.  This assumes you don't have fine grain password policies.  In this example, we look at expiring passwords between Dec 21 and Jan 4 given the working days and anticipated return dates around the Christmas and New Years holidays:

import-module activedirectory

#grab the domain wide password policy and extract a # of days integer
$passwordage = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge |select -exp days

#define your start and end filter dates and subtract the max Password age value.
#We need to calculate using passwordlastset timestamps

$startdate = ([datetime]"12-21-2015").adddays(-$passwordage)
$enddate = ([datetime]"1-4-2016").adddays(-$passwordage)

#Filter as much as possible on the LDAP side with the date ranges.
#The Select statement includes a calculated
#expression to convert the passwordlastset value to an actual expiration date.
#Convert to CSV and output to file.
#Zip it and mail it out.

get-aduser -filter {(enabled -eq $true) -and (passwordlastset -ge $startdate) -and (passwordlastset -le $enddate)} -Properties passwordlastset, mail | select samaccountname, name, mail, @{name="ExpirationDate"; exp={$_.passwordlastset.adddays($passwordage)}} | convertto-csv -notypeinfo | out-file .\expiringholidays.csv

Tuesday, December 15, 2015

Checking Mcafee DAT version information remotely with powershell

This script can be used to remotely check a Mcafee client's datversion number and the date of the DAT file's release. It uses basic remote registry reading techniques, so remote registry access (usually administrator on the remote machine) will be required for it to work. You can use this script as a template for other registry reading operations that you may need. It is a basic script that only accepts single computer input. You can change parameters and turn this into a function to handle pipeline or array input.

Update July 2018. Added a secondary registry key in due to some differences I'm seeing in my environment lately. With different product versions and EPO use, this location may move around a bit. If you find this code doesn't work for you, check the current dat version in the taskbar icon, then search the registry for the dat version number. This should get you to the location that your product is storing this information in.


param (
 [parameter(mandatory=$true)][string]$computername
)
function ping-host([string]$computername) {
 #This function will perform a simple, small size single packet ping of a machine and return true/false for the result
  if ([string]::IsNullOrEmpty($computername) ) {return $false}
  #ping first for reachability check
  $po = New-Object net.NetworkInformation.PingOptions
  $po.set_ttl(64)
  $po.set_dontfragment($true)
  [Byte[]] $pingbytes = (65,72,79,89)
  $ping = new-object Net.NetworkInformation.Ping
  $savedEA = $Erroractionpreference
  $ErrorActionPreference = "silentlycontinue"
  $pingres = $ping.send($computername, 1000, $pingbytes, $po)
  if (-not $?) {return $false}
  $ErrorActionPreference = $savedEA
  if ($pingres.status -eq "Success") { return $true } else {return $false}
}


if ((ping-host $computername) -eq $false) {
 New-Object PSobject -Property @{
  Computername = $computername
  DATVersion = "System Not Online"
  Datdate = $null
 }
} else {

 try {
  #Set up the key that needs to be accessed and what registry tree it is under
  $key = "Software\McAfee\AVEngine"
  $type = [Microsoft.Win32.RegistryHive]::LocalMachine

  #open up the registry on the remote machine and read out the TOE related registry values
  $regkey = [Microsoft.win32.registrykey]::OpenRemoteBaseKey($type,$computername)
  $regkey = $regkey.opensubkey($key)
  $status = $regkey.getvalue("AVDatVersion")
  $datdate = $regkey.getvalue("AVDatDate")
 } catch {
  try {
   $key = "Software\Wow6432Node\McAfee\AVEngine"
   $type = [Microsoft.Win32.RegistryHive]::LocalMachine
   #open up the registry on the remote machine and read out the TOE related registry values
   $regkey = [Microsoft.win32.registrykey]::OpenRemoteBaseKey($type,$computername)
   $regkey = $regkey.opensubkey($key)
   $status = $regkey.getvalue("AVDatVersion")
   $datdate = $regkey.getvalue("AVDatDate")
  } catch {
     #try newer registry location
    try {
     $key = "Software\Wow5432Node\Network Associates\ePolicy Orchestrator\Application Plugins\VIRUSCAN880"
     $regkey = [Microsoft.win32.registrykey]::OpenRemoteBaseKey($type,$computername)
     $regkey = $regkey.opensubkey($key)
     $status = $regkey.getvalue("DATVersion")
     $datdate = $regkey.getvalue("DatDate")
    } catch {
      $status = "Cannot read regkey"
    }
  }
 }
 New-Object PSobject -Property @{
  Computername = $computername
  DATVersion = $status
  DatDate = $datdate
 } |select Computername,DatVersion,DatDate
}

Thursday, December 3, 2015

Powershell Switch for a numeric range

There are several ways of handling a range of values in a Switch statement in powershell. In this example, I will show a simple conversion of a numeric month value to a quarter [string].

First example:
switch($month){
 {1..3 -contains $_}{"Q1"}
 {4..6 -contains $_}{"Q2"}
 {7..9 -contains $_}{"Q3"}
 {10..12 -contains $_}{"Q4"}
 
}


The range operator returns an array of the numbers in that specified range, so we use -contains to test our value. If we left out -contains, all switch values would match as no comparison is being made, yet the range is being evaluated as true.

Second example:
switch($month) {
 {$_ -le 3} {"Q1";break;}
 {$_ -le 6} {"Q2";break;}
 {$_ -le 9} {"Q3";break;}
 {$_ -le 12} {"Q4"; break;}
}


The key to this example is the Break statement. Without this, everything would match as the switch operator continues to evaluate for all possible matches (not just the first one). Break will cause it to stop evaluating.

Third example:

switch($month) {
 {$_ -ge 1 -and $_ -le 3} {"Q1"}
 {$_ -ge 4 -and $_ -le 6} {"Q2"}
 {$_ -ge 7 -and $_ -le 9} {"Q3"}
 {$_ -ge 10 -and $_ -le 12} {"Q4"}

}


The more "wordy" example with multiple conditions and the -And. This may be more readable, however the first example does a pretty good job with this. The functionality is far more obvious than example #2. In any case, using conditions to check a range of values will save time and code. Alternatively (though not very elegant or practical), you can use a regex switch for this:

switch -regex ([string]$month) {
 "^[1-3]$" {"Q1"}
 "^[4-6]$" {"Q2"}
 "^[7-9]$" {"Q3"}
 "^1[0-2]$" {"Q4"}
}