Windows PowerShell cmdlets to secure PSCredential Objects

I have previously discussed securing credentials using Windows Powershell atPowershell: Securing credentials using the PSCredential class. In this article, I will discuss a number of cmdlets I have created to secure credentials using a Advanced Encryption Standard (AES) encryption key to retrieve the content from a encrypted standard string.

As I am using an encryption key and storing the information in a content file, I will be using ACLs on the NTFS filesytem to control access. Alternative methods could be to store the encryption key in a database or use a certificate to control access to the item. Also, in practice I will store the encryption key on a remote file server.

Firstly, we need to create the encryption key using the ‘New-EncryptionKey’ cmdlet to which we use the RNGCryptoServiceProvider class to generate a random byte array for the encryption length. By default, the cmdlet using a 32-byte array to support the AES 256-bit encryption length. The cmdlet also supports using 128-bit and 192-bit encryption lengths which a 16-byte and 24-byte array. The random byte array for the specified encryption length is generated and sends the output to a file which will be the encryption key content.

Once the content has sent to the output file, the content of random byte array is then removed from the current session.

# Creates an AES 256-bit encryption key at the location D:\Output\Keys\mykey.key
New-EncryptionKey -Output D:\Output\Keys\mykey.key 

# Creates an AES 192-bit encryption key at the location D:\Output\Keys\mykey.key
New-EncryptionKey -Bytes 24 -Output D:\Output\Keys\mykey.key

We have now created an encryption key so that we may now convert the secure string for a credential object password using the specified encryption key and sends the output to a password file using the ‘New-EncryptedString’ cmdlet. The cmdlet will retrieve the content of the specified file containing the encryption key and from the stored credential objects convert the secure string of the password to an encrypted standard string and send the output to a file and clear the content of the stored encryption key from the current session.

New-EncryptedString -KeyFile D:\Output\Keys\mykey.key -PasswordFile D:\Output\Passwords\mypassword.txt 

Finally we want to retrieve the credential object to use as a variable to pass to a subsequent cmdlet which will require authentication. The content of the password file is retrieved and converted to a secure string using the content of the encryption key and stored as a password variable and passed to the PSCredential class to represent a set of security credentials and return the object. For subsequent cmdlets I can use the ‘$Password.GetNetworkCredential().Password’ property value for authentication from the PSCredential object.

$Password = Get-PSCredentialObject -Username administrator -KeyFile D:\Output\Keys\mykey.key -PasswordFile D:\Output\Passwords\mypassword.txt 

The cmdlets are available from the below:

New-EncryptionKey – https://github.com/dean1609/PowerShell/blob/master/Functions/New-EncryptionKey.ps1
New-EncryptedString – https://github.com/dean1609/PowerShell/blob/master/Functions/New-EncryptedString.ps1
Get-PSCredentialObject – https://github.com/dean1609/PowerShell/blob/master/Functions/Get-PSCredentialObject.ps1

Part Two – Computing email address hash value using Windows PowerShell

In part one I discussed computing the hash value using the MD5 hash algorithm for an email address, in this post I am going to discuss using other hash algorithms available in the HashAlgorithm Class to compute the value.

The available hash algorithms are MD5, RIPEMD160, SHA1, SHA256, SHA384 and SHA512 therefore in the advanced function we will set a parameter value which validates the input provided. By default, the hash algorithm of MD5 will be used unless specified.

[CmdletBinding()]
Param (
[Parameter(Mandatory=$True, ValueFromPipeline=$True, HelpMessage="Enter an email address to compute hash")]
[ValidateScript({If ($_ -match "\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b") {$True}
Else {Throw "The email address $_ is not a valid format."}})]
[String[]]$Email,
[ValidateSet('MD5','RIPEMD160','SHA1','SHA256','SHA384','SHA512')]
[String] $HashAlgorithm = "MD5"
)

The script block to compute the hash value does not differ from the example provided in the previous example which uses the StringBuilder Class .NET Framework object, other than we pass the hash algorithm parameter to the HashAlgorithm Class to compute the value.

Begin {} #Begin
Process
{
Write-Verbose ("The following email addresses have been specified " + ($Email -join ',') + ".")
ForEach ($String in $Email)
{
Write-Verbose ("Computing hash for the email address " + $String + " using the " + $HashAlgorithm + " algorithm.")
$StringBuilder = New-Object System.Text.StringBuilder
[System.Security.Cryptography.HashAlgorithm]::Create($HashAlgorithm).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))| ForEach-Object {
[Void]$StringBuilder.Append($_.ToString("x2"))} # ForEach-Object
return $StringBuilder.ToString()} # ForEach
} #Process
End {} #End
} # Function

Now be invoking the function and specifying a hash algorithm to compute the hash value I am able to use the default MD5 in the first instance and then each of the hash algorithms specified in the class.

Create-EmailHashPartTwoOutput

In the final part, I will look at using the  Blowfish-based hashing algorithm to compute the hash value and have an available advanced function and also provide the ability to specify not only an email address but a string value to compute the hash value.

Part One – Computing email address hash value using Windows PowerShell

I was recently discussing tracking email addresses and in particular protecting sensitive information and ensuring that the email address may not be re-used on deactivation. This got me thinking of how we could achieve this using Windows PowerShell.

As the advanced function required an email address I wanted to validate the  parameter specified for an email address so that is was valid. In the initial scenario I used a match based on regular expression as below. The regex pattern should match 99.9% of valid email addresses, but only allows for letters, digits and a couple of special characters. The official definition specified in RFC 5322 is far more complex

  (?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*
  |  "(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]
      |  \\[\x01-\x09\x0b\x0c\x0e-\x7f])*")
@ (?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?
  |  \[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}
       (?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:
          (?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]
          |  \\[\x01-\x09\x0b\x0c\x0e-\x7f])+)
     \])
[CmdletBinding()]
Param (
[Parameter(Mandatory=$True, ValueFromPipeline=$True, HelpMessage="Enter an email address to compute hash")]
[ValidateScript({"\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b"})]
[String[]]$Email
)

However, I found that this generated what could be considered as an unfriendly error exception message, if the validation of the patten was returned as ‘false’.

 Cannot validate argument on parameter 'Email'. The "$_ -match "\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b"" validation script for the argument with value "someone@where" did not return a result of True

Therefore I decided to modify the script block for the parameter validation to throw an error if the validation of the pattern was returned as ‘false’,

[CmdletBinding()]
Param (
[Parameter(Mandatory=$True, ValueFromPipeline=$True, HelpMessage="Enter an email address to compute hash")]
[ValidateScript({If ($_ -match "\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b") {$True}
Else {Throw "The email address $_ is not a valid email address format."}})]
[String[]]$Email
)

This would now return the error exception message:

The email address someone@where is not a valid email address format.

So, lets now look at computing the hash value for the string provided as a valid email address. By default, Windows PowerShell only provides the Get-FileHash cmdlet to compute the hash value for a file by using a specified hash algorithm.

In the initial instance I used the MD5 hash algorithm by creating a StringBuilder Class .NET Framework object  and invoking the HashAlgorithm Class to compute the hash value.

Begin {} #Begin
Process
{
Write-Verbose ("The following email addresses have been specified " + ($Email -join ',') + ".")
ForEach ($String in $Email)
{
Write-Verbose ("Computing hash for the email address " + $String+ ".")
$StringBuilder = New-Object System.Text.StringBuilder
[System.Security.Cryptography.HashAlgorithm]::Create("MD5").ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))| ForEach-Object {
[Void]$StringBuilder.Append($_.ToString("x2"))} # ForEach-Object
return $StringBuilder.ToString()
} # ForEach
} #Process
End {} #End

This would return the following output when computing the hash algorithm:

CreateEmailHashMD5

Now we are all aware that MD5 contains weaknesses and we should not be using this as hashing algothrim for sensitive information so I will look at using alternative hashing algorithms and the Blowfish-based hashing algorithm (one good thing they were doing at Ashley Madison!) in additional posts covering this topic.

 

Security – Part Five: Security Profile, Services and Firewall

When an ESXi host system is installed by default the firewall is enabled. All incoming and outgoing ports are blocked except the default TCP and UDP ports using for Management Services. The ESXi firewall protects the management interface of the ESXi host system, but provides no protection to virtual machines. By default, the following management services are open:

Management Service Description
lbtd (/sbin/net-lbtd) Load balancing teaming for distributed switches is an operation that evaluates the uplink load
vpxa (/usr/lib/vmware/vpxa/bin/vpxa) Virtual Center Agent to enable communication from the vCenter Server System to hostd service on the ESXi host system.
NTP Daemon (/sbin/ntpd) Network Time Protocol Daemon to synchronise the time between the ESXi host system and either a stratum or an atomic clock over the network
Direct Console UI (/sbin/dcui) Direct Console User Interface to enable the starting and stopping of the system and to perform limited setup, maintenance and troubleshooting tasks.
CIM Server (/bin/cimslp) Common Interface Model interface to monitor and manage health of the manager server hardware

In addition to the management services, by default the following ports are open for management access on the ESXi host system:

Port Description
22 SSH Server for incoming TCP
53 DNS client for incoming and outgoing UDP
68 DHCP client for incoming and outgoing UDP
161 SNMP server for incoming UDP
80 Fault tolerance for incoming TCP and outgoing TCP and UDP
427 CIM client SLPv2 to discover server for incoming and outgoing UDP
443 HTTPS access for incoming TCP
902 Host access and heartbeat for incoming and outgoing TCP and outgoing UDP
1234, 1235 vSphere replication for outgoing TCP
5988 CIM transactions over HTTP for incoming TCP
5989 CIM XML transactions over HTTPS for incoming and outgoing TCP
8000 vMotion requests for incoming and outgoing TCP
8100, 8200 Fault tolerance traffic for incoming and outgoing TCP and UDP

In order to protect the management interface the ESXi host system uses firewall rulesets to allow and disallow access to the ESXi host system. The default rulesets are defined initially in the read-only ‘/etc/vmware/firewall/service.xml’ file. To retrieve a list of firewall rule sets invoke the esxcli network firewall namespace.

esxli network firewall ruleset list

In addition to the firewall rulesets, more detailed information maybe retrieved:

esxli network firewall ruleset rule list

The ESX host system firewall ports and services can be made either using the vSphere Web Client, using the esxcli command line tool or using PowerCLI.

Configuring Firewall Service Properties Using vSphere Web Client

1) Select the ESXi host system to configure the Firewall service properties.

2) Browse to Manage > Settings > Security Profile > Services and select Edit.

Configure the ESXi Firewall Properties Using vSphere Web Client

1) Select the ESXi host system to configure the Firewall properties.

2) Browse to Manage > Settings > Security Profile > Firewall and Edit.

Configure the ESXi Firewall Using PowerCLI 

To retrieve information regarding the ESXi Firewall we can invoke both the Get-VMHostService and Get-VMHostFirewallException cmdlets.

1) For retrieving the status of services running on the ESXi host system, invoke the following:

Get-VMHost esxi1.dean.local | Get-VMHostService

2) To retrieve more detailed information for the incoming and outgoing service ports for each service invoke the following:

Get-VMHost esxi1.dean.local | Get-VMHostFirewallException | Where-Object {$_.Enabled}

By default, management services on the ESXi host system are enabled to provide local and remote client access and allowed through the firewall if the service ports are open. For Example, the syslog service is enabled on the ESXi host system but the firewall service ports are not allowing outbound connections. To modify the configuration using the esxcli network firewall namespace, perform the following steps.

1) Connect to the ESXi host system using an SSH client.

2) Retrieve the status of the syslog ruleset status.

esxcli network firewall ruleset list | grep syslog
syslog false

3) Enable the syslog firewall service.

esxcli network firewall ruleset set -e true -r syslog

We may also disable the syslog firewall service by invoking the following using the esxcli network firewall namespace.

esxcli network firewall ruleset set -e false -r syslog

There may be certain scenarios where the you may need to add more services than those preconfigured on the ESXi host system, in order to perform this action we must create a configuration file in the directory ‘/etc/vmware/firewall’. In this example, I will configure a service named ‘DeanTestApplication’ to enable inbound connectivity on TCP service port 3210 where the default state will be disabled.

1) Connect to the ESXi host system using an SSH client.

2) Create the following file ‘/etc/vmware/firewall/deantestapplication.xml’, with the following configuration.

<! -- Firewall information for Dean Test Application -->
<ConfigRoot>
  <service>
    <id>DeanTestApplication</id>
    <rule id='0000'>
      <direction>inbound</direction>
      <protocol>tcp</protocol>
      <porttype>dst</porttype>
      <port>3210</port>
    </rule>
  </service>
</Config>

3) Save the file and reload the firewall, which will restart the services listed for the firewall.

esxcli network firewall refresh

Once the firewall has been reloaded we can use the vSphere Web Client to confirm if the firewall service has been loaded into memory and further configure the service. Alternatively, we can confirm this form the esxcli network firewall namespace:

esxcli network firewall ruleset list | grep DeanTestApplication
DeanTestApplication false

Security – Part Four: Enabling ESXi Lockdown Mode

To increase the security of an ESXi host system which is being managed by a vCenter Server system you can enable Lockdown Mode to restrict users from performing actions directly on an ESXi host using SSH or the ESXi shell. Also, users without the DCUI Access privelage will be restricted from accessing the DCUI. As Lockdown Mode restricts access all actions on managed ESXi host systems must be performed using a connection to the vCenter Server system using an account with necessary permissions.

Lockdown Mode is only available when an ESXi host system is managed by a vCenter Server and can be enabled and disabled using the following methods.

Configuring Lockdown Mode using the vSphere Web Client 

1) Select the ESXi host system you wish to configure Lockdown Mode.

2) Browse to Manage  > Security Profile > Lockdown Mode and select Edit

3) Enable or Disable the Lockdown Mode checkbox and select OK.

Configuring Lockdown Mode using the ESXi Shell Command Line

1) Connect to the ESXi host system using an SSH client.

2) To retrieve the status of Lockdown mode, invoke the following:

~ # vim-cmd -U dcui vimsvc/auth/lockdown_is_enabled
false

3) To enable Lockdown Mode, invoke the following:

~ # vim-cmd -U dcui vimsvc/auth/lockdown_mode_enter

4) To disable Lockdown Mode, invoke the following:

~ # vim-cmd -U dcui vimsvc/auth/lockdown_mode_exit

Configure Lockdown Mode Using the Direct Console User Interface

1) Connect to the ESXi host system using the DCUI.

2) Press F2 to customise the system.

3) Select Configure Lockdown Mode

4) Enable or Disable Lockdown Mode using the spacebar to toggle the configuration state.

5) Select OK.

Configure Lockdown Mode using PowerCLI

1) Connect to the ESXi host system using the Connect-VIServer cmdlet.

2) To retrieve the status of the Lockdown Mode, invoke the following using the Get-VMHost cmdlet.

Get-VMHost esxi1.dean.local | Select Name, @{N="Lockdown Mode";E={$_.ExtensionData.Config.adminDisabled}}

3) Invoke the following to enable Lockdown Mode using the Get-View cmdlet.

(Get-VMHost esxi1.dean.local | Get-View).EnterLockdownMode() | Get-VMHost | Select Name, @{N="Lockdown Mode";E=$_ExtensionData.Config.AdminDisabled}} 

4) Invoke the following to disable Lockdown Mode using the Get-View cmdlet

(Get-VMHost esxi1.dean.local | Get-View).ExitLockdownMode()