Watch-PerfCounter

Just a little Powershell I wrote when I had nothing better to do.  It's kinda' reminiscent of procdump's ability to wait until a certain threshold is crossed before it takes action.  That's what this Cmdlet does.  It just watches a performance counter (any performance counter, also works against remote computers,) and when the specified threshold is crossed, it executes whatever file you specified in the "Action" parameter.  I wrote it because it helps solve performance-related cases where a repro is hard to catch.

#
<#
.SYNOPSIS
Watches a Windows performance counter, and executes the specified file
once the specified counter threshold is crossed.
.DESCRIPTION
Watches a Windows performance counter, and executes the specified file
once the specified counter threshold is crossed. You must know the 
name and path of the performance counter you're after. Use Get-Counter
if you want to explore the syntax of performance counter paths. You
may monitor performance counters on a remote machine as well. The
specified Action can be any executable file, an exe, a vbs, a bat file,
etc. You can also specify the PollFrequency, in seconds. Default is 5.
Use the Verbose switch for extra detail.
.PARAMETER CounterName
This is the full path of the performance counter that you want to monitor.
It may be from the local machine or from a remote machine.
.PARAMETER Threshold
When the performance counter crosses this threshold, the specified action will be triggered. 
It could be an absolute value or it could be a percentage, depending on the perf counter.
.PARAMETER Action
This can be any file that exists and is accessible - it will be executed when the performance
counter threshold is crossed.
.PARAMETER PollFrequency
The frequency, in seconds, that the performance counter will be polled. Default is 5 seconds.
.EXAMPLE
C:\PS> Watch-PerfCounter -CounterName '\processor(_total)\% processor time' -Threshold 90 -Action 'C:\MyFile.bat' -PollFrequency 10 -Verbose
Polls the processer time performance counter every 10 seconds, and executes MyFile.bat once the CPU is over 90%. Outputs Verbose information.
.EXAMPLE
C:\PS> Watch-PerfCounter -CounterName '\\host02\memory\% committed bytes in use' -Threshold 25 -Action 'C:\Compress.exe'
Polls the memory perf counter on remote computer HOST02, and executes a program once it crosses 25%.
.NOTES
Powershell written by Joseph Ryan Ries, but it was Justin Turner's idea.
#>
Function Watch-PerfCounter
{
    #Version: 01.00 - September 17 2015 - Initial release.
    #Version: 01.01 - September 17 2015 - Added the word "seconds" to the sentence "Polling ever x seconds."
    [CmdletBinding()]
    Param([Parameter(Mandatory=$True)]
            [String]$CounterName,
          [Parameter(Mandatory=$True)]
            [Decimal]$Threshold,
          [Parameter(Mandatory=$True)]
          [ValidateScript({Test-Path $_ -PathType Leaf})]
            [String]$Action,
          [Parameter(Mandatory=$False)]
          [ValidateRange(1,360)]
            [Int]$PollFrequency = 5)
    BEGIN
    {
        Set-StrictMode -Version Latest
        [Diagnostics.Stopwatch]$Stopwatch = [Diagnostics.Stopwatch]::StartNew()

        Write-Verbose "$($PSCmdlet.CommandRuntime) beginning on $(Get-Date)."

        Try
        {
            Get-Counter $CounterName -ErrorAction Stop | Out-Null
            Write-Verbose "Performance counter $CounterName was found."
        }
        Catch
        {
            Write-Error "Perfermance counter was not found or could not be read! `n`n $($_.Exception.Message)"
            Return
        }

        Write-Verbose "Polling every $PollFrequency seconds."
        Write-Verbose "Use Ctrl+C to abort."


        [Bool]$ThresholdCrossed = $False

        While (-Not($ThresholdCrossed))
        {
            Try
            {
                $CookedValue = (Get-Counter $CounterName -ErrorAction Stop).CounterSamples.CookedValue

                Write-Verbose "$CounterName = $CookedValue"

                If ($CookedValue -GT $Threshold)
                {
                    Write-Verbose "Performance counter $CounterName has crossed the threshold of $Threshold!"
                    $ThresholdCrossed = $True
                }
            }
            Catch
            {
                Write-Error "Error reading performance counter. This error may be transient. `n`n $($_.Exception.Message)"
            }

            If (-Not($ThresholdCrossed))
            {
                Start-Sleep -Seconds $PollFrequency
            }
        }

        Invoke-Expression $Action
    }
    PROCESS
    {

    }
    END
    {
        # Reminder: This code block still executes even if we return prematurely from the BEGIN block.
        $Stopwatch.Stop()
        Write-Verbose "$($PSCmdlet.CommandRuntime) completed in $([Math]::Round($Stopwatch.Elapsed.TotalSeconds, 2)) seconds."
    }
}

Poking Around DNS Scavenging Settings with Powershell

I've been working toward getting DNS scavenging turned on in a domain.  DNS scavenging, as you may know, takes a good deal of patience and forethought.  It's not something you want to just blindly enable without doing any reconnaissance first.  First off, since I'm new to this environment, let me scan all the domain controllers (which are also the DNS servers in this case) and see what the scavenging and aging settings currently look like:
 
#
$Servers = @()
ForEach ($DC In Get-ADDomainController -Filter *)
{
  $Server = New-Object PSObject -Property @{ 
     Hostname   = $DC.HostName
     Scavenging = $((Get-DnsServerScavenging `
                        -ComputerName $DC.HostName).ScavengingState)
     Aging      = $((Get-DnsServerZoneAging `
                        -Name 'acme.com' `
                        -ComputerName $DC.HostName).AgingEnabled)
    }
  $Servers += $Server
}

$Servers | FT -AutoSize
#
Hostname Scavenging Aging
-------- ---------- -----
DC01     False      True
DC02     False      True
DC03     False      True
DC04     False      True
DC05     False      True

So record aging is already turned on for the zone.  All that's left to do is enable scavenging on one of the DNS servers. (I don't like having all of the domain controllers scavenging - just one.)  But before I do that, I want to wait a while (like, a couple weeks) and see what hosts are updating their DNS records and which ones aren't. Lucky us - DNS server has a WMI provider.

#
$Records = Get-WmiObject 
              -Namespace 'Root\MicrosoftDNS' 
              -Query 'SELECT * FROM MicrosoftDNS_ResourceRecord WHERE Timestamp != 0'

$Records | Select TextRepresentation, `
           @{n='Timestamp'; e={([DateTime]'1/1/1601').AddHours($_.Timestamp)}} `
         | Where Timestamp -LT (Get-Date).AddDays(-30) | FT -AutoSize

The only tough bit is that the record's timestamp comes as a 32-bit integer that represents the number of hours elapsed since January 1st, 1601.  So you'd want to convert that into a meaningful date.  Now we can see which resource records in DNS aren't refreshing themselves on a regular basis.  After checking that list for sanity and correcting any problems, we can turn on scavenging. 

TinyWebRedirector

I wrote something this weekend.

TinyWebRedirector v1.0 - Redirects HTTP Requests
Copyright (C) 2015 Joseph Ryan Ries
www.myotherpcisacloud.com

Usage:
Install:   TinyWebRedirector -install
Uninstall: TinyWebRedirector -uninstall

I wrote this micro web server for all the sysadmins out there who have an internal Active Directory that shares the same DNS name as their public domain name. Let's say your internal AD domain name is contoso.com. Your public website is also contoso.com. In this scenario, internal employees at the office cannot reach your public website by entering http://contoso.com into their web browsers, because contoso.com internally resolves to the IP address of one of your AD domain controllers. This has lead to messy solutions, such as installing IIS on each domain controller, for the sole purpose of redirecting requests on port 80 to www.contoso.com. But installing IIS on your domain controllers is not a great idea.

TinyWebRedirector is more suited to this purpose because:

  • It is tiny. The image file is 110KB, and runs with a ~2.6MB working set. It requires no redistributable DLLs.
  • It does one thing and one thing only. This translates to a much smaller potential attack surface than a large web server such as IIS.
  • It is written in C, and so does not require .NET. Will run on any Windows machine Vista/2008 or greater.
  • The listening port (default 80) and the URL to redirect visitors to is configurable in the registry at HKLM\SYSTEM\CurrentControlSet\Services\TinyWebRedirector. Restart the service for changes to take effect.
  • The service runs as Local Service. This is a much safer configuration than services that run as Local System.

Please let me know if you find any bugs or weaknesses.

Github repository is here.

The compiled and signed x64 binary is here:

TinyWebRedirector.exe (112.8KB)

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.

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!