SharpTLSScan v1.2

Edit March 16, 2015: Updated to v1.4.

SharpTLSScan has been upgraded to version 1.2.  You can download the executable from this post, or you can find the source on Github.

Version 1.2 adds some visual enhancements, such as having the protocol versions grouped together instead of interleaved in the output.  This is because each separate protocol scan (SSLv3, TLS1.0, TLSv1.1, TLS1.2,) executes on a separate thread, (the SSLv2 scan is different than the others so it runs by itself,) and each thread would be sending output to the screen simultaneously, causing the results from separate threads to be mixed together.  Now we wait until all threads are finished and display the output all once, meaning the results are grouped together properly again.  As a consequence of this, I added a little "Working..." status indicator so that the user doesn't feel like the program is just hung while it's performing its tests.

The one thing that I would really like to add in the future, is something to the certificate validation callback routine that can check for the existence of a discrete signature algorithm in the certificate. (Such as "sha256NoSign")  Certutil.exe -dump can do it just fine, but I have not yet figured out how to emulate the behavior from C#.

Download v1.4

Powershell Code That Literally Writes Itself - Automaception

I amused myself with Powershell today and thought I'd share. I might have also named today's post, "Write-Host does have a use after all!"

Today, I had the task of synchronizing the country attributes of thousands of users from a non-Microsoft LDAP server into multiple Active Directories.  This non-Microsoft LDAP server stored the country attribute ("c" in LDAP parlance) of each user as an ISO 3166 Alpha-3 three-letter abbreviation.  I wanted to convert that into the ISO 3166 alpha-2 two-letter notation before I imported it into Active Directory, as well as fill out the rest of the country-related attributes at the same time.

As most AD administrators know, when you want to programmatically set a user's country, you have to make the change in three different places if you want to be thorough.  You have to change co (Text-Country,) c (Country-Name,) and countryCode (Country-Code.)  Those three fields are a free-form text entry, an ISO-3166 A2 or A3 abbreviation, and a numeric value, respectively.

So the first thing I do is track down the ISO 3166 list.  It looks like this:

AALAND ISLANDS                                  AX      ALA     248
AFGHANISTAN                                     AF      AFG     004
ALBANIA                                         AL      ALB     008
ALGERIA                                         DZ      DZA     012
...

And on and on... for 240 countries.

I was thinking I'd want a Switch statement in my script... the idea is that I Switch($x) where $x is the three-letter country abbreviation that came from the LDAP server. Visions flashed through my mind of me staying up all night writing this monotonous switch block with 240 cases in it.  And then I thought, "Hey, Powershell is the most powerful automation framework there is. Surely I can use it to automate the automation!"  And so I set out to have my Powershell script literally write itself.

First, save that ISO 3166 country code list to a text file. That the list is already in a fixed-width format is going to make this extremely simple.

$Countries = Get-Content C:\Users\Ryan\Desktop\3166.txt

Write-Host "Switch (`$Transaction.NewValue.ToUpper().Trim())" #E.g. 'USA' or  'BEL'
Write-Host "{"
Foreach ($Line In $Countries)
{
    [String]$CountryCode = $Line[64] + $Line[65] + $Line[66]
    [String]$ThreeLetter = $Line[56] + $Line[57] + $Line[58]
    [String]$TwoLetter   = $Line[48] + $Line[49]
    [String]$FreeForm    = $Line.Substring(0, 46).Trim().Replace("'", $Null)

    Write-Host "    '$ThreeLetter'"
    Write-Host "    {"
    Write-Host "        Set-ADUser -Identity `$Transaction.ObjectGUID -Replace @{ countryCode = $CountryCode } -ErrorAction Stop"
    Write-Host "        Set-ADUser -Identity `$Transaction.ObjectGUID -Replace @{ co = '$FreeForm' } -ErrorAction Stop"
    Write-Host "        Set-ADUser -Identity `$Transaction.ObjectGUID -Replace @{ c = '$TwoLetter' } -ErrorAction Stop"
    Write-Host "    }"
}
Write-Host "    Default { Write-Error `"`$(`$Transaction.NewValue) was not recognized as a country.`" }"
Write-Host "}"

When I ran the above script, it printed out all the code for my massive switch block, so that all I had to do was copy the text out of the Powershell window, and paste it into the middle of the script I was working on.  It came out looking like this:

Switch ($Transaction.NewValue.ToUpper().Trim()) #E.g. 'USA' or  'BEL'
{
    'ALA'
    {
        Set-ADUser -Identity $Transaction.ObjectGUID -Replace @{ countryCode = 248 } -ErrorAction Stop
        Set-ADUser -Identity $Transaction.ObjectGUID -Replace @{ co = 'AALAND ISLANDS' } -ErrorAction Stop
        Set-ADUser -Identity $Transaction.ObjectGUID -Replace @{ c = 'AX' } -ErrorAction Stop
    }
    #
    # ... 238 more countries ...
    #
    'ZWE'
    {
        Set-ADUser -Identity $Transaction.ObjectGUID -Replace @{ countryCode = 716 } -ErrorAction Stop
        Set-ADUser -Identity $Transaction.ObjectGUID -Replace @{ co = 'ZIMBABWE' } -ErrorAction Stop
        Set-ADUser -Identity $Transaction.ObjectGUID -Replace @{ c = 'ZW' } -ErrorAction Stop
    }
    Default { Write-Error "$($Transaction.NewValue) was not recognized as a country." }
}

And there we have it.  Now I don't have any employees from Burkina Faso, but it's nice to know that I'd be able to classify them if I did.  Now with all the time I saved myself from having to type all that out by hand, I figured I'd write a blog entry about it.

SharpTLSScan v1.1

The v1.0 post is here.

A few minor improvements.

  • Caught a couple of previously unhandled exceptions.
  • Improved the certificate subject and issuer visualization to handle commas embedded within quotation marks.
  • Added a color-coded legend to the help text briefly describing what red, yellow and green text mean.
SharpTLSScanv1.1.zip (14.7KB)

SharpTLSScan v1.0

Update 08/13/2014: v1.1 is here.

SSL and TLS have been getting a lot of attention from me lately, and recently I found myself in want of a tool that would tell me precisely which protocol versions and cipher suites a given server supported.

Sure, there's SSLScan and SSLScan-Win, but those tools haven't been updated in 5 years, and thus don't support the newer versions of TLS, 1.1 and 1.2.  And of course there are nice websites like SSL Labs that do a fine job, but I wanted to use this tool to audit internal/private systems too, not just internet web servers.

So I created a new tool and called it SharpTLSScan.  It's pure C# and has no reliance on outside libraries (such as OpenSSL,) and I managed to avoid the pain of directly interfacing with the SChannel API as well.

SharpTLSScan comes with the "It works on my machine (tm)" guarantee.  It's free, and the source will probably show up on Github pretty soon.

Here are some screenshots:

Usage is simple - SharpTLSScan myhost:636

First, the server's certificate is inspected and validated.  Next, a default connection is negotiated, which is useful for seeing what kind of connection your system would negotiate on its own.  Then, all protocol versions and all cipher suites are tested to see what the server will support.  (This can take a couple of minutes.)  Things that are obviously good (such as the certificate validating) are highlighted in green, while things that are obviously bad (such as SSL v2 support) are highlighted in red.  Things that are fair, but not great, (such as MD5 hashes) are in yellow.


*Oh dear...*

The reason why the protocol versions seem interleaved is a side-effect of a the multithreading in the program.  I'll likely fix it in the next update.

Here you go:

SharpTLSScan.zip (14.3KB)

Override Write-Debug to Avoid 80-Character Word-Wrapping in Transcript

I was writing some long, complicated Powershell scripts recently, and complicated scripts call for plenty of tracing/debug logging.  These scripts were to be run as scheduled tasks, and it was important that I saved this debug logging information to a file, so I figured that instead of re-inventing the wheel, I'd just use Start-Transcript in conjunction with Write-* cmdlets to automatically capture all the output.

This almost worked fine, except that the lines in the resulting transcript file were word-wrapped at 80 characters.  This made the transcripts ugly and hard to read.  The only thing worse than reading debug logs while trying to diagnose a subtle bug is having to do it when the debug logs are ugly and hard to read.

Hm, would it help if I resized the Powershell host window and buffer at the beginning of the script, like so? 

$Host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size 180, 3000
$Host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.Size 180, 50

No, that did not help.

As it turns out, the Write-Host cmdlet does not exhibit the same word-wrapping limitation as its Write-Debug and Write-Warning brethren.  So instead of changing the 900 Write-Debug cmdlets already in our script, let's just "override" the Write-Debug cmdlet:

Function Global:Write-Debug([Parameter(Mandatory=$True, ValueFromPipeline=$True)][String]$Message)
{
    If ($DebugPreference -NE 'SilentlyContinue')
    {
        Write-Host "DEBUG: $Message" -ForegroundColor 'Yellow'
    }
}

Ah, no more needless word wrapping in my transcripts now.  Just be careful about putting stuff in the Global scope if you're working with other actors in the same environment.