How Not to Obfuscate Passwords in Code

By Tim MalcomVetter ·

Software programs, from client-server to web to mobile, often need credentials to access a resource like a database or a web service. Storing these passwords is not an easy task, since there are so many potential threats. If one of those threats is an adversary able to disassemble your compiled code, well … all bets are probably off.

On a recent client engagement, I came across an application we had assessed a year prior.  This was a Java application contained within a .jar file, but the following principles hold the same for Android .apk files (which are just .jar files with special organization inside) or even .NET apps. From an attacker’s perspective, one of the best features of Java and .NET apps is the ability to reverse the high-level source code from them with tools like JD-GUI for Java and .NET Reflector for any language (C#, VB.NET, F#, Python) that is compiled down to .NET runtime. There are some decent tools out there for obfuscating code to make disassembly difficult (note: “difficult” not “impossible”), but in this particular case, the client did their obfuscation by hand.

In the assessment, we reported to the client that credentials were stored in plaintext as String objects in the compiled .jar file.  We verified the credentials were valid by logging in with them. The criticality of an issue like this really depends on what the credentials can access. It can range from “no big deal” to “fix this now!”

On the annual check-up a year later, the client’s developers left the connection details, including the User IDs as plaintext String objects in the Java code, but they removed the String password objects. Curious, I looked a little deeper and found a new helper object that was called when the passwords were needed. The following is a similar mock-up for illustration purposes:

public class PasswordObfuscator 
{    
    static String GetPassword(boolean bool)
    {
        StringBuffer sb = new StringBuffer();
        double[] dArray = { 72.125D, 36.9999999999D, 119.27000000000001D, 71.999999999D, 66.222D, 67.01D, 99.9999D, 121.0001D, 122.00000001D, 79.000023423D, 98.34119D, 33.0D, 48.0012312D, 101.00023D, 64.00004312999D, 45.000009999999D, 118.1D, 53.001D, 78.32323D, 95.5D };
        int i = 0;
        for (double d : dArray)
        {
            int val = (int)d;
            if (bool && (i % 2 == 0))
            {
                sb.append((char)val);
            }
            if (!bool && (i % 2 == 1))
            {
                sb.append((char)val);                
            }
            i++;
        }
        return sb.toString();
    }
}

I stared at this code for a couple moments trying to recall how Java handled overflow of all the bits on the right hand side of the decimal point when cast to integer. It was immediately apparent to me this code was alternating through the values in the array, casting every other integer value to its ASCII value. I began working the algorithm mentally for a second and then it hit me: Why am I doing this the hard way? 

At this point, I decided to pick on one of the recent graduates from our successful junior consultant program who was working this engagement with me. “Take a look at this for a second.” Being a recent computer science grad, I saw him start working the algorithm like I previously started, but (much to my enjoyment) he broke out a notepad and pencil to work it all out by hand. [Mental note: universities need to teach how to break code, not just build it.]

“That’s the hard way. Cheat!” 

He looked confused for a second as he saw me copy the entire helper object out of JD-GUI and paste it into a new “Hello World” Java project in Eclipse.  I modified the main() function as follows: 

public class Program {
    public static void main(String[] args) {
        System.out.println("Hello World.");
        System.out.println(PasswordObfuscator.GetPassword(true));
        System.out.println(PasswordObfuscator.GetPassword(false));
    }
}

Then he smiled.  He saw where this was going.  Click build, then click run > debug:

Out to the console came the super-secret passwords. What took the developer probably at least a couple hours to obfuscate by hand was unraveled in the few seconds it takes to copy, paste, build and run.

Moral of the story: don’t write your own security features—this includes code obfuscation.