COMMAND

    Postaci

SYSTEMS AFFECTED

    Postaci

PROBLEM

    Berk  Demir  found  following.   Popular  webmail software Postaci
    (ships with Debian)  lacks of checking  for malicious SQL  code in
    variables coming  from user  while deleting  addressbook contacts,
    bookmarks and notes.  This gives opportunity to malicious user  to
    execute arbitrary SQL query.

    Postaci  (Turkish  word  for  Postman)  is a multiplatform GPL' ed
    webmail   software   which   is   database   independent   (MySQL,
    mSQL,PostgreSQL, Sybase, MS SQL), multilanguage(Turkish, English),
    POP3/IMAP and  fully MIME  compatible.   While using  POP3 mode to
    fetch messages it uses  database fields to simulate  IMAP folders,
    on which you can save, move, delete,read e-mail.

    The problem affects Postaci  if using PostgreSQL as  your database
    backend.   MySQL seems  to bo  not affected  by this  way.   PHP's
    mysql_query() function code does NOT allow query strings including
    a  semicolon  inside,  for  passing  multi  queries.   The  method
    illustrated  here  uses  query  seperating  with  a semicolon, but
    someone can  find a  suitable malicious  SQL code  to also exploit
    Postaci while using MySQL as the db backend.

    The main problem is not  just with Postaci, it's with  the general
    manner of PHP coding exercises.  Both in POST and GET methods, PHP
    sets the remote variable name  to a global variable with  the same
    name in the  executing PHP script  (defined in the  "action" field
    of HTML form).

    If you have a  <input  type="text" name="foo">  line in your  HTML
    form;  after  submission,  you'll  be  available  to reach its set
    value with the variable named "$foo" in your PHP script.

    Trusting the user input in this era is just the big problem.   For
    example: while expecting  an integer type  content for $foo,  user
    can input  a malicious  string that  can cause  compromise of your
    SQL query.

    Let's focus to Postaci code.

        --- deletecontact.php ---
        1    // security check
        2    $dbq = $db->execute("select user_id from tblAdressbook where
        item_id=$item_id");
        3    $auth_user = $dbq->fields['user_id'];
        4    if ($auth_user != $user_id) {
        5      Header("Location: index.php?error_id=1");
        6    }
        7    $dbq->close();
        --- deletecontact.php ---

    In the relatively numbered line  1, we see a magical  comment: "//
    security check".   This security  check is  really needed  but not
    enough unfortunately.  Lines  2,3,4,5 ensures that, anyone  who is
    not the owner of the contact item, can not delete it.  Up to here,
    everything is O.K. but the implicit trust to user input.  As  seen
    from the  code snippet,  variable $item_id  is never  checked.  It
    must be  an integer  naturally bu  as we  discussed above,  it's a
    user  input  and  it  can  be  everything.   Suppose that variable
    "$item_id" includes the string  "144 OR TRUE; select  user_id from
    tblAdressbook where item_id=144"

    144 is a real contact  item id associated with the  current logged
    in user.

    The SQL Query:

        select user_id from tblAdressbook where item_id=144 OR TRUE; select user_id from tblAdressbook where item_id=144

    ... will return all the user_id's also ours at the top row of  the
    result.  At line  3, "$dbq->fields['user_id']" just points  to the
    "user_id" column of first row of the returned result.

    So we managed to pass the first security barrier.

    Let's look at the code comes after:

        --- deletecontact.php ---
        8      if ($log_id == ""){
        9        Header("Location: index.php?error_id=1");
        10     } else {
        11
        12       $dbq = $db->execute("delete from tblAdressbook where item_id=\
        13                      $item_id and user_id = $user_id");
        14       $dbq->close();
        15
        16       Header("Location: adressbook.php");
        17     }
        --- deletecontact.php ---

    At line 12, Postaci still trusts  the user input and puts it  into
    the deleting query.  Flashback to our malicious $item_id variable:

        "144 OR TRUE; select user_id from tblAdressbook where item_id=144"

    Then the composed query becomes:

        delete from tblAdressbook where item_id=144 OR TRUE; select user_id from tblAdressbook where item_id=144 and user_id = [your user id]

    In here PHP code of mysql_query won't let you pass a query  string
    including a semicolon but  this doesn't apply for  Postgresql (and
    maybe for MS SQL, Sybase, msql).

    When we  focus to  the first  query in  the query  string, we  can
    clearly see  that, it  will delete  all the  records in the table.
    Voila!  We're done.

    For the {lamer | impatient | lazy |etc...}:

        http://a.postaci.running.host/deletecontact.php?item_id=[legitimate_item_id]+OR+TRUE+;+SELECT+item_id+FROM+tblAdressbook+WHERE+item_id=[some id]

    Here   we   used   "SELECT   item_id   from   tblAdressbook  WHERE
    item_id=[some id]" as the second query.  Of course it can be  more
    dangerous.  It can be "DROP  [some data base]" if the current  SQL
    user have  rights to  do so.   Or it  can simply  delete something
    "DELETE from tblMessages" will delete all the saved incoming  mail
    messages and it can be really painful.  Or it can add any  records
    to  any  table  (owned  by  postaci  db  user).  Variations can be
    incremented...

SOLUTION

    Will be fixed.