Tag Archives: Security

Brute Security

DISCLAIMER: I do not endorse the use of any “cracking” methods I discuss in this article for anything but educational purposes, nor do I claim to be any sort of “security expert”. Everything here is provided “as-is” with no warranty whatsoever.

With the legal jargon taken care of, let’s discuss passwords and why you have them (note the plurality – I sure hope you don’t simply have one password!). Passwords protect your information.Without them, everyone would have access to everything with only a username. Would you feel comfortable with that? I didn’t think so.

So, if the point of a password is to protect your security, then it follows that a password should be as secure as possible. What makes a secure password? I turned to Google for some advice. Here’s the jist of what I learned:

  • Length is super-important
  • Mixing uppercase letters, lowercase letters, numbers and symbols makes the best password possible

What’s groundbreaking about that? Absolutely nothing – tips for crafting great passwords have been around for as long as I have been able to read them. The problem is that great passwords are often difficult to remember. For example, AsJ*()gM,4 is ten characters long and has at least one character from each category I just mentioned. XKCD came up with a solution to the problem, though I don’t agree with simply using lowercase letters (and Google rates the example “correcthorsebatterystaple” password as “Good” rather than “Strong”). Why only strong for such a long password? Here are two common for breaking passwords:

  • Dictionary attacks are attacks using common “passwords”, usually derived from the dictionary or other common phrases. Dictionary attacks are the easiest, fastest way to break insecure passwords. Is your password “password”, “password1″, “123456″, “qwerty”, or anything else on this list of tragically bad passwords? It will be broken with a dictionary attack.
  • Brute force attacks are exactly what they sound like – they try every password combination possible. They are useful when dictionary attacks fail, because “zebra” will be cracked by a dictionary attack, but “ze6ra!” probably won’t be. They also aren’t very much work to implement but take much, much longer than a dictionary attack. How much longer? I wanted to find out.

I wrote a very, very slow brute forcer implementation in C#. I am not going to publish the source code on the blog, but I’ll give you an idea of how it works:

  •  The brute forcer is passed a string containing every character it should try, which could be something like “abcdefghijklmnopqrstuvwxyz”. It is also passed the MD5 hash of the string it is trying to break.
  • The brute forcer uses a for loop starting at zero up to the length of the trial string raised to the power of the maximum length to look for.
  • It computes the MD5 of the generated string and compares it with the one it is trying to break. If they match, it is done.
  • There are some big slowdowns – the .NET framework (which I did my best to counteract by using the “unsafe” keyword) and that I am only using one core. Sure, I have a 3.7gHz i7, but I am only using one of eight possible cores. These passwords would break much faster if I used all of them or if I had a supercomputer.

The way this brute forcer works has taught me two things just how correct that password advice is. It’s all in the math.

Longer passwords make taking passwords way longer. Let’s examine a two character password comprised of uppercase and lowercase letters. There are 52 possible first letters and 52 possible second letters, so the total number of password permutations is 52 * 52, or 52 ^ 2. It follows that the number of permutations is 52 ^ N, where N is the length of the password. Therefore, assuming every cracking iteration takes the same amount of time (around 50 microseconds on my password cracker near the beginning, which isn’t very fast), every letter you add makes cracking take 52 times as long. If you add three characters, there will be about 140,000 times as many combinations.

Special characters make password cracking take far longer as well. Think about it – if your password is comprised solely of lowercase letters, there are 26 ^ N permutations. Add the uppercase letters, and there are 52 ^ N. With the numbers 0 – 9, there are 62 ^ N. Add ~`!@#$%^&*()_-+= and there 78 ^ N.

According to my expert Windows calculator usage (in scientific mode, which the C# calculator tutorial didn’t cover), a five-character lowercase password has 11,881,376 permutations, while a five-character password that could use every symbol I have mentioned has 2,887,174,368 permutations. That’s right – the jump is from 11.8 million to 2.9 billion. In fact, you would have to use about a seven-character (well, about 6.7-character) lowercase password to match the strength of the five-character anything-possible password.

Not convinced about the huge difference? Here are some action shots with some different password lengths (all matching against the “anything-possible” string of 78 characters):

Three-Character Password

Three-character password.

Four-Character Password

Four-character password.

Five-Character Password

Five-character password. To be fair, my computer was asleep for much of that time.

So, in going from a three character password we went from less than one million iterations to success to 2.4 billion iterations to success. Imagine how long this would take with, say, an eight-character or ten-character password.

As always, there is one big concept I want you to take away from my articles. In this case, it is that adding just one symbol to your current password (even if it as simple as putting it in parentheses or putting an asterisk in front) will strengthen it immensely. Stay secure!

.NET Applications: Open Source by Default

If you have been developing .NET applications for much time, I hope you have come to realize that your Windows applications are not very secure. What do I mean by that? Let’s take a very simple application as an example:

using System;
 
namespace InsecureApp
{
    class Program
    {
        static void Main(string[] args)
        {
            for (; ; )
            {
                Console.WriteLine("Enter your password:");
                if (Console.ReadLine() == "secret")
                {
                    Console.WriteLine("Logged in. My social security number is 123-45-6789.");
                    Console.WriteLine("Your session has expired. Logged out.");
                }
                else
                {
                    Console.WriteLine("Incorrect password.");
                } 
            }
        }
    }
}

I realize this example is trivial, but I am trying to illustrate a point. I’ll do it in two ways: first, using ildasm.exe and second by using Telerik JustDecompile. No, this isn’t a product endorsement. I just needed to find a free decompiler.

You can run IL DASM with the Visual Studio Command Prompt. Here’s the output from my test program:

IL DASM Example Image

Please don’t sign me up for too many credit cards!

Not that scary, right? Potential crackers can only see the login password and your social security number. In an actual application, an email address, password, or authentication token could be visible in plain text. Remember, I did this with a free tool. I didn’t even have to download anything! What if I did, you ask?

Decompiled Code

Oh, bother.

Not only is the code reasonably close to what it actually was (to the point where it is easy-to-understand), I could look at the code in VB.NET if I wanted to and there is syntax highlighting! This is another free tool that you can use without even understanding CIL. But, say you did. Back in the day (and I do mean “in the day”, perhaps four or five years ago), I became interested in what was going on behind-the-scenes in .NET and learned some CIL. I also learned that you can use /OUT=”location” with IL DASM to actually output CIL to a file. Oh, I forgot to mention – there is a corresponding IL ASM to put the program back together.

Let’s say I’m a lazy cracker, so I am going to just jump to being logged in and then never log out. I changed the CIL for Main to look like this:

  .method private hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       81 (0x51)
    .maxstack  2
    .locals init ([0] bool CS$4$0000)
 
	ldstr "Cracked by 1337 H4x0r or something"
	call void [mscorlib]System.Console::set_Title(string)
 
    IL_0026:  ldstr      "Logged in. My social security number is 123-45-6789."
    IL_002b:  call       void [mscorlib]System.Console::WriteLine(string)
 
	call string [mscorlib]System.Console::ReadLine()
	pop
	ret
  } // end of method Program::Main

I then reassembled my program and ran it. There was no need to log in, and the program conveniently waited for me to hit enter before closing!

Simple Program Cracked

It took a surprising number of tries to assemble this application properly.

So, what can you do? Can you strong name your assembly? Sure, but it won’t do much to anyone that knows his or her stuff. Can you obfuscate it? Sure, but it’s only a roadblock. I did some reading about protecting .NET Windows applications from piracy, and the consensus seems to be that much protection isn’t worth it. Would you rather spend your time keeping people that will not pay for your software from using it or adding features to make more people willing to pay for it?

The important thing to take away from this post is to realize that anyone can see and modify what you write. You should not handle tasks that should be secure (such as anything that requires password or key storage) on the client side. Server-sided code is much better suited to secure tasks (since you cannot download ASP.NET binaries from a server unless something is seriously wrong), so anything that has to remain a secret should remain remote. Remember, transmitted data can be intercepted, so I am not telling you to send your passwords from the server to the client. I am telling you to have the client ask the server to do its dirty work.

The bigger message I wish to convey, though, is that you should minimize the amount of data that needs to be protected.