Bulk Modification of Active Directory ACLs with Powershell

The other day, I encountered an Active Directory-related problem.  The security model on the organizational units in this particular domain was quite complicated, arising from the multi-tenant nature of this domain and its "List Object Mode" configuration. (I've talked about List Object Mode before.)

First, a little background.  Imagine you're looking at the OUs in Active Directory Users & Computers:

CONTOSO.COM
   |
   + Builtin
   + Computers
   - Customers_OU
        |
        + Customer1_OU
        + Customer2_OU
        + Customer3_OU
        + Customer4_OU
        + ...

To cut to the chase, there was a problem with the ACL on each individual OU beneath Customers_OU (and there were hundreds!) where an access control entry (ACE) had been applied directly to each OU that allowed "Authenticated Users" the generic read permission.  This was essentially undermining the inheritance of permissions from parent object to child object, and allowing an account in Customer1_OU to view the contents of Customer2_OU, and vice versa.

*The siren noise from Kill Bill plays here.*

I'm glossing over some of the details, but basically what needed to happen, was for me to enumerate over each and every one of those hundreds of individual customer OUs, and remove those <not inherited> ACEs.

There was no way I was going to do that by hand, through the GUI.

So I scripted it.

Import-Module ActiveDirectory
Set-Location AD:\
$AllOUs = Get-ADOrganizationalUnit -Filter * `
             -SearchBase 'OU=CUSTOMERS_OU,DC=CONTOSO,DC=COM' `
             -Properties * -SearchScope OneLevel

Foreach ($OU In $AllOUs)
{
    $ACL = Get-ACL $OU.DistinguishedName
    
    Foreach ($ACE In $ACL.Access)
    {
        If (($ACE.IdentityReference -EQ 'NT AUTHORITY\Authenticated Users') -AND `
            ($ACE.IsInherited -EQ $False))
        {            
            $ACL.RemoveAccessRule($ACE)
            Set-ACL -AclObject $ACL $OU.DistinguishedName -Verbose
        }
    }
} 

That would have taken hours if done manually.  Hours that I'd rather spend playing GTA V.

Keeping My Sysinternals Tools Up To Date

Microsoft Sysinternals (primarily Mark Russinovich, but with occasional help from his buddies like Andrew Richards, Thomas Garnier, etc.,) has put out a ton of very useful Windows tools and utilities over the years. If you spend any time at all on Windows computers, you undoubtedly know and love these tools.

The tools are updated quite regularly, too.  And I hate having out of date stuff.  So, I wrote a little bit of Powershell this morning that will automatically scan my local folder to see which Sysinternals tools I have in that folder (like C:\Program Files\Sysinternals\,) then compare them with the latest and greatest versions of the executables on the internet (it's WebDAV,) and update my local copy if necessary.  I just run this script as a scheduled task once a week.

Set-StrictMode -Version Latest

[String]$EventSource    = 'UpdateSysinternals'
[String]$LocalDirectory = 'C:\Program Files\SysInternals\*.exe'
[String]$WebDAVShare    = '\\live.sysinternals.com\Tools'
[Int]$FilesUpdated      = 0

If (-Not([System.Diagnostics.EventLog]::SourceExists($EventSource)))
{
    Try
    {
        New-EventLog -LogName Application -Source $EventSource -ErrorAction Stop
        # MSDN says you shouldn't immediately use a new source after creating it.
        Start-Sleep -Seconds 10
        Write-EventLog -LogName Application -Source $EventSource -EntryType Information ` 
             -EventId 1 -Message 'Event source created.'
    }
    Catch
    {
        Exit
    }
}

$LocalFiles = Get-ChildItem $LocalDirectory

Try
{
    New-PSDrive -Name S -PSProvider FileSystem `
           -Root $WebDAVShare -ErrorAction Stop | Out-Null
}
Catch
{
    Write-EventLog -LogName Application -Source $EventSource -EntryType Error `
          -EventId 2 -Message "Failed to connect to $WebDAVShare!"
    Exit
}

Foreach ($LocalFile In $LocalFiles)
{    
    $RemoteFile = Get-ChildItem "S:\$($LocalFile.Name)"
    If ([Version]$RemoteFile.VersionInfo.ProductVersion -NE [Version]$LocalFile.VersionInfo.ProductVersion)
    {
        Write-EventLog -LogName Application -Source $EventSource -EntryType Information `
             -EventId 3 -Message "Remote file $($RemoteFile.Name) has version $($RemoteFile.VersionInfo.ProductVersion), which does not match local version $($LocalFile.VersionInfo.ProductVersion)."
        Copy-Item $RemoteFile.FullName $LocalFile.FullName -Force
        $FilesUpdated++
    }
}

Remove-PSDrive S

Write-EventLog -LogName Application -Source $EventSource -EntryType Information `
     -EventId 4 -Message "$FilesUpdated file(s) were updated."

Removing the 'Protect object from accidental deletion' Flag From A Bunch of OUs

I was recently conducting an experiment that involved creating a ton of Organizational Units in Active Directory.  Typically, when new OUs are created, they have a flag set on them that says "Protect object from accidental deletion."

This is normally fine, as you wouldn't want an admin to accidentally delete an entire OU full of users and computers.  But I had created a large hierarchy of OUs to conduct an experiment, and now that it was done, I wanted to get rid of all the OUs.  Like, immediately.  And I didn't want to sit in the AD Users & Computers GUI and uncheck that box on every OU, one at a time, so that it would let me delete them.

$AllOUs = Get-ADObject -Filter "ObjectClass -EQ 'organizationalUnit'" `
          -SearchBase 'OU=TestOUs,DC=CONTOSO,DC=COM' -Properties *

Foreach ($OU In $AllOUs)
{
    Set-ADOrganizationalUnit $OU.DistinguishedName `
          -ProtectedFromAccidentalDeletion $False
}

Now I can just delete the base of the tree and they'll all vanish.

UNC Hardening

A couple months ago, Microsoft published a couple of Windows patches to address some vulnerabilities found in the way that Windows machines access UNC paths over the network.

MS15-011

MS15-014

Guidance on Deployment of MS15-011 and MS15-014 by AskPFE Platforms

This is essentially another man-in-the-middle style SMB hijack, and these types of attacks have been well-known for a long time, maybe second only behind pass the hash stuff.  One of the countermeasures that we admins have had for years to help combat these sorts of SMB proxy attacks, is SMB signing:

Of course I'd recommend enabling this everywhere - on both domain controllers and domain members - but that's no longer quite enough.  Security researchers found a way of bypassing or disabling SMB signing, which is what prompted Microsoft to release those two security patches I mentioned above.  One of those hotfixes comes with a new Group Policy configuration setting, called UNC Hardening.

You can find this new setting in Computer Configuration > Policies > Administrative Templates > Network > Network Provider:

So keep in mind that just applying the patch alone doesn't award you any of the benefits of Hardened UNC Paths.  There is additional GPO configuration you must do to enable it.

In the GPO, an admin would specify the types of UNCs that he or she wanted to harden, so that when a client connects to a UNC that matches a certain pattern, that client applies additional security policies to that connection.

Wildcards are supported, but you must supply either a server name or share name, so no, you cannot do \\* or \\*\*.

To get the two most important UNC paths in an Active Directory domain, you'd configure the GPO thusly:

\\*\NETLOGON  RequireMutualAuthentication=1, RequireIntegrity=1
\\*\SYSVOL    RequireMutualAuthentication=1, RequireIntegrity=1

This additional layer of security costs very little, relative to the benefit of ensuring all your Windows clients will only connect to genuine, mutually authenticated domain controllers to get their Group Policies and logon scripts.  Especially if you have mobile clients on the go that connect from coffee shops and hotels!