Introduction

String data type is used quite often for storing hard-coded secrets in the code. These secrets could be general purpose literals used in applications like connection strings or business specific secrets (like coupon codes, license key, etc.). The fact that a vast majority of developers/applications have sensitive data in string data types makes it quite interesting for hackers. In this article, we will demonstrate some techniques that hackers use to discover sensitive information stored within strings.

Let's Make Couple of Things Clear Upfront

There are couple of things that I would like to make absolutely clear before we delve into any further discussion. First in this article, I will demonstrate how hackers can use tools like JustDecompile, ILDASM and Windbg to their advantage. What's important to remember is that none of these tools were built to facilitate hacker's interests. These tools are built for legitimate purposes and for good reasons. It's just that bad guys use the same tools for their own interests that can be potentially harmful for others. It is therefore quite important to learn the techniques that hacker's use so that we can be better prepared to mitigate against specific threats posed by those techniques.

Finally this article by no means is about defense mechanisms for the threats that I will demonstrate. I will briefly mention some common approaches that could be used to defend against these threats, however, it is not a comprehensive discussion about defense mechanisms & methodologies.

What Makes this Type of Attack Possible?

With CLR Execution model, you write code in your favorite .NET Language like C#, Managed C++, VB.NET, etc. This code is compiled into byte code called Microsoft Intermediate Language or IL. So your application or .NET Assembly actually contains code in the form of Intermediate Language. When you execute your application, at runtime this IL code is compiled into native CPU instructions. The process of compiling IL code into native instructions is called Just In Time compilation aka JITting.

Figure 1: CLR Execution model

This intermediate language contained in .NET assemblies is highly detailed and it preserves information about all data structures like classes, fields, properties, methods, parameters, even the method code. Presence of this highly verbose IL is what makes reverse engineering a .NET assembly quite trivial thus enabling attackers to gain valuable information without having access to the actual source code.

Let the Fun Begin

In order to find hard coded secret in an assembly, you can open the assembly in .NET code browsers like Telerik’s JustDecompile or ILSpy, etc. These code browsers provide you with the ability to open a .NET assembly and view the code in form of C#, VB.NET or Intermediate Language. Let’s take a brief look at our sample application. The code itself is very simple & straightforward but it’s good enough to demonstrate the concept. This sample application has a class called Constantsthat contains some strings used to store sensitive business information as shown below:

Now let’s open the compiled executable assembly in Telerik’s JustDecompile. Figure 2 shows the view of this assembly and as you can easily view these strings that should not be easily revealed to anyone.

Figure 2: .NET Assembly in JustDecompile

Typically in a real-life application, searching through strings by opening the assembly (or set of assemblies) in any .NET code browser (like Telerik JustDecompile) is a tedious job. Today’s hackers are smart and they have more efficient tools in their arsenal to efficiently find vulnerable pieces of code and plan their next attack accordingly. One of those tools is Intermediate Language Disassembler (a.k.a ILDASM). Just like JustDecompile, ILDASM could be used to disassemble a .NET assembly and view its code, however, with ILDASM you can only view this code in the form of Intermediate Language.

ILDASM can be operated in two modes, one using its graphical user interface; the other one using command console. ILDASM in console mode is what typically hackers prefer especially to perform an information disclosure attack. Figure 3 shows how ILDASM could be used in command mode to search for string types present in .NET Assembly code.

Figure 3: ILDASM for searching string data types in .NET Assembly

We have used ILDASM with text switch, which basically implies to display the decompiled assembly in the console windows. This text is then piped into findstr command. The argument to findstr is ldstr. ldstr is an intermediate language instruction to load a string into memory/evaluation stack. As you can see from the output of this command, it only list strings from our .NET assembly including sensitive data like connection string, user name, password, etc.

One common mechanism widely used to protect intellectual property and hard-coded secrets is by obfuscating your assembly. Most commercial obfuscation tools not only make .NET code less human-readable but they also provide options like encrypting string types. Figure 4 below demonstrates the same ILDASM command when run against an obfuscated assembly with strings been encrypted.

I would like to point out that assembly obfuscated and string encryption by no means a perfect solution. It does provide some protection against casual user, however, a determined user with reasonable skillset and enough time in hand can crack though many defense mechanisms.

Secrets Leakage during Runtime

What we have seen so far are "static" attacks performed against .NET assemblies. String data types are also vulnerable to secret leaks during runtime. What it means is that at runtime a hacker can attach a debugger and inspects data stored within these string data types. Let's take a look at how this type of attack could be performed. I will use Windbg for this demo. Windbg is a native debugger that could also be used to debug managed application with the help of extensions DLLs. Windbg is part of "Debugging Tools for Windows" and could be downloaded from here.

Let's take a look at how string secrets can be inspected at runtime. When you launch the demo application, a login screen appears. I have simplified a couple of things in this demo application to make things easy to follow. For instance, typically a password type field you don't see the characters that user is typing (rather just display a * for each character). Another simplification I did is by displaying a message box when user hits OK button. In a real-life application, typically clicking on OK button takes users to a new screen. I have used this method to simplify the process of attaching windbg to a running process. There are ways of setting breakpoint in Windbg also. You can read one way of setting breakpoint my blog here.

The figure below shows the login screen from demo.

Figure 5: Login Screen

When you hit OK, a dialog will appear showing that login has failed. At this point, launch windbg and attach it to the running process. For this, you have to choose "Attach to a Process" menu option under File as shown below:

Figure 6: Attach to a Process menu option

Next, you will get the following dialog box to select the running process that you want to attach.

Figure 7: Attach to a Process Dialog

At this point, we would like to take a peek at .NET managed heap and inspect string data type instances. There are various ways of doing it. I will simply use !strings command to get to it.

Figure 8: Running !strings command

The output of !strings command could be really long but the values we entered in text boxes for username and password should be visible here as shown below:

Figure 9: Secrets visible at runtime

Microsoft has provided SecureString class that could help provide some protection against these type of attacks. Keep in mind people have found ways of inspecting SecureString class to determine the actual value of string stored within SecureString.

Summary

In this article, we discussed why string data types is of particular interest for hackers. We demonstrated some common ways that hackers can use statically and at runtime to discover sensitive information stored in string data types. Hopefully, you will find this information useful the next time you store some secrets within your code.

Share

About the Author

Kamran Bilgrami is a seasoned software developer with background in designing mission critical applications for carrier grade telecom networks. More recently he is involved in design & development of real-time biometric based security solutions. His areas of interest include .NET, software security, mathematical modeling and patterns.

First, thank you for this nice article.
I follow your instructions but cannot list the string values. When I enter the !strings command I get "No export strings found." What am I missing? (attach to process is succesful)

Sorry about the late reply. This type of attack is possible against other types, however, as I mentioned in the article typically string data type is what most developer used for keeping their "secret". There is a reason why Micorosoft has provided a SecureString class and not a SecureInt or SecureDouble class. Also there is a reason why all commercial obfuscation tools provides features like string encryptions. Hope this answers your question.