NTHashTickler_C v1.0

Last time, I unveiled my multithreaded brute force NTLM hash cracker written in C#.  However, I was not very happy with the performance... specifically the scalability... it has some pretty massive concurrency issues that are apparent when executing on a machine with lots of processors.  For instance, the program barely ran any faster on a machine with 8 CPUs than it did on a machine with 4 relatively slower CPUs.  There is obviously a big threading issue there causing a problem with diminishing returns. 

Now don't get me wrong - I love C#, but I had a hunch that if I wanted to be serious about performance I needed to go native.  So I spent about a day porting it to standard C, and now both single-threaded and mult-threaded performance is about seven times better than in C#.  Again, I'm not bashing C#, it's just that different types of problems call for different types of tools.  And like I said last time, I realize there already several other apps out there that do this, but I've taught myself a lot of C doing this, which makes it worth it to me.

As usual, you can view the source on Github if you're interested.


NTHashTickler

And to go along with my Get-MD4Hash Powershell cmdlet that I wrote last time, here is a multithreaded brute-force NT hash cracker written in C#.  I call it NTHashTickler:

https://github.com/ryanries/NTHashTickler


First of all, I realize that there are already many tools out there that already do this exact same thing - and they do a much better job at it that I have been able to do so far.  However, I wrote this tool as a learning experience for myself to exercise some basic speed and multithreading concepts in C#.  How many threads it will use equals the number of logical processors in your machine.  It uses Microsoft's native BCrypt implementation of the MD4 hashing algorithm.  I use the .NET framework's RandomNumberGenerator class to get my randomness, which is a lot slower than the basic Random class, but I sacrificed speed in this instance for better random numbers. I use memcpy straight out of the C runtime for comparing byte arrays, which I do not think can be beat in terms of speed.  However, I'm sure that there are still some massive opportunities for improvements both in terms of speed and in coming up with a more clever algorithm for generating random plain-texts other than just dumb randomness.

Get-MD4Hash

Greetings, Earthlings.

Today was a pretty lazy Sunday, in which I was throwing around some ideas involving pass-the-hash attacks and Windows password hashes.  Nothing ground-breaking there, but during my research, I realized that there's very little out there for just simply generating an NT hash from a password, which if you're reading this you probably already know is just the MD4 hash of the UTF16 little endian-encoded password.  There is nothing built into the .NET framework for computing MD4 hashes at all, which is a shame when you think of how simple and easy it is in comparison to generate MD5, SHA1, SHA2, etc., hashes in .NET.  It's easy to understand why there are no .NET commodities for the MD4 algorithm though, since it is ancient and horribly broken from a security standpoint.  I even thought about implementing MD4 myself from scratch using nothing but RFC 1320, but it wasn't that important to merit all that work.  This is the type of thing I just wanted to bang out on a Sunday afternoon without much bother... not a long, full-fledged project.  And of course there are some C, C++, Python, etc. implementations floating around out there, but I didn't see anything specifically for .NET or for Powershell that was just too easy to ignore, so I decided to write something.

I knew Microsoft already had a perfect implementation of MD4 lying around... no reason to re-invent that wheel.  It's in bcrypt.dll, right next to a bunch of other much better algorithms.  (But I need MD4 ... for... research...)     I wrote this in C#, but I ported it to Powershell because I think Powershell fits better on this blog.  I call it Get-MD4Hash, and it simply returns the MD4 hash of whatever byte array you feed it.  If you specifically want to make it an NTLM hash, make sure you feed it a string that is encoded in Unicode (wide characters.)  I verified its accuracy by giving my real workstation password to the cmdlet, then I dumped the SAM on my machine and verified that it's the exact same hash.

Function Get-MD4Hash
{
<#
.SYNOPSIS
    This cmdlet returns the MD4 hash of the data that is input.
    WARNING: MD4 is not secure, so it should NEVER be used to 
    protect sensitive data. This cmdlet is for research purposes only!

.DESCRIPTION
    This cmdlet returns the MD4 hash of the data that is input.
    WARNING: MD4 is not secure, so it should NEVER be used to 
    protect sensitive data. This cmdlet is for research purposes only!
    This cmdlet uses Microsoft's implementation of MD4, exported 
    from bcrypt.dll. The implementation is fully compliant with
    RFC 1320. This cmdlet takes a byte array as input, not a string.
    So if you wanted to hash a string (such as a password,) you 
    need to convert it to a byte array first.

.EXAMPLE
    Get-MD4Hash -DataToHash $([Text.Encoding]::Unicode.GetBytes("YourPassword1!"))

.PARAMETER DataToHash
    A byte array that represents the data that you want to hash.

.INPUTS
    A byte array containing the data you wish to hash.

.OUTPUTS
    A 128-bit hexadecimal string - the MD4 hash of your data.

.NOTES
    Author: Ryan Ries, 2014, ryan@myotherpcisacloud.com

.LINK
    https://myotherpcisacloud.com
#>
    [CmdletBinding()]
    Param ([Parameter(Mandatory=$True, ValueFromPipeline=$False)]           
           [Byte[]]$DataToHash)
    END
    {        
        Set-StrictMode -Version Latest
        Add-Type -TypeDefinition @'
        using System;
        using System.Text;
        using System.Runtime.InteropServices;
        public class BCrypt
        {
            [DllImport("bcrypt.dll", CharSet = CharSet.Auto)]
            public static extern NTStatus BCryptOpenAlgorithmProvider(
                [Out] out IntPtr phAlgorithm,
                [In] string pszAlgId,
                [In, Optional] string pszImplementation,
                [In] UInt32 dwFlags);

            [DllImport("bcrypt.dll")]
            public static extern NTStatus BCryptCloseAlgorithmProvider(
                [In, Out] IntPtr hAlgorithm,
                [In] UInt32 dwFlags);

            [DllImport("bcrypt.dll", CharSet = CharSet.Auto)]
            public static extern NTStatus BCryptCreateHash(
                [In, Out] IntPtr hAlgorithm,
                [Out] out IntPtr phHash,
                [Out] IntPtr pbHashObject,
                [In, Optional] UInt32 cbHashObject,
                [In, Optional] IntPtr pbSecret,
                [In] UInt32 cbSecret,
                [In] UInt32 dwFlags);

            [DllImport("bcrypt.dll")]
            public static extern NTStatus BCryptDestroyHash(
                [In, Out] IntPtr hHash);

            [DllImport("bcrypt.dll")]
            public static extern NTStatus BCryptHashData(
                [In, Out] IntPtr hHash,
                [In, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
                [In] int cbInput,
                [In] UInt32 dwFlags);

            [DllImport("bcrypt.dll")]
            public static extern NTStatus BCryptFinishHash(
                [In, Out] IntPtr hHash,
                [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbInput,
                [In] int cbInput,
                [In] UInt32 dwFlags);

            [Flags]
            public enum AlgOpsFlags : uint
            {            
                BCRYPT_PROV_DISPATCH = 0x00000001,
                BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x00000008,
                BCRYPT_HASH_REUSABLE_FLAG = 0x00000020
            }

            // This is a gigantic enum and I don't want to copy all of it into this Powershell script.
            // Basically anything other than zero means something went wrong.
            public enum NTStatus : uint
            {
                STATUS_SUCCESS = 0x00000000
            }
        }
'@

        [Byte[]]$HashBytes   = New-Object Byte[] 16
        [IntPtr]$PHAlgorithm = [IntPtr]::Zero
        [IntPtr]$PHHash      = [IntPtr]::Zero
        $NTStatus = [BCrypt]::BCryptOpenAlgorithmProvider([Ref] $PHAlgorithm, 'MD4', $Null, 0)
        If ($NTStatus -NE 0)
        {
            Write-Error "BCryptOpenAlgorithmProvider failed with NTSTATUS $NTStatus"
            If ($PHAlgorithm -NE [IntPtr]::Zero)
            {
                $NTStatus = [BCrypt]::BCryptCloseAlgorithmProvider($PHAlgorithm, 0)
            }
            Return
        }
        $NTStatus = [BCrypt]::BCryptCreateHash($PHAlgorithm, [Ref] $PHHash, [IntPtr]::Zero, 0, [IntPtr]::Zero, 0, 0)
        If ($NTStatus -NE 0)
        {
            Write-Error "BCryptCreateHash failed with NTSTATUS $NTStatus"
            If ($PHHash -NE [IntPtr]::Zero)
            {
                $NTStatus = [BCrypt]::BCryptDestroyHash($PHHash)                
            }
            If ($PHAlgorithm -NE [IntPtr]::Zero)
            {
                $NTStatus = [BCrypt]::BCryptCloseAlgorithmProvider($PHAlgorithm, 0)
            }
            Return
        }

        $NTStatus = [BCrypt]::BCryptHashData($PHHash, $DataToHash, $DataToHash.Length, 0)
        $NTStatus = [BCrypt]::BCryptFinishHash($PHHash, $HashBytes, $HashBytes.Length, 0)

        If ($PHHash -NE [IntPtr]::Zero)
        {
            $NTStatus = [BCrypt]::BCryptDestroyHash($PHHash)
        }
        If ($PHAlgorithm -NE [IntPtr]::Zero)
        {
            $NTStatus = [BCrypt]::BCryptCloseAlgorithmProvider($PHAlgorithm, 0)
        }
        
        $HashString = New-Object System.Text.StringBuilder
        Foreach ($Byte In $HashBytes)
        {
            [Void]$HashString.Append($Byte.ToString("X2"))
        }
        Return $HashString.ToString()
    }
}

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

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)