Verifying a [DATETIME] format string is valid or not with Confirm-DateTimeFormatPattern

Early this evening I sent this tweet out regarding a #PowerShell Function I wrote called Confirm-DateTimeFormatPattern and Dan Franciscus (@dan_franciscus) recommend that I should blog about it, so here it goes!

To elaborate on that tweet, this Function spawned from a larger initiative that I am working on for a presentation I’m giving at PowerShell Summit 2018 in April.  That presentation will be about a PowerShell Module I have built called PSLogging, which is a logging framework built using PowerShell Classes.

One of the items on my to-do list, which always seems to grow and not shrink, is to add a configuration component.  This configuration component would allow someone to create a config.json or config.yml file that would be referenced when logging within their application/script/function/etc.  Here is a quick example of what the configuration may (work in-progress) look like:

Within this configuration file I would allow someone to specify the “log level” and all the available formatting options.  As you can see in the image above, one such configuration option is that ability to specify the datetime format, a filename, encoding, etc.  But the point of this post is specifically about the datetime format option.

I needed (wanted) a way to identify if the provided format string in the datetime configuration option is an actual valid [DATETIME] format string value.  If you are not familiar with this concept, then please see the below example when using the Get-Date Cmdlet:

PS /Users/josh.rickard> Get-Date -Format "yyyy-MM-dd HH:mm:ss"
2018-02-11 22:43:05

To further explain, I will show you a snippet of PowerShell code and the corresponding Class that does exactly what I described above and it may shed some light on the “why”. Imagine you have a JSON configuration file, but it has multiple options available and only certain options are “mandatory”. You would first need to load the content from that JSON file and then convert it using the ConvertFrom-Json Cmdlet. The next step is that you would need to iterate through the configuration and identify (and verify) each piece of configuration before you rely on it, right? Here’s an example of just that:

$JsonConfigurationFile = 'C:\_Github\PSLogging\PSLogging\config\logging.json'
$JsonConfig = Get-Content -Path $JsonConfigurationFile | ConvertFrom-Json
$FormatObject = @()

foreach($output in $JsonConfig.outputs)
{
    if ($output.PSObject.Properties.Name -eq 'console')
    {
        if ([DateTimeValidation]::validateDateTimePatterns($output.console.datetime))
        {
            [ConsoleConfiguration]::setConsoleConfiguration($output.console.level, $output.console.datetime)
        }
        else
        {
            Write-Error -Message 'Error!  Could not Validate Date Time Patterns!'
            Throw
        }
    }
}

As you can see, we loop through the first time and identify if the console setting was set, if it was then we would need to check and see if the passed in [datetime] format string is correct, if it is then set our console configuration (if you’re following along, let me tell you there are other forms of validation this is just an example). 🙂

Now, to show you the [DateTimeValidation] Class that is doing all the magic. Additionally, I have a ConsoleConfiguration Class and a LogLevel Enum but we won’t get into the details of those at this time:

enum LogLevel
{
    INFO
    DEBUG
    SUCCESS
    WARNING
    ERROR
}

Class DateTimeValidation
{
    static [string] $DateTimePattern

    static [bool] validateDateTimePatterns([string]$Pattern)
    {
        $TimePatterns = [System.Globalization.DateTimeFormatInfo]::CurrentInfo

        foreach ($item in $TimePatterns.GetAllDateTimePatterns())
        {
            if ($Pattern -eq $item)
            {
                [DateTimeValidation]::DateTimePattern = $Pattern
                return $true
            }
        }
        return $false
    }
}

# Define a class
class ConsoleConfiguration
{
   static [LogLevel] $LogLevel
   static [DateTimeValidation] $DateTimeFormat

   # Static method
   static [void] setConsoleConfiguration([LogLevel]$Level, [string]$DateTimeFormat)
   {
        $DateTimeFormat
        [ConsoleConfiguration]::LogLevel = $Level
        [ConsoleConfiguration]::DateTimeFormat = $DateTimeFormat
   }
}

To briefly explain, we call the [DateTimeValidation] Class from within our If statement above. This call then validates that our passed in [string] format is valid (or not) based on the current Culture/Globalization settings. Once the provided string data has been validated, then we can proceed to add it to our [ConsoleConfiguration] Class. The great thing about this class is that I can just reuse it, over and over and over.

Now, let’s get back to the actual function and not all this Class non-sense. I ported over the logic from a Class in my PSLogging Module because I thought it would be useful to validate this type of input. In #PowerShell, especially Advanced Functions, we learn/preach/teach that we should always validate our inputs and ensure our outputs are what the user expects – this is crucial in any language. If you are familiar with Parameter Validation on PowerShell Functions then this should come at no surprise to you. We should ALWAYS validate our data!

This function is no different. You may not have a use case for it now, and this is NOT revolutionary at all, but there may come a time when you need to validate those pesky string formatting rules and I hope this function helps out.

You can find the GIST here: https://gist.github.com/MSAdministrator/b2c3ace163c4fbf76069503528de6cf1

And the full code here for the lazy:

.Synopsis
    Confirm if a date time format pattern is valid or not 
.DESCRIPTION
    Confirm if a date time format pattern is valid or not     
    based on the current culture on the machine that this function     
    is being ran on 
.EXAMPLE 1 
    PS C:\> Confirm-DateTimeFormatPattern -Format "yyyy-MM-dd HH:mm:ss"

    Format              Valid
    ------              -----
    yyyy-MM-dd HH:mm:ss  True

.EXAMPLE 2
    PS C:\> Confirm-DateTimeFormatPattern -Format "yyyy-MM-dd HH:mm:ss", "xxx-MM/dd/yy"

    Format              Valid
    ------              -----
    yyyy-MM-dd HH:mm:ss  True
    xxx-MM/dd/yy        False

.EXAMPLE 3
    PS C:\> "yyyy-MM-dd HH:mm:ss", "xxx-MM/dd/yy", "MMMM d", "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK" | Confirm-DateTimeFormatPattern

    Format                                 Valid
    ------                                 -----
    yyyy-MM-dd HH:mm:ss                     True
    xxx-MM/dd/yy                           False
    MMMM d                                  True
    yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK  True
function Confirm-DateTimeFormatPattern
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([PSCustomObject])]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [string[]]$Format
    )

    Begin
    {
        $CultureObject = ([System.Globalization.DateTimeFormatInfo]::CurrentInfo).GetAllDateTimePatterns()
    }
    Process
    {
        foreach ($item in $Format)
        {
            if ($CultureObject -contains $item)
            {
                $props = [PSCustomObject]@{
                    'Format' = $item
                    'Valid'  = $true
                }
                Write-Output $props
            }
            else
            {
                $props = [PSCustomObject]@{
                    'Format' = $item
                    'Valid'  = $false
                }
                Write-Output $props
            }
        }
    }
    End
    {
        # Intentionally Left Blank
    }
}
Advertisements

Manage User Rights with Group Policy

Group Policy is nothing but flexible and extremely powerful when it comes to both configuration management and installation of software.  In addition, Group Policy is one of your best tools for securing your endpoints.  You can manage anything and everything from Firewall rules, account privileges, application white-listing, etc.  You can also manage user rights as well.

Computers and Users in your environment have a lot of rights, by default, that they don’t need.  Using Group Policy, we can manage these privileges and start to lock-down our environments without making it burdensome for our end users.  One such privilege is the ability to Log on Locally.

Use Group Policy to define accounts or services that are allowed to log on locally
Use Group Policy to define accounts or services that are allowed to log on locally

By default, anyone that has the correct username and password can log on locally to a system, but do you really need to log on to a server in your datacenter using a local account?  Some of you might, in this case you should explicitly allow certain accounts to have this privilege.  Instead of using the default setting of “if you have a local account you can log in to this system”, but instead restrict this at a global level.  This ensures that your systems are properly managed and no one was being lazy or malicious by creating a one-time account they forgot about.

Use Group Policy to define accounts or services that are not allowed to log on locally
Use Group Policy to define accounts or services that are not allowed to log on locally

Let’s take the opposite approach.  Do you have systems on your network(s) that should only be logged on by specific individuals, accounts, services, etc?  Instead of relying on plainly on the security of your username and password, we should restrict this right to the specific accounts, computers, users, etc. that should have access. Only then, if they have the correct credentials are they allowed to login over the network.

Use Group Policy to allow access to computers from the network
Use Group Policy to allow access to computers from the network

By restricting this access to only the certain machines or users within a specific group, someone who has either found a set of credentials or has somehow infiltrated your network will only be able authorize a set of systems because others have been restricted.

Another example is to manage who has access to Log on Remote Desktop Services permissions.  Does everyone need to access a set of systems (this goes for both workstations and servers)?  I bet not, so why just enable RDP (Remote Desktop Protocol) for anyone who has valid credentials?  We should tightly restrict who has access to our systems, especially when using RDP.

Use Group Policy to allow or deny access to Remote Desktop Services
Use Group Policy to allow or deny access to Remote Desktop Services

For all situations regarding user rights and access to systems, we should always restrict access as much as possible.  The key is to create these restrictions without being burdensome for your end-users.  There is a fine line, but we should always attempt to be reasonable with this approach.  Some systems just shouldn’t be accessed over RDP or locally, this will be up to the discretion of your organizations risk.  Providing clear guidance and allowing your management to either enable these restrictions or to accept the risk is all that we as IT professionals can do.

 

Cattle vs. Unicorns

When organizations begin to think of users as cattle instead of unicorns we begin to remove their pride for, and their responsibility to, an organization.  When responsibility for their actions are only out of necessity or self-preservation then you have lost the battle, but not necessarily the war.  You can change people’s mindset but it just may take a little more effort.

I believe that people are the answer to most security problems.  Empowering people by making them part of your security team enhances their awareness and fosters a sense of shared responsibility.  Organizations that encourage (and consistently preach) a shared responsibility will have continual communication and awareness of their responsibilities in order to protect themselves and their fellow employees.  Those organizations that treat employee’s as another expense (or cattle) will push their employees away which allows them to disassociate themselves from their responsibility.  You have now created more resistance, and ultimately your security team has another force fighting against them – and not with them.

Do you really want to be another statistic?

Cattle organizations encourage people to use their creativity in an unproductive way.  The results include employees who attempt to bypass your security measures, instead of identifying and reporting potential incidents.  Most organizations have warmed up to the idea of phishing simulations (I hope your organization has) to provide an immersive and lifelike training experience.  Some cattle organizations have gone as far as to let go of employees that fail these tests.  By taking this approach, you have now forced your employees to either report everything, report nothing, or use creative ways to fight against the security team instead of for it.  Employees in a cattle organization will begin to use their creativity to bypass their training by creating Outlook rules to detect these simulations, or completely ignoring (caring) about a new face walking through the halls.  The first question that should come to your mind is, why are my employees doing this.  The second is identifying and preventing these (negative) creative ideas within your environment.

Are you a Batman Unicorn?

Unicorns are majestic and beautiful creatures.  Organizations and security teams that believe, and routinely encourage, their employees have equal (shared) responsibility for the protection of their organization, their data, and ultimately their job, will obtain an army of security agents to constantly watch for suspicious activity both digitally and physically.  Our security teams now have more actionable intelligence to do their jobs successfully.

Just imagine, if everyone (Finance, HR, System Administrators, Shipping, etc.) in your organization is encouraged to protect themselves, their fellow employees, and their organization from suspicious email, people, system configurations, and so on.  If you have worked in security for any amount of time you know that the best information about problems or incidents comes from employees.  Unicorn organizations create a culture of responsibility – a stake-hold of sorts – which enables a form of peer pressure to report and communicate any suspicious activity.  I don’t know about you, but I would rather believe in unicorns than step in cow shit.

Understanding Group Policy order

Group Policy order can be confusing. To understand how exactly Windows applies one GPO (Group Policy Object) versus another, you can use the “LSD OU” rule.

You should always ask yourself two questions when dealing with Group Policy:

  1. Where are you (local, site, domain, or organizational unit)?
  2. What are you (computer or user)?

Read More

PowerShell Phishing Response Toolkit (PPRT)

Yesterday I gave a talk at ShowMeCon in St. Louis regarding PPRT.  I also gave this talk at CircleCityCon, but had some technical issues. 🙂  I wanted to write this quick post to share out my PowerPoint Slides from this presentation.  If you have any questions about PPRT, please reach out via this blog or create an issue on my GitHub page: https://github.com/MSAdministrator/PPRT—PowerShell-Phishing-Response-Toolkit

Enjoy!

Slides: PowerShell Phishing Response Toolkit