views:

799

answers:

8

Background

I've been contracted to analyze an existing Data Provider and I know the following code is faulty; but in order to point out how bad it is, I need to prove that it's susceptible to SQL injection.

Question

What "Key" parameter could break the PrepareString function and allow me to execute a DROP statement?

Code Snippet

Public Shared Function GetRecord(ByVal Key As String) As Record
    Dim Sql As New StringBuilder()

    With Sql
        .Append("SELECT * FROM TableName")
        If String.IsNullOrEmpty(Agency) Then
            .Append(" ORDER BY DateAdded")
        Else
            .Append(" WHERE Key = '")
            .Append(PrepareString(Key))
            .Append("'")
        End If
    End With

    Return ExecuteQuery(Sql.ToString())
End Function

Public Shared Function PrepareString(ByVal Value As String) As String
    Return Value.Replace("''", "'") _
                .Replace("'", "''") _
                .Replace("`", "''") _
                .Replace("´", "''") _
                .Replace("--", "")
End Function
A: 

You are trying to black list characters to implement your own version of SQL Escaping. I would suggest reviewing this URL - SQL escaping is not necessarily the worst choice (i.e. quickly fixing existing apps) but it needs to be done right to avoid vulnerabilities.

That URL links to another page for escaping in SQL Server where the author gives suggestions that help you avoid vulnerabilities without limiting functionality.

If it helps, the articles suggest escaping braces too (I call them square brackets - but []).

Mayo
No. What I am trying to do is break this code. That's what I am trying to do.
Josh Stodola
+1  A: 

This MSDN article covers most of the stuff you need to look out for (I'm afraid to say all when it comes to SQL injection).

But I will echo everyone else's sentiment of parameters parameters parameters.

As for your example some gotchas [Edit: Updated these]:

  • wouldn't the string "1 OR 1=1" allow the user to get back everything

  • or worse "1; drop table sometablename"

According to the article you want to check for:

; - Query delimiter.

' - Character data string delimiter.

-- - Comment delimiter.

/* ... / - Comment delimiters. Text between / and */ is not evaluated by the server.

xp_ - Used at the start of the name of catalog-extended stored procedures, such as xp_cmdshell.

brendan
If I was writing the code, you can believe that's how it would be.
Josh Stodola
Updated with a possible gotcha in your code, will try to think of more.
brendan
no, the clause WHERE Key = '1 OR 1=1' doesn't allow the user to get back everything because there are quote marks around the '1 OR 1=1'.
Jacob
What horrible woolly-thinking advice in that article. Removing semicolons won't help you if an attacker can break out of a string literal because there are many other bad things you can put in an SQL statement. And if they *can't* break out of a string literal because the quotes are properly escaped (or, hopefully, because parameterisation is in effect), it will only be mauling perfectly legitimate input.
bobince
bobince, Look at the 2nd character delimiter in the article: ' - Character data string delimiter.
scurial
A: 

If you try to use your code some one could pass a key (;select * from table; and get a list of what ever table they want.

In your code your not checking for the semi-colon which lets you end a t-sql statement and start another one.

I would go with a parameterized query.

Chuckie
I have a testing environment setup to try and break this. That string does not do anything but throw an exception because of invalid syntax.
Josh Stodola
+13  A: 

To answer your questionable question, no it wouldn't work.

.Replace("``", "''") would prevent legitimate queries with '`'

.Replace("´", "''") would prevent legitimate queries with '´'

.Replace("--", "") would prevent legitimate queries with '--' in them

.Replace("''", "'") would incorrectly modify legitimate queries with '''' in them

and so on.

Furthermore, the full set of escape characters can vary from one RDBMS to another. Parameterized queries FTW.

Yann Schwartz
agreed, there is no 100% surefire way to escape a querystring. They will just build a better mousetrap.
Neil N
What value passed in as the key would cause me to execute any SQL I want? I'm not worried about rare queries that will not work, but malicious queries that can do serious harm.
Josh Stodola
Josh, the point is, people find new ways to exploit canonization issues, and preventing them this way is bound to fail. It's like trying to be clever in escaping HTML tags, or file paths. Sometimes a null character inserted in the right place can fool the sanitization code, and so on. You have to rely on the underlying paramaterization mechanism and cross your fingers it works (or that it will be patched in due time).
Yann Schwartz
Furthermore, as I've said, the sanitization function do yield false positives and break legitimate queries, which is a problem too (just try to take a plane nowadays to feel how painful it can be).
Yann Schwartz
+1 don't mangle unrelated characters. Escape only the characters that need to be escaped. For SQL Server and standard ANSI SQL this is **solely** the string delimiter `'`. For MySQL and occasional others, the backslash can also be a problem. You would also want to ensure there aren't any control characters in the string (MySQL doesn't like a 0 byte), but hopefully you have already filtered those out of your input anyway.
bobince
@Yann I agree with you 100%. That said, I want ONE example of a string that lets me execute any SQL I want. You seem very very confident that the given code allows it, so please just provide ONE sample "Key" parameter that exploits the seriousness. That's all I ask. I know this is bad, so help me prove just how bad it is. Is that too much to ask?
Josh Stodola
@Josh I already provided one and you decided that wasn't good enough...
BenAlabaster
A: 

I think it's safe (at least in SQL server), and I also think the only thing you actually need to do is s = s.Replace("'", "''"). Of course you should use parameterized queries, but you already know that.

erikkallen
+12  A: 

In answer to your direct question: Does this code prevent SQL injection: No

Here's the proof - push this string through the PrepareString method:

Dim input = "'" & Chr(8) & "; Drop Table TableName; - " & Chr(8) & "-"
Dim output = PrepareString(input)

Console.WriteLine(input)
Console.WriteLine(output)

I modified the GetRecord method you posted to return the fully prepared SQL string rather than get the record from the database:

Console.WriteLine(GetRecord(output))

And this is the output

Input  = ; Drop Table TableName; --
Output = '; Drop Table TableName; --
Query  = SELECT * FROM TableName WHERE Key = ''; Drop Table TableName; --'

Add 1 extra line of code:

My.Computer.Clipboard.SetText(input)

And you've got the string you need copied right to your clipboard to paste into your input field on the website to complete your SQL injection:

'; Drop Table TableName; - -

[Noting that the control characters have been omitted from the post output by StackOverflow, so you'll have to follow the code example to create your output]

After the PrepareString method is run, it will have the exact same output - the Chr(8) ASCII code is the backspace which will remove the extra "'" that you're appending to mine which will close your string and then I'm free to add whatever I want on the end. Your PrepareString doesn't see my -- because I'm actually using - - with a backspace character to remove the space.

The resulting SQL code that you're building will then execute my Drop Table statement unhindered and promptly ignore the rest of your query.

The fun thing about this is that you can use non-printable characters to basically bypass any character check you can invent. So it's safest to use parameterized queries (which isn't what you asked, but is the best path to avoid this).

BenAlabaster
Sorry, but it has to be something that can be typed into a textbox on an HTML web page. It might be possible to do this via programmatic POST, but that also brings up an issue with ASP.NETs event validation model, which the site in question does utilize. Thank you very much for your efforts though, this is good stuff here!
Josh Stodola
Additionally, I should add that simply adding another `.Replace(Chr(8), "")` will take care of that. What I mean is: it's not exactly cold-hard evidence to convince a (stubborn) manager that converting 200,000 lines of production code to use parameterized queries is worthwhile.
Josh Stodola
How many exploits will convince him? I could do this all night - I could generate a POST from the original page data that you send out and doctor the correct form value to get past the event validation. I can then use a plugin in my web browser so that your server sees it as the same session. Sure replacing Chr(8) would fix *this* exploit, but what about the next one?
BenAlabaster
And to be honest - all you asked for was some code that proved that the method was flawed and I've produced that... and hence answered your question.
BenAlabaster
You could dump the text output to a file and use a hex editor to cut and paste it into the textbox on the site - that would effectively remove the need for the browser plugin... but would require a hex editor...
BobTheBuilder
Or just add a single extra line of code to your generator which copies it to the clipboard... it would almost be amusing to create a WinForms application to allow you to insert the control characters on the fly.
BenAlabaster
@Josh: I updated my code snippet to take care of your concern that the output couldn't be put into a textbox on your webpage - with one line of code the string is written to the clipboard ready to be pasted directly into the form. Now it's just a case of coming up with other string exploits to get through your PrepareString method... anyway, I think I've proven my point.
BenAlabaster
If your manager is that stubborn, no amount of examples will convince him. He'll just take the "well, just replace chr(8) as well" track that you used above. You're fighting a losing battle.
Donnie
-1. This "proof" only *appears* to exploit the code because the chr(8)'s are processed as actual backspaces by the Console. If you put those characters into a string and copy them to the clipboard as you suggest, they remain as characters in the string and DO NOT eat any characters.
Jon Seigel
A: 

I think it is unhackable if you just replace ' with ''. I have heard that it is possible to change the escape quote character, which could potentially break this, however I am not sure. I think you are safe though.

Shawn Simon
A: 

How to Prevent SQL Injection for Web Application in Java Coding

Can't Solve Sql Injection in java Coding from web Application?. How to prevent single code for injection or something like Union ..... is there any method or function to prevent sql injection in java? what is the best secure to prevent for SQL Injection.

Paut si
What?!?!?!?!?!?!?
Josh Stodola