Managing App.Config Integrity using Xml Digital Signatures

§ February 20, 2008 18:25 by beefarino |

Maintaining the integrity of your binaries is important.  After all, we've seen how simple it is for someone to crack open your assemblies and start muddling around, making your binaries do terrible ... unspeakable things.  Strong names help in this regard, as they are treated as a form of load-time validation.  But what about your application configuration file?  It's just sitting there, plain ol' XML, just begging to be tampered with.  Obviously we don't want to store any super-secrets in the app.config (and if we do, we certainly want to encrypt them), but there are times when the data in our app.config dramatically affects program behavior, and we just want to make sure the app.config can be trusted before we make any drastic decisions.

It is surprising to me that the .NET framework doesn't provide canned support for such app.config validation, especialy when there is a readily accessible solution...

XML Digital Signatures

The .NET framework provides a limited implementation of the XMLDSIG  (XML Digital Signature) specification.  In a nutshell, XMLDSIG specifies a procedure for calculating the digital signature of an XML node tree, and also provisions different ways of including the signature either with or external to the XML node tree.  The process can be summed up as follows:

  1. An XML node set is selected for signing.
  2. The XML node set is then canonicalized, which is a fancy way of saying that the node set is put into a standard representation.  For example, redundant XML namespace references are removed, and attributes are placed in alphabetic order.
  3. The canonical XML representation is then hashed using a standard hashing algorithm like SHA1 or MD5.
  4. The hash is signed using standard private/public key cryptographic techniques, resulting in the XML digital signature of the node set.
  5. The digital signature is either embedded in the document as a new XML element, or left out-of-band as a detached signature.  The signature can be validated by repeating the hash calculation process and comparing the result against the decrypted signature value.  If the node set is altered in any way, the hash will vary from the signature and anyone can figure out that the XML has been altered.

There are some big words and concepts in there, but taken in small bites none of it is very difficult to digest; it doesn't matter anyway because the .NET Framework does most of the heavy lifting for us.

Applying XMLDSIG to App.Config Files

Since app.configs are just XML files, XMLDSIG seems like a good choice for ensuring the integrity of those files.  Here are the things we need to accomplish:

  1. massage our app.config file to be able to contain the XML digital signature without breaking the standard .NET configuration subsystem;
  2. finalize our app.config contents;
  3. generate the XML digital signature of our app.config and embed it in the app.config file;
  4. have our program validate the app.config digital signature before it attempts to load any configuration values;

Since any one of these three things can be a post unto itself, I'm going to focus on the how.  If there is enough interest, I can elaborate on some of the whys in future posts.  If you just can't wait, Rick Strahl has a good example available, and InfoMosaic has a nice backgrounder

Let's get started.

Preparing the App.Config for the XMLDSIG Signature Element 

When an XML document contains its own digital signature, the signature is described as "enveloped," meaning that the document element surrounds the signature XML like an envelope surrounds a letter.  After our app config is signed, the root <configuration> element will contain a new child element; it will look something like this (all detail removed for brevity):

 

<configuration>   ...   <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">     <SignedInfo>       <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />       <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />       <Reference URI="">...</Reference>     </SignedInfo>     <SignatureValue>...</SignatureValue>     <KeyInfo>...</KeyInfo>   </Signature> </configuration>

 

We can't just plop this new element in the app.config and expect the .NET configuration manager to process it without knowing what it is; this will cause failure during application startup.  No special tricks here, we simply need to instruct the configuration system to ignore this element by adding the following to the top of the config file:

 

<configSections>     <section name="Signature" type="System.Configuration.IgnoreSectionHandler" />     </configSections> ...

The app.config is almost ready to accept the XML digital signature...

Finalizing the App.Config Contents

Once we apply the signature to the app.config, we won't be able to alter the app.config contents without invalidating the signature.  For our example, we need to make sure our application settings are set properly:

 

<configuration>     <configSections>         <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">             <section name="XMLDSIGValidate.Settings1" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />         </sectionGroup>         <section name="Signature" type="System.Configuration.IgnoreSectionHandler" />     </configSections>     <applicationSettings>         <XMLDSIGValidate.Settings1>             <setting name="ReallyImportantSetting" serializeAs="String">                 <value>if this value is altered, the application should not run</value>             </setting>         </XMLDSIGValidate.Settings1>     </applicationSettings> </configuration>

The only setting in this mess is on lines 12-13.  I set the value to something obnoxious, then move on to signing the file.

Generating the XML Digital Signature

There is no tool in the .NET SDK for applying XMLDSIG to a file, so we need to create one.  Here's the code, explanation of important stuff follows:

using System; using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Security.Cryptography.Xml;
using System.Security.Cryptography;
using System.IO;
namespace AppConfigSigner
{
    class Program
    {
        static void Main( string[] args )
        {
            // load the app config as an XmlDocument
            XmlDocument config = new XmlDocument();
            config.Load( args[ 0 ] );

            /* build up the XMLDSIG processor
             *
             * we'll use the following elements in our signature:
             *
             * - an enveloped signature transform; this will remove the
             *      actual Signature element from the document tree during
             *      XMLDSIG validation
             * - an empty Reference URI; this will indicate that the signature is applied
             *      to the entire containing document
             * - a default RSA signature key pair
             */
            XmlDsigEnvelopedSignatureTransform transform = new XmlDsigEnvelopedSignatureTransform();
            Reference reference = new Reference();
            reference.Uri = "";
            reference.AddTransform( transform );
            
            // create the XMLDSIG processor
            SignedXml xmldsig = new SignedXml( config );
            xmldsig.AddReference( reference );
            
            // set the signature key
            xmldsig.SigningKey = new RSACryptoServiceProvider();
            
            /* configure the key info elements of the signature;
             * this will instruct the XMLSIG validator on how to
             * obtain the public key to validate the signature.
             *
             * in our case, we're simply dumping the public key
             * right into the Signature element.  in production
             * this is a REALLY BAD idea, but it keeps the example
             * simple
             */
            xmldsig.KeyInfo = new KeyInfo();
            xmldsig.KeyInfo.AddClause(
                new RSAKeyValue( ( RSA )xmldsig.SigningKey )
            );
            
            xmldsig.ComputeSignature();
            
            XmlElement signature = xmldsig.GetXml();
            XmlNode signatureNode = config.ImportNode( signature, true );
            config.DocumentElement.AppendChild( signatureNode );
            
            /* save the config file
             *
             * note that we need to retain control over the file encoding
             * and the XML formatting to keep the signature valid.
             *
             * if you try this:
             *
             * config.Save( args[ 0 ] );
             *
             * you may never get a valid signature because the XmlDocument class
             * will pretty-up the XML with whitespace that wasn't there during
             * signature generation
             */
            using( FileStream fs = File.OpenWrite( args[ 0 ] ) )
            {
                using( XmlTextWriter writer = new XmlTextWriter( fs, Encoding.UTF8 ) )
                {
                    config.WriteTo( writer );
                }
            }
        }
    }
} 

The program does three things:

  1. Load an XML file specified on the command line;
  2. Sign the XML;
  3. Save the XML file back to its original location.

To calculate the XML digital signature, we need to specify few items; minimally:

  • what XML is being signed - this is called a Reference in XMLDSIG speak;
  • the private/public key pair to use for signing operations;

In addition, we may also want to include information that the signature validator can use to find the appropriate public key during signature validation.  This is referred to as the XML digital signature KeyInfo, or sometimes the KeyInfo clause.

Creating the Signature 

The signature generation begins on line 30 with the creation of our Reference.  The XMLDSIG spec allows an XML digital signature to apply to all or part of an XML node set; the reference object is the way we identify what XML is being signed.  By supplying an empty URI value on line 32, we are indicating that the signature applies to the containing XML document.

On line 36, the SignedXml instance is created.  This is the class that will do the XMLDSIG heavy lifting.  Line 37 adds our Reference to the XMLDSIG processor.

Line 40 supplies the XMLDSIG processor with the signature key pair.  In this example, I'm using a randomly generated key set each time the config is signed; in production, you will likely want to use a specific key set every time.  

Lines 52-55 add key information to the XML digital signature.  Whoever will be verifying the app.config integrity will need to know what public key will validate the XML digital signature.  There are a plethora of options as to how to communicate public key information between two parties, enough best practices to fill a book.  To keep things simple (vs. secure), line 53 merely dumps the entire public key BLOB right into the XML digial signature.  No prizes for realizing this is a VERY BAD IDEA; there should be a very formal mechanism of public key exchange between the app.config signer and the app.config validator.  That's another post, let's get this working for now.

Line 57 calculates the signature of the app.config, and 59-62 add the XML digital signature to the app.config. 

Save the XML Delicately

The rest of the program saves the signed app.config.  This can be tricky business, as ANY significant alteration of the XML will result in the signature becoming invalid.  What's a significant alteration you ask?  Well, the addition of any whitespace for one, element ordering, text node changes of course, or the encoding of the XML.  I find that you cannot simply call the Save method of the XmlDocument class; the resulting XML is somehow invalidated during the write due to the addition of whitespace.  Your mileage may vary; if you find a better way, let the world know.

Validating the App.Config Digital Signature

The application in question will need to validate the digital signature of its app.config before it attempts to load any configuration values.  If the app.config is validated against its signature, then the application can merrily load up any values it needs, fully trusting that the configuration file is in a proper state.  If the file fails validation, the application knows that the configuration file has been tampered with and cannot be trusted.

Let's look at a sample program: 

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography.Xml;
using System.Xml;
using System.Configuration;
using System.Security.Cryptography;

namespace My
{
    static class Program
    {
        static void Main(string[] args)
        {
            // discover the path to the app config
            string configFilePath = ConfigurationManager.OpenExeConfiguration( ConfigurationUserLevel.None ).FilePath;

            /*
            * load the app config as an XmlDocument
            *
            * it is vitally important to preserve any whitespace in
            * the original document, as the XMLDSIG spec defines ALL
            * whitespace as SIGNIFICANT
            */
            XmlDocument config = new XmlDocument();
            config.PreserveWhitespace = true;
            config.Load( configFilePath );

            // Create and initialize the XMLDSIG processor from our app config XmlDocument
            SignedXml xmldsig = new SignedXml( config );

            // find the XMLDSIG element
            XmlElement signature = ( XmlElement )config.GetElementsByTagName( "Signature" )[ 0 ];

            // load the XMLDSIG element into the XMLDSIG processor
            xmldsig.LoadXml( signature );

            // check the result of the signature
            bool result = xmldsig.CheckSignature();

            Console.WriteLine( result );
        }
    }
}

All this program does is validate the signature on its app.config and write the result of that validation to the console.

On line 26, the configuration XML document is told to preserve whitespace during load.  This is vital to proper XMLDSIG processing.  Normally the XmlDocument class will ignore "pretty-print" whitespace that exists between element nodes, but XMLDSIG considers this whitespace significant, so we need to make sure it gets into the XML node tree.

Line 30 initializes the XMLDSIG processor with the configuration XML document.  This is the document we need to validate.

Line 33 locates the XML digital signature element embedded in our app.config.  This element was added by our signer application.  The signature is fed to the XMLSDIG processor in line 36.

Line 39 is the money: the XMLDSIG processor is instructed to validate the app.config XML document against the digital signature by invoking the CheckSignature method.

The remainder of the program outputs the state of the app.config: True if the file is valid, False if it isn't. 

Do I Really Need to do This?

I tend to use DI/IoC a lot, and I tend to use one or more frameworks for configuring IoC containers, and they tend to like to read their configuration from my application configuration file, and I tend to wrinkle at the possibility of someone being able to modify my well-planned object graph with a little text file tinkering.  I've seen projects with configuration settings named "DisableSecurity" and "EnablePremiumFeatures" and yes the settings do exactly what you think they do.  In these cases I think this technique is valuable.

The question you need to ask yourself is: what could happen to my application if I can't trust my configuration file?  Consider the possibilities, and you'll have your answer.

Tune In 

I think this is a pretty kewl technique, if you think so too then grab the code and play around with the possibilities.  If there's interest and I get some time, I'd like to expand on some of the ideas started in this post.

XMLDSIGValidate.zip (8.43 kb)



Exposing your privates the IL way!

§ February 10, 2008 15:55 by beefarino |

Here's a quick hack you can use to pry open those pesky closed types in a .NET assembly, even if you don't have the source code.  It also demonstrates the openness of managed platforms like .NET and Java.

NOTE: The procedure involves some simple decompilation techniques, so be sure you have legal hudspa to reverse whatever you're reversing.

The hack is quite simple:

  1. disassemble the .NET assembly using the ILDASM disassembler available in the .NET SDK;
  2. modify the disassembled IL code, changing private types / methods / properties / fields to public as necessary;
  3. reassembly the .NET assembly using the ILASM assembler, also available in the .NET SDK.

I'll walk through the technique using a simple example.  Take a quick look at the source code for a simple console application named myprogram.exe:

 namespace My
{
    static class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine( Message );
        }

        static string Message
        {
            get
            {
                return "this message is top secret";
            }
        }
    }
}  

The code defines one class, with one method and one property, and everything is private.  Our goal for today will be to make all of these things publically accessible without accessing this original source code.

Step 0: Create the Assembly ...

Build the sample code into an assembly named myprogram.exe using your favorite C# compiler.  If you want, try to referece the Program class in another assembly or project, and you'll notice that there are no publically visible types exposed by the myprogram.exe assembly.

If you have a flair for the dramatic, then you can delete the source code at this point since we don't need it anymore.

Step 1: Disassemble ...

The first step is to disassemble the .NET assembly using ILDASM:

ILDASM <assmebly-file> /nobar /out=<output-file> 

where <assembly-file> is the file name you want to disassemble, the /nobar switch supresses a UI progress bar, and the /out argument specifies the <output-file> that will contain the disassembly code.

The result of the above command is usually two new files, an IL file containing the disassembly, and a RES file containing the compiled resources from the assembly. 

For our purposes, we'll dump the IL into a temporary code file:

ILDASM myprogram.exe /nobar /out=temp.il

Step 2: Modify the IL ...

Step two is the nut of the hack.  If you've ever worked in assembler, but have never seen IL, you're in for a real treat (if you've never seen assembler and prefer not to dwell into such lands, you may want to skip the next section).

Sidebar to IL-town

Many of the CLI constructs that occur in the C# language appear almost verbatim in IL, making our current task excessively simple.  Crack open the temp.il file in your favorite code editor and gaze upon the IL rendition of myprogram.exe:

// note: assembly metadata removed for brevity

.class private abstract auto ansi sealed beforefieldinit My.Program extends [mscorlib]System.Object
{
  .method private hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       13 (0xd)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  call       string My.Program::get_Message()
    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000b:  nop
    IL_000c:  ret
  } // end of method Program::Main

 .method private hidebysig specialname static string  get_Message() cil managed
  {
    // Code size       11 (0xb)
    .maxstack  1
    .locals init ([0] string CS$1$0000)
    IL_0000:  nop
    IL_0001:  ldstr      "this message is top secret"
    IL_0006:  stloc.0
    IL_0007:  br.s       IL_0009

    IL_0009:  ldloc.0
    IL_000a:  ret
  } // end of method Program::get_Message

  .property string Message()
  {
    .get string My.Program::get_Message()
  } // end of property Program::Message
} // end of class My.Program

 

Ignore all of the stuff that looks like gibberish for now - what I want you to note is that the class, method, and property-get accessor definitions are recognizable and follow predictable patterns.  Note also that they are each explicitly marked as being "private."  Sorry, but no prizes for guessing what comes next...

Modify the IL using simple text processing

The IL is just text, so we can do a simple search-and-replace with our favorite editor to make the modifications we need.  If we need to get fancy or specific, the signatures adhere to patterns well-suited for simple regular expression matching.  Whatever our scope, we simply need to change the declarations of interest from "private" to "public" (changes hilited):

.class public abstract auto ansi sealed beforefieldinit My.Program extends [mscorlib]System.Object
{
  .method public hidebysig static void  Main(string[] args) cil managed
  // ...
  .method public hidebysig specialname static string  get_Message() cil managed
  // ...
} // end of class My.Program

 

Step 3: Reassemble ... 

The IL doesn't do us any good in text form - we need the binary the IL describes.  Thanks to ILASM, the binary just a command line away...

ILASM /exe /output=<assembly-file> /res=<res-file> <source-file>

where /exe means we want to build an executable assembly, /output identifies the <assembly-file> we want to create, /res identifies a <res-file> referenced within the assembly.  Let's reassemble the open version of myprogram.exe:

ILASM /exe /output=open.myprogram.exe /res:temp.res temp.il 

This will produce a new executable named open.myprogram.exe that behaves identically to the original.  Granted, the final result of this effort may not appear terribly impressive, until you try to reference this new assembly in another project.  You'll find that the My.Program class and its members are all publically visible usable in your new project.

So ... What?

How often will you really need to expose hidden members and types without access to the original source?  Granted it will be infrequent, if ever.  I've done it enough times in the arcane projects I end up on to script the process in powershell.

And I'm not trying to prove that access modifiers fail to constitute a security platform.

The thing I find really interesting about this technique is the breadth of things you can accomplish with little to no background in reversing (the author being a case-in-point).  With a little effort and some googling on IL you can use the same basic technique to accomplish all of the following:

  • alter the behavior of a sealed type;
  • virtualize type members at whim;
  • remove the strong name from an assembly, or replace the existing strong name with another;
  • add or remove metadata to the assembly, types, or type members;
  • intercept and forward all calls from one method or type to another, as in a man-in-the-middle attack;
  • etc.

Accomplishing these kinds of things used to require specialized and esoteric knowledge, like "e9 is the machine code for an absolute jump on an x86 processor".  But in .NET and other managed platforms like Java, these things become so accessible you can practially leverage the original code during the reversing process.

... I'm not sure whether to stop grinning or shivering ...



The obligatory and awkward first post

§ February 8, 2008 14:39 by beefarino |

After a day of false starts, dead end meetings, project management goose chases, and vaporous bug reports, it's nice to have a success.

The blog is up, the site is live.  Welcome.