Jump to navigation

Poupou's Corner of the Web

Looking for perfect security? Try a wireless brick.
Otherwise you may find some unperfect stuff here...

Weblog

Back to treasure iSlaNd - follow the map until you hit a 0

Note: This is a direct follow-up to while (being(stubborn) + bruteforce < strongname) { !Sleep(); }

Well there are a lot of 1 on this map - which only makes it easier to follow. 0's are visible in three sections:

  • 4 bytes @ 0xd8 (the PE checksum)
  • 8 bytes @ 0x110 (the Security/Certificate directory - RVA and size)
  • 16 bytes at 0x1f0 (padding before first section)

The first two spots were expected because Authenticode® signatures are similar (see /mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs). The third one is the padding before the first section.

Add to this the actual signature (128 bytes starting, in our case, at position 0x250 - defined in "Partition II Metadata", section 24.3.3), which cannot be modified for validation but, obviously, isn't part of the assembly hash.

But this isn't yet the complete solution. The previous four sections may each either be excluded from the hash (like the first two are in Authenticode®) or zeroized before hashing. Well when there's more than one X on a treasure map you better be prepared to dig more than one hole. At this stage computing hashes (for all possibilities) is easy - but not enough. We need to compare our hash results with the "real" hash used for the signature - and you may find it meaningful to know that an RSA signature is actually an encrypted padded-hash ;-). As we know where the signature is (at 0x250) by using some PE tools or the documentation this should be easy, no? Well it turns out that Microsoft RSA's implementation, based on CryptoAPI, doesn't let you get the decrypted hash (RSACryptoServiceProvider.DecryptValue throws a NotSupportedException). But we're lucky because Mono has a managed RSA implementation - so we have an easy way to our decrypted hash (which is the last 20 bytes of the decrypted signature - the rest being the PKCS#1 padding).

Here is the decrypted hash: 46-65-DA-3D-2B-64-B0-CB-E4-63-60-C2-2D-42-09-9D-E7-02-C4-4F for the helloworld.exe assembly

After some more trials and errors (like using the map file, not the actual EXE file) I finally got a hit! The first two (checksum and security directory entry) are zeroized (unlike Authenticode®, while the last two (padding and signature) are skipped. Here's the source code:

static void Main(string[] args) { const byte marker = 0x00; byte[] data = ReadFile ("helloworld.exe"); // PE checksum data [0x00d8] = marker; data [0x00d9] = marker; data [0x00da] = marker; data [0x00db] = marker; // security directory data [0x0118] = marker; data [0x0119] = marker; data [0x011a] = marker; data [0x011b] = marker; data [0x011c] = marker; data [0x011d] = marker; data [0x011e] = marker; data [0x011f] = marker; SHA1 hash = SHA1.Create (); hash.Initialize (); hash.TransformBlock (data, 0, 0x1f0, data, 0); // skip padding (16 bytes) hash.TransformBlock (data, 0x200, 0x50, data, 0x200); // skip signature (128 bytes) hash.TransformFinalBlock (data, 0x2d0, data.Length - 0x2d0); Console.WriteLine (BitConverter.ToString (hash.Hash)); }

Using this treasured, and now publicly shared, knowledge I should now be able to re-sign/validate a simple assembly :-).

Wait a minute! How about delay signing ? There must be some things to do before signing, for the first time, an assembly.

Actually by using the same "map" trick it's easy to see the differences between a signature delayed assembly and a really signed assembly. Results are:

  • 2 bytes @ 0xd8 (parts of the PE checksum, the other 2 bytes were already 0)
  • 1 bytes @ 0x218 (the CLI header flags, which is part of the previous hash calculation, equals to 0x9 COMIMAGE_FLAGS_ILONLY | COMIMAGE_FLAGS_STRONGNAMESIGNED)
  • 128 bytes (1024 bits) at 0x250 (the signature, un-hashed)

That last part was easy, but two challenges remains: First mcs must output a delay signed assembly (I'll leave that to Miguel or other mcs gurus) and, second, generalizing the process (mainly testing) for more elaborate assemblies (including resources, Authenticode® signatures ...).


10/8/2003 22:42:05 | Comments

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.