Mysql_escape_string: the charset vulnerability

The mysql_escape_string is a deprecated and vulnerable PHP function used to sanitize the user input before it reaches the mysql query.
It escapes most of special character that can be used by a malicious user to perform SQLi.

This is an exampre of how the function works:

root@bt:~# cat /tmp/esc_str.php
<?
        function escape_str($s)
        {
                $mystr = mysql_escape_string($s);
                echo "mystr is: " . $mystr . "\n";
        }
         escape_str(" ' \ ; A B ! % ");
?>

root@bt:~# php /tmp/esc_str.php
mystr is:  \' \\ ; A B ! %

In spite of this, as you can see, some sensible chars aren’t escaped like the % that can be useful in a LIKE query.

The mysql_escape_string have some vulnerability partially patched with the mysql_real_escape_string.
Particularly mysql_escape_string don’t require authentication and can be insert before the mysql_connect function.
This means that it doesn’t verify the database character encoding, but analyzes and sanitizes the string one byte at time also if the batabase encoding is multi bytes (GBK, UTF-8, Big5).

Take a look at this example of mysql authentication using PHP code:

function mysqllogin(){
   $db_name  = "db0001";
   $tbl_name = "tb0023";
   $user = mysql_escape_string($_POST["login_user"]);
   $pswd = mysql_escape_string($_POST["login_pswd"]);
   mysql_connect("127.0.0.1", "root", "toor")or die("No MYSQL connection");
   mysql_query("SET CHARACTER SET 'gbk'");
   mysql_select_db("$db_name")or die("No DB connection");
   $sql = "SELECT COUNT(*) FROM tb0023 WHERE user='$user' and pswd='$pswd'";
   $rsql = mysql_query($sql) or die(mysql_error());
   $rw = mysql_fetch_row($rsql);
   if ($rw[0]) {bf
      return 1;
   } else {
      return 0;
   }

If we suppose that the db encoding is GBK (in this case I forced it in the PHP code: mysql_query("SET CHARACTER SET 'gbk'")) we can try to take advantage of the use of different encoding type.
First of all let’s try to use PHP to see difference between GBK and ASCII, before and after mysql_escape_string()
Use the follow php sample:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ascii">
<title>PERU.local</title>
</head>

<body style="font-family:Verdana;color:#9bbbcb;">
<div style="text-align:center">

<div style="text-align:center;color:#ff0000;">
=========================================================<br>
This is a php page with ASCII charset<br>
---------------------------------------------------------<br>
The string passed to mysql_escape_string is \xBF\x27<br>
The output of mysql_escape_string is \xBF\x5c\x27<br>
<br>
=========================================================<br>
</div>
<div align="center">
<table style="margin-top:50px;">
<tr>
<td style="text-align:right">
<?
$lol =  "\xbf\x27";
$lol2 = mysql_escape_string($lol);
?>
<strong>--------</strong>
</td>
<br><br>
<? echo "This is the string before mysql_escape_string: " . $lol ; ?><br>
<? echo " --- "; ?><br>
<? echo "This is the string after mysql_escape_string: " . $lol2; ?><br>
<td style="text-align:left">
</table>
</div>
</form>
</div>
</body>
</html>
ascii

Image 1

Now modify the previous sample and load it:

...
<meta http-equiv="content-type" content="text/html; charset=gbk">
...

gbk

Image 1


As you can see in image 2 the escape (\) char is no longer displayed, this because the mysql_escape_string output is \xbf\x5c\x27
This output is encoded by ASCII (a single character charset) as “\xbf” (an inverted question mark), “\x5c” (\ the escape char) and “\x27” (‘ a single quote).
On the other side (gbk – multibytes) the output is encoded in “\xbf\x5c” (a kanji) and \x27 (‘ a single quote).

Ok, the string that reaches the MySQL will be \xbf\x5c\x27; now, if the charset on MySQL is GBK, the behaviour will be the same of the PHP page: a kanji and a single quote that is what we need for a SQLi.
In the image 3 you can see actually the result:

sqli

Image 3

The only encoding I found to be vulnerable are GBK and BIG5 because are the only that have \x5c as second byte of an allowed character.
But you can explore more by referring to this site.
Also I can’t find a way to force the DB chatset before MySQL connection, so I suppose that SQLi can be reached only if the GBK is already the BD charset.

You can try to read the following posts to get more info about mysrl_real_escape and this kind of vulnerability:
http://security.stackexchange.com/questions/8028/does-mysql-escape-string-have-any-security-vulnerabilities-if-all-tables-using-l
http://ilia.ws/archives/103-mysql_real_escape_string-versus-Prepared-Statements.html
http://stackoverflow.com/questions/5741187/sql-injection-that-gets-around-mysql-real-escape-string
http://stackoverflow.com/questions/3665572/mysql-escape-string-vs-mysql-real-escape-string
http://www.sans.org/reading-room/whitepapers/application/web-application-injection-vulnerabilities-web-app-039-s-security-nemesis-34247

2 Responses to Mysql_escape_string: the charset vulnerability

  1. […] Username and password are escaped before they are used in the querystr above. This means any apastroph(single quote) is escaped as well. I found a blog describing this very issue here: mysql_escape_string-the-charset-vulnerability. […]

  2. On September 29, 2015 at 01:22 carlos said:

    I’m trying to play your example. But in both cases’ it is escaped. I find the way to introduce SQL injection. If I force the conezion to GBK if it works, but not how to force this without changing the file

Leave a Reply

Your email address will not be published.