SHA-256 for Code Verification in VB.NET

I’ve got a problem. I’m staring at possible security issues with the the current software I’m writing. The nature of the software means that it has a higher than normal chance of being exposed to things that aren’t healthy for it. It’s a .NET utility, and there doesn’t seem to be a whole lot said out there on a list of considerations for hardening my .NET assemblies.

Basically, I’ve seen the following three high-level variants talked about with respect to .NET applications:

  1. The generation and signing of strongly-named assemblies.
  2. Obfuscate the code into a tangle that even die-hard dis-assembly won’t easily pick apart ( via tools like  Dotfuscator, CryptoObfuscator etc.)
  3. Sign the shipped assemblies with a certificate verified by a  trusted Certificate Authority.

Now, strongly-named assemblies should only be considered if you’re going to register your assembly with the GAC, which you should only do if you’re considering having a number of other assemblies that rely on it calling into the same (shipped only once) library. I’ve got no interest in going down the GAC registration path. In fact, I’m shipping all the assemblies that the bootstrap executable needs as resources within that executable, which conveniently side-steps the entire issue of whether I’m calling the assemblies I think I’m calling. Option one is thus a non-starter in this instance.

I’ll grant that obfuscation of the code would surely interfere with black-hat activities around dis-assembly and re-assembly with malicious additions. Then again, they’ve got to navigate a dis-assembly that implements Inversion of Control, where all the “meat” is in those assemblies packed up as resources, along with runtime behaviour that relies on reflection to assemble the final product.  Frankly, the script-kiddie end of the Black-hat community  are already way, way outside their league in grokking what I’ve done, even with original class names available.

So we’re clear, option 2 is still appealing, but I’m under the gun, and none of the three obfuscators I’ve tried seem to enjoy my trick with reflection to completely side-step falling into needing a dependency injection framework. Old memories of obfuscation, Java and reflection suggest that I may be trying to  push a large boulder uphill whist wearing a wet-suite primed with butter. For now, I’ll leave a deep groking of option 2 until I have some time to explore.

Now, Option 3 involves time and money, both of which this project seems to be short on.  Upon some deep naval-gazing over this, it turns out that I’m not that concerned about whether it’s me doing the verification of the programming deployed or trusted some third party, at least not for the first delivery.

Because I’m not concerned (and my end-users are admittedly oblivious to software security issues), I can save the client a bunch of money, and me a bunch of time by taking a leaf from the Linux world, and ship the final product with a checksum that allows the client to guarantee that what I delivered is what they have installed. 

MDA-5 and SHA1 are no longer hot given recent security discoveries. SHA-256 hasn’t been proven to be vulnerable, so I’ve settled on generating an SHA-256 key that verifies the content of the one and only assembly I ship.

As my user-base will number in the hand-full, and admit openly that they are not technophiles, I’ve kept it simple for them.  On their about screen, I have my code calculate the SHA256 key on itself, and report that to the user. At least in terms of ensuring that they have an un-modified executable, they can check that key against the one supplied in the install instructions.

The SHA-256 key, calculated by the entry assembly on itself upon dialog display.

SHA-256 key, calculated by the entry assembly on itself upon dialog display.

I had to do a bit of digging to put together how to do that, but it turns out to be pretty straightforward:

Imports System.IO
Imports System.Reflection
Imports System.Security.Cryptography
Imports System.Text

Public Module CodeHardeningCollection

  Public Function RetrieveEntryAssemblySHA256AsString() As String
    Return ByteArrayToHumanReadableString(
      RetrieveSHA256([Assembly].GetEntryAssembly)
    )
  End Function

  Public Function RetrieveSHA256(ByRef thisAssembly As [Assembly]) As Byte()
    Dim mySHA256 As SHA256 = SHA256Managed.Create()
    Dim hashValue() As Byte

    Using stream = File.OpenRead(thisAssembly.Location)

      hashValue = mySHA256.ComputeHash(stream)

      stream.Close()
    End Using

    Return hashValue
  End Function

  Public Function ByteArrayToHumanReadableString(ByVal array() As Byte) As String
    Dim bytesAsString As New StringBuilder()
    For byteIndex As Integer = 0 To array.Length - 1
      bytesAsString.Append(
        String.Format(
          "{0:X2}", array(byteIndex)
        )
      )
      If byteIndex Mod 4 = 3 Then ' splits output into 4-char blocks to aid in chunking.
        bytesAsString.Append(" ")
      End If
    Next ' byteIndex
    Return bytesAsString.ToString()
  End Function 'PrintByteArray

End Module

And wallah, the executing assembly will report all alterations made to it through a change in its calculated SHA-256 key.

However, it can still be spoofed via malicious code replacing the call to that “key calculation code” with a straight write-out of the original “shipping” key. That scenario though has us in the territory of an attack deliberately targeting this particular software with a clear intent to circumvent the software’s extant security features. If an attacker is that determined, I’m working on the principle that they’d probably pick first from the plethora of houses out there with their front doors wide open.

An unspoofed SHA-256 key can still be reported via a separated third-party tool such as openssl, so I’ll be recommending that as a more secure approach to checking they have the right software in the install instructions.

Deriving the assembly SHa25 key via OpenSSL

Deriving the assembly SHa25 key via OpenSSL

Why not just suggest the 3rd-party tool checking right off the bat and call it a day? Because from what I know of their operation, it’s unlikely that they’d have the technical skill to go there. At least this gives them somewhere to start if we need to verify if software has been compromised.

There we go. Basic .NET assembly security on a tight time-budget.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s