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.