How to prevent spoofing of DLLs in .NET - c#

I have a .NET application that references a managed DLL.
This DLL contains a class, say ScoreKeeper that implements a method called GetHighScore(). The application calls this periodically.
Is there a way to prevent the .NET application from using a "non-authorized" DLL here in place of the one I am supplying?

You mention:
This DLL contains a class, say ScoreKeeper that implements a method called GetHighScore(). The application calls this periodically.
And then:
Is there a way to prevent the .NET application from using a "non-authorized" DLL here in place of the one I am supplying?
Assuming that you want to prevent someone from swapping out the assembly you have provided with an assembly of their own which has the same name and type (which is in the same namespace), you could apply a strong name to the assembly that contains the ScoreKeeper class and have your consumers reference that.
However, we'll see that there are issues that make this not 100% reliable. Strong Names help you protect unaware users from replacement of your DLL with a malicious spoofed copy. But if the user is complicit in the spoofing (which would be the case if he is trying to cheat), then code signing will be no more than a speed bump and provides no real protection. Certainly, Strong Names don't provide protection comparable to e.g. PunkBuster.
Using a Strong Name to verify an assembly publisher's identity
When you add a strong name to an assembly, you are using a private key (part of an asymmetric public/private key pair, more on this later) to generate a cryptographic hash and the public key is included in the assembly name (along with the hash).
Using the public hash and the public key, the CLR is able to verify that the signature of the assembly did in fact come from the private key.
Of course, this means, you should protect the key (internally and externally); if someone else has your key, then they can effectively impersonate you and publish assemblies that people would trust to be from you.
Then, when you add a reference to your signed assembly, if someone tries to put a different assembly in with the same assembly name (not the fully qualified one, just the name without version, hash and public key) and same type name, the CLR fill fail when trying to load the type, indicating that it couldn't find it; the type is resolved using the fully-qualified assembly name, along with the namespace and type name.
Why Strong Names are not 100% secure (is anything?)
1) Hash Collisions
It is still a hash that is being verified. While the hash is quite large (160 bits for the default hash algorithm, SHA-1), any hash that has a finite number of values is subject to a collision. While extremely unlikely, it is possible (impossible vs. improbable). Furthermore, only the last 8 bytes is used by default. Combined with research indicating that SHA-1 is relatively weak, this is a good reason to use SHA-256 Enhanced Strong Naming as described in MSDN.
2) Removal of the Strong Name
The strong name can be removed. However, in this case, because your assembly is referencing the strong named version of the referenced assembly, when your assembly tries to use the compromised version, it will fail at runtime, assuming you've correctly re-enabled verification (see below).
3) Physical access to the assemblies means all the assemblies
If someone has access to the physical machine and can modify the assembly that you are referencing, then your assembly is just as vulnerable. If the attacker has the ability to modify the strong name of an assembly that you referenced, then they can just as easily modify your assembly and all others involved in the execution. To this end, the only way to be 100% sure that the physical assembly isn't hacked is to deny physical access through it. Of course, that brings up a slew of different security concerns.
4) Administrator disabling the Strong Name check
The computer administrator can simply bypass the strong name check, using sn -Vr. According to MSDN:
Registers assembly for verification skipping... A malicious assembly could use the fully specified assembly name (assembly name, version, culture, and public key token) of the assembly added to the skip verification list to fake its identity. This would allow the malicious assembly to also skip verification.
5) Strong Name checking has to be explicitly enabled post .NET 3.5 SP 1
From .NET 3.5 SP 1 on, simply having a strong name doesn't provide any protection:
Starting with the .NET Framework version 3.5 Service Pack 1 (SP1), strong-name signatures are not validated when an assembly is loaded into a full-trust AppDomain object, such as the default AppDomain for the MyComputer zone.
In order to have .NET check the strong name of each assembly loaded into your application, you'll want to insert the following snippet (provided by MSDN) into your application configuration file:
<configuration>
<runtime>
<bypassTrustedAppStrongNames enabled="false" />
</runtime>
</configuration>
Beware, however, that this only protected against removal of the strong name.
When you override the bypass feature, the strong name is validated only for correctness; it is not checked for a StrongNameIdentityPermission. If you want to confirm a specific strong name, you have to perform that check separately.
If in light of the concerns above, you'd still like to pursue Strong Naming your assembly, here's how.
Generating a Strong Name and signing your assembly
You have two options for which key to use when generating a strong name. In Visual Studio, go to the Signing tab on the project properties and click "Sign the assembly":
From there, you have two options to generate the public/private key, to have VS.NET generate the keys for you, or point to an existing one:
When selecting "New", Visual Studio will prompt you for the name of the file to generate, as well as whether or not you want to optionally use a password to access it:
At which point, the key will be added to your project:
Now, you can move this to a solution item (if you have multiple projects in your solution).
Visual Studio in this case is really just calling the Strong Name command line tool to generate a public and private key pair. If you'd rather do that yourself, you'd want to call sn.exe with the -k command line option to generate the key, like so:
sn -k keyPair.snk
And then add it via the "Browse" dialog above.
Note that when you do this, it will pull the key into your project. If you don't want to do this (as it copies the key into every project), then delete the key from the project and then add an existing file to the project, but link it. This will clear the "Choose a strong name key file" option, but if you drop it down, you'll see the full path to your linked key file.

Seems like the most straightforward answer would be to calculate a secure hash of the DLL you are loading, and then compare it against a golden value that you have precalculated. Of course,this will still be vulnerable to being broken by a sufficiently determined attacker, but it will significantly raise the bar for people who want to cheat.

Related

What's an assembly identity?

(Let's assume I only have a single default appdomain for this question)
Apparently Assembly.LoadFrom() can lead to multiple assemblies of the same name, version and content to be loaded even if just their paths are different.
This seems to go against what is said here:
"At assembly-bind time, the components of the assembly display name
determine identity". (http://blogs.msdn.com/b/suzcook/archive/2003/07/21/57232.aspx)
Because if the identities are the same, why would .NET load it into the process a second time? It seems there are two different kinds of identities.
From that blog post what also doesnt make sense is this:
"Not all of the components matter in all cases. If the assembly is not
strongly-named, then the version is ignored for binding. But, if it is
strongly-named, the entire version in the assembly reference needs to
match the found assembly."
How does this make sense with what is said here:
"When this feature is enabled, strong names are not validated for
correctness when the assembly is loaded. The default is true"
(http://msdn.microsoft.com/en-us/library/cc679069(v=vs.90).aspx)
So strong names aren't validated at all which would make what Suzanne wrote in her blog post incorrect. Or what exactly is meant by "validation"? I think all of this boils down to a complete misunderstanding of assembly identity. I dont understand where and why and how an assembly identity matters in what ways.
Crash course in assembly identity: An assembly has a name and version, so far so straight forward. An assembly also has a culture; the implementations for different cultures may for example format date strings differently, and while they may have the same name and same version they do not do exactly the same thing. The public key token is part of a weak security measure to keep different publishers from producing assemblies with colliding identities.
Why would .NET load an assembly into the same App Domain multiple times?
Because the assembly is loaded in different load contexts. There is a context corresponding to default resolving behavior, a context to which copies loaded from a file path, and a context for other situations such as when the assembly is loaded from bytes in an assembly resolver. This behavior has been criticized since the types from assemblies with the same identity but in different contexts are treated as completely different types. See this MSDN article and this blog post:
https://msdn.microsoft.com/en-ie/magazine/dd727509%28en-us%29.aspx#id0400031
http://www.pabich.eu/2009/07/how-many-times-can-you-load-assembly.html
What does Suzanne Cook mean by validation? As a security mechanism (which has proven to be weak and easily broken) there is a private strong name key and a corresponding public key. The public key appears in the manifest of the DLL. The DLL is signed using the private key and the signature appears somewhere in the PE file - the PE header points to this information. A short form of the public key becomes the public key token. This ensures that only the publisher who holds the private key can produce binaries with a particular public key token, although this has security issues. This mechanism prevents assembly identity collisions between different publishers. Full trust assemblies have other signing mechanisms which ensure their authenticity. As an optimization, you can bypass checking the strong name signature since it is assumed fully trusted publishers won't attempt to collide assembly identities with each other. See more in this Microsoft doc:
https://learn.microsoft.com/en-us/dotnet/framework/app-domains/how-to-disable-the-strong-name-bypass-feature
Bonus question: I want a strong security mechanism so that my customers can really trust my assemblies. I might even want to verify that components of my application are really published by me to prevent reverse engineering, licensing avoidance, etc. How do I do this?
SHA256 Authenticode signatures are the latest and greatest in signing any PE file, be it a managed assembly or an unmanaged binary. This is not a replacement for the public key token. The public key token is important for establishing assembly identity. The Authenticode signature is important for verifying assembly authenticity.
https://blogs.msdn.microsoft.com/ieinternals/2011/03/22/everything-you-need-to-know-about-authenticode-code-signing

How can a signed application use an unsigned assembly (which can be regenerated at customer sites)

My C# .net executable needs to be signed in order to use one of the methods in another of our assemblies that provides access to a password.
That same executable needs to reference and use an assembly that is not signed. The reason it is not signed is we provide a way for customers to regenerate it on site (it wraps access to modelled customizations in the app).
Is there any way the single executable can make use of both the signed and unsigned assemblies?
You can't reference unsigned assembly from signed assembly, but you can easily Load one using one of Assembly.Load methods.
Usually you provide interface (or base class if you like) in signed assembly and use that throughout code to obtain information at compile time. Customer's unsigned assembly (plugin) implements that interface on some specially marked class. Than your application loads the assembly (either from default location via Load, or from specified file via LoadFile), finds and constructs the class than uses it to obtain information at run-time.
Note: You may also look into "how to implement plugins in C#/.Net" questions for approaches.
I believe the only way for you to use an unsigned assembly is to load it at runtime using reflection.
You cannot a add strong name to your executable if it references something which is not strongly named.

Changing public key token, how to redirect assemblies?

One of my customer wants to change the assembly signing policy.
Basically, all code and assemblies will be signed with the same and unique snk file.
Unfortunately, a very large code base is using the old public key token.
Is there a way to "redirect" old name to new name assembly?
I've read Assembly Binding Redirection, but it looks like it can only redirect to new version of the same assembly name/public key token.
While I can recompile code, I have a lot of aspx and ascx referencing the old name. I also have 3rd party database that serialize some objects in binary format. I can't change that easily.

Signing assembly with Private Key

Dear all I have some confusion, if one of you please help me regarding this. My questions are:
Everyone say to sign assembly with private key, but no one say how to get private key means by using sn.exe -K I can create private/public key pair but how to extract private key from it to sign it.
If i sign my assembly with private key and then send it to user of this assembly then he will directly add this assembly in global assembly and use, so is it needed to send him public key to use this assembly or public key is with assemblyl so anyone can use this assembly and if anyone can use what is meaning of encrypting it will private key.
I have some confusion
This is a confusing topic.
how to extract private key from it?
You don't need to. The signing tool takes the key file. It is smart enough to extract the private key if it needs to.
is it needed to send him public key to use this assembly?
The public key is embedded in the assembly. You can see it if you look at the strong name of the assembly. However, it is necessary to somehow communicate to the customer what the correct public key is, if you think that someone might be trying to impersonate you! See the next bit for details.
if anyone can use what is meaning of encrypting it with the private key?
The purpose of signing the assembly with the private key is to produce evidence. The evidence means "this assembly was produced by the person who possessed the private key associated with this public key".
For example, Microsoft's public key token is well known. That is why it is called the "public" key: because it is known by the public. When you see an assembly that has Microsoft's public key, that is evidence that Microsoft produced that assembly because an assembly whose signature can be decrypted with Microsoft's public key must have been produced by someone who had access to Microsoft's private key. That key is under heavy guard deep inside Building Seven here in Redmond, so you have good evidence that someone who really did work at Microsoft produced that assembly.
The purpose of this system is to enable people to set policies that turn evidence into permissions. Your customer can have a policy that says "if I have evidence that this assembly was produced by Ashish, then allow the code to do anything that I am allowed to do".
Now, you might reasonably ask "how does the customer know that a given public key is your public key?" That is the "key management" problem, and that is your responsibility. If you have an evil twin out there who is telling your customers that his public key is your public key, then your customers might trust your evil twin, thinking he is you. In that case, it is your responsibility to educate your customers that there is an evil twin out there who is trying to trick them, and teach them how to tell the difference between the public key of the good Ashish and the public key of the evil twin.
You are also required to ensure that your private key stays private. If your evil twin learns of your private key then obviously they can impersonate you as much as they like; remember, the signed assembly provides evidence that the person with access to the private key produced the assembly; if more than one person has access to that key then the evidence does not identify one person.
You don't extract the private key to sign the assembly. You just configure the project to be signed using the SNK file (generated by sn -k) which contains both the private and public keys. You can specify that in an assembly attribute, but these days the preferred method is to specify it in the project options instead (the equivalent of the /keyfile flag on csc.exe, I believe).
Personally I generally wouldn't add libraries to the GAC - I prefer to keep the assembly alongside the project which is using it. But no, you don't need to send him the public key.
You're not encrypting the file with the private key - you're signing it with that key. The public key is embedded within the assembly, and basically you're saying, "The producer of this assembly definitely owns this public/private key pair, because you can verify that it's been signed with private key that corresponds to this public key."
You can create a key with sn.exe, it will create an .snk file. You can also sign an assembly with sn.exe and the same .snk file, no need to extract anything. Also it's much more convenient to do it all in Visual Studio (project properties, can generate and sign too).
No, the assembly includes the public key. Maybe you're confusing it with code signing with X.509 certificates. It's not that.
I think these two articles tell you all you need know:
How to: Create a Public/Private Key Pair
How to: Sign an Assembly with a Strong Name

How hard is it to tamper with a strong named assembly?

Assume I have a .NET assembly which is strong named. Only I have access to the private key. I then distribute the assembly to some client system.
How hard is it for the client to modify the assembly? Ie: what would they need to do to modify my assembly?
Strong-naming does not prevent modifying the assembly, but it does prevent other applications which reference a strong-named assembly from inadvertently using a modified version.
It's no different from modifying a non-strongly typed assembly. The only real difference is that they would have to run the strong name utility (sn.exe) in order to use the modified assembly.
As others have said, its very easy.
One technique you can use is to use the public key (or token) of your assembly to encrypt important information (such as algorithm parameters, connection strings, etc) in your assembly. This way if the public key has been changed or removed, the decryption will fail and your assembly would no longer run correctly. Obfuscators such as Crypto Obfuscator use this technique as one part of the protection.

Categories