Securing Business Apps
Rise of the White Collar Crackers





Section 2 - Bypassing the login



Database Security

When an attacker first comes to the application, they will need to gain primary access. Lets assume a condition where the attacker is in after hours or does not have a valid login of his own. The first level of security comes with how much it takes just to gain access to the rest of the application.

There are many login and authentication schemes available. Our sample application uses a simple portable small business scheme based around data stored in an access database.

In order to provide a portable demonstration for these and other techniques, I was forced to use an Access database. As we will see shortly, that is a poor choice for sensitive data.

If the computer we are working on has Access installed, we could actually just open up the database and see what all the user names and passwords were.

In our example the Access database does not have a password required to open it, however even if it had, it would provide little to no real security for the database.

There are many programs on the market, and even open source code available on the internet to crack Accesses weak authentication scheme. Even if the attacker was unable to open the database, they could open it in a hex editor and search for table and field names which appear unencrypted.

The above concerns all have the precondition of physical access to the database and are commonly understood security factors. In this next section we are going to begin to look at what can be done with only physical access to the application.

The login mechanism of our sample app first connects to the database and then compares our login values to the usernames and passwords found in a users table.

If the username/password combination is found it loads the usergroup into a global variable and loads the main form. If a correct username and password is not given then it will alert them of an invalid login and again prompt for valid credentials.

The next level of attack we are going to look at comes through the use of cracking techniques. That is binary analysis and manipulation of the compiled executable.


Query Manipulation - Null Byte String Truncation

Since our authentication model stores the users in the database, our Visual Basic code must first open a connection to verify the login information. In this classic example, our developers have chosen to implement application layer security controls rather than information layer security controls.

Because all application security is handled with code running on our local machine, and because we have physical access to the executable, we have the power to explore the application as it runs and manipulate the code as it executes.

The first bypass we are going to discuss is actually very simple. As we step through the application code in our debugger we can make out the SQL statement used to query the database with the username and password we supplied. In Visual Basic it looks like this:

	"Select * from tblUsers where " & _
       "username='" & txtUserName & "' And " & _
       "password='" & txtPassword & "'"
Another thing we can see in the debugger is where these strings are loaded from. All of the strings we see in the app are plainly readable in the executable in the form of unicode strings compiled in as data in the executable file. The first fragment to be loaded for this sql statement is "Select * from tblUsers where " which is located at offset 5C74.

We can see from the SQL syntax that if either the password or username does not match no records will be returned. The developer will then test this condition. If no records are found in the recordset, then its an invalid login. Since username/ password pairs have to be unique, only one record could be returned for a successful login thus identifying the user.

But here’s the catch...we know how the sql query string is built up and from where. The thing that limits the result of the query is that where clause that gets appended onto it with our values from the textboxes.

If we say...replace the space just before "where" with a null character what will happen then?

In C and many other programming languages the end of a string is denoted by the presence of the first null character found. Visual Basic does not have this limitation because it uses an OLE type to hold its strings, so VB can contain embedded nulls with out a problem.

It happily uses the modified string, appends on the username and password just as it was designed to do then hands the query off to the database engine to return the recordset.

The catch is, the database engines are not written in VB, they locate the length of the strings passed to them by the occurrence of the null terminator, which in our case, now truncates our sql query to be "Select * from tblUsers" which will always return a result set.

You can try this for yourself by starting up PatchManager.exe and selecting the first Sql Null char truncation sample and hitting Patch & startup button. After the binary is patched, it will execute for you and you will be able to login with any username and password you want.

This may sound like alot of work...but really this much can be accomplished in under 5 minutes by a skilled hand.

Lets say I knew a users login and I wanted to login as them but did not know thier password. This could also be readily accomplished by changing the space before "And" to a null in effect limiting the results to just checking for a valid username.

If we knew there was an account named admin, we could now login with his privledge level without having his password. Try the second Sql truncation demo from the patch manager with user "admin" and see for yourself.


Debugging and Byte Patching

The next manipulation is more of a classic example of cracking and actual command modifications. Above, we didn’t really change the logic of the program flow, just exploited some knowledge of how languages handle string manipulation.

After the standard Sql query is made, the recordset either contains a record, or it doesn’t. If not, then its an invalid login. In the end...this comes down to one processor command. Valid? go on, Invalid? Stop and warn

Because of the unique string used for the message box warning, it is actually extremely simple to locate this processor command. Running the executable through a debugger you you can find the go-nogo command very easily by just scrolling up from the API call for the messagebox.

The debug window shown above lists the raw assembler commands that make up the executable. For vb developers, this view of their application is worlds away from they see and write. The top line highlighted you see the conditional jump that will bypass the failed login message if there were no records returned from the query.

What if this conditional jump was turned into an unconditional one thus making any login, good or bad goto the login verified code instead?

Such a manipulation is trivial and can be made right in the debugger.

If you tried it out, you will find that the program now crashes unexpectedly on us with some recordset related error. Ok, so this isn’t quite as easy as it could be. Apparently because it takes a filled recordset to bypass the failed login message, some results from that recordset are also being used to load variables to determine user privilege level or something.

For some application code, just simply switching the kind of jump that is taken will lead to immediate results. Such would be the result in a simpler case where there was just a test like

if userGroup <> Administrator then 
	Msgbox "You do not have permission to perform this action"
	exit sub
else
	'do some privileged stuff
end if 
As for our login example...Is this a stumbling block? yes, Does it put us out of the game? No. With some time and experimentation we could look through the disassembly and try to make sense of what was going on. In our case, to bypass the login using this technique, we have to not only modify the type of jump that is executed, we also have to modify the address it takes us to.

We know it does some recordset manipulations that are messing us up but don’t really know what its trying to do. A logical assumption would be that its trying to get some information for our user level to use through out the rest of the program.

Going back to our debugger, we stop execution to right after our patched jump occurs. With out really having to know to much of what is going we can look at what calls to the VB runtime and what strings are being used.

We see the string "group" being loaded. If we find its string offset and change its name, and let it run then we get a new error about not being to find that field in the recordset. So we know the error has to do with recordset manipulation trying to access the group the logged in user is from.

If we scroll down a ways in the debug window we see this

You dont really have to understand assembler to see what is going on, Look at the names of the highlighted VB runtime calls. We can see that some object is being accessed, then freed, then finally at the bottom a new object is being created with a couple arguments (the two pushs before the call to _vbaNew)

We cant know for sure, but its a good shot in the dark to try to just jump directly to where the grey highlighted line where it is preparing for the _vbaNew function call. At this point in the code the recordset looks like it has been disposed of, and something new is happening. What we are hoping is hat we aren’t jumping over any critical initialization code, and that a default value for whatever usergroup variable there is valid.

Going back to the place where we patched our jump, we now change the address it jumps to to be our new location of 40AD6A. Running the program again and hey were in.

If you would like to try this patch for yourself fire up the PatchManager program and select the first option and hit patch it. You will now be able to login with any username and password combination with the default level privledges.

Because of the extra guess work and fishing we had to do to dodge the subsequent recordset query, we got kind of lucky. Had the post jump code been much more complex bypassing the login with this method could have become a very very arduous task. As is, figuring out where to make it jump to bypass the recordset took me on the order of an hour or so with trying to play with a couple different techniques to get through it. (what I really wanted to do was to insert my own error handler into the function mais c'est la vie oui?)