MongoDB操作&&注入漏洞&&未授權訪問漏洞

  • 2019 年 10 月 8 日
  • 筆記

注入不止有傳統的SQL資料庫,NoSQL型資料庫也一樣存在注入漏洞,在比賽中跟傳統的注入相比也算新題型,不少同學可能還不太了解,本文向大家科普MongoDB資料庫的常見操作以及攻擊的方法——NoSQL注入和未授權訪問

MongoDB簡介

MongoDB中,沒有 表、列的概念,取而代之的是 集合(collection)、文檔(document)

  • 庫(DB):
  • 資料庫,包含多個集合
  • 集合(Collection):
  • 一組文檔
  • 文檔(Document):
  • 鍵值對的一個有序集,即有序的哈希表

MongoDB內置 JavaScript解釋器,它的文檔是 JS中的對象( {...}),就是那種沒成員函數的對象

MongoDB默認運行於 27017

MongoDBBSON格式保存數據,即 BinaryJson


簡單操作

CRUD操作

文檔插入後會自動添加一個 _id屬性,為唯一標識符 {"_id":ObjectId("56064886ade2f21f36b03134")}


Create

創建資料庫

> use [newdbname]        //不存在則會創建

創建collections

db.createCollection()

創建集合

集合的 insert方法,插入單個文檔

db.collection.insert({      key1: value1,      ley2: value2  })

Read

集合的 find方法,第一個參數是用來確定返回的文檔,第二個參數確定返回的鍵值的過濾條件

db.collection.find()    // 返回全部文檔  db.collection.find({"age": 20})        // 按條件查詢  db.collection.find({      "age": 20,      "name": "iv4n"  })        // condition1 && condition2

查詢條件

$lt

$lte

$gt

$gte

$ne

<

<=

>

>=

!=

db.collection.find({      "age": {"$gt": 18, "$lt": 30}  })            // 返回年齡18< <30

$in

$nin

$or

$not

$size

指定列表,成員可為不同類型

不在列表中

條件邏輯或

元操作符,可用於其餘任何條件

大小

db.collection.find({      "number": {"$in": [1, 2, 3]}  })  db.collection.find({      "$or": [{key1: value1}, {key2: value2}]  })  db.collection.find({      "$not": {"age": 20}  })  db.collection.find({      "name": {"$size": 4}  })

null值

db.collection.find({key1: null})        // 返回所有無key1鍵的文檔

RegExp

db.collection.find({"name": {"$regex": /^[a-z]{0,4}$/i}})        // 可加入正則flag位,如i忽略大小寫

$slice,返回鍵中數組切片

db.collection.find({},                    {"comments": {"$slice": 10}})        // 返回前十切片  db.collection.find({},                    {"comments": {"$slice": 10}})        // 返回後十切片  db.collection.find({},                    {"comments": {"$slice": [23, 10]}})        // 返回24~33切片,神奇的左開右閉

$where,危險語句,可執行任意 JS函數

db.collection.find({"$where": function(){                     for (var i in this){                         for (var j in this){                             if (i!=j && this[i]==this[j])                                 return true;                         }                     }                     return false;                     }})

本質其實就是傳入一個返回值為 bool的匿名函數,對文檔進行選擇

Update

修改器

$inc,原子操作,並發安全

db.collection.update({key: value}, {"$inc": {a: 1}})        //find操作後鍵值a會自增1

$set,存在則修改,不存在則創建,就像sql語句的 REPLACE一樣

db.collection.update({"_id": "xxxxxx"}, {"$set": {key: value}})

$unset,set的逆操作

$push,修改文檔數組,因為 JavaScript的數組增刪元素就是用 push& pop

$each,批量修改數組

db.collection.update({}, {"$update": {key: {"$each": [val1, val2, val3]}}})

Delete

db.collection.remove()        //刪所有文檔  db.collection.remove({key: value})        //指定查詢條件  db.collection.drop()        //刪集合  db.dropDatabase()        刪庫

NoSQL注入

我將 MongoDB裝在我的 Debian9虛擬機上,創建了以下內容:

> use sqli  switched to db sqli  > db  sqli  > db.createCollection("users")  { "ok" : 1 }  > db.users.insert({"uname": "admin", "passwd": "admin"})  WriteResult({ "nInserted" : 1 })  > db.users.insert({"uname": "iv4n", "passwd": "iv4n"})  WriteResult({ "nInserted" : 1 }  > db.users.find()  { "_id" : ObjectId("5ba3412314139eac63f891f7"), "uname" : "admin", "passwd" : "admin" }  { "_id" : ObjectId("5ba3424114139eac63f891f8"), "uname" : "iv4n", "passwd" : "iv4n" }

以下是PHP程式碼,這裡是PHP 7.x,和5.x的mongoDB庫使用有差異:

<?php  // init mongoDB engine  $server = new MongoDBDriverManager("mongodb://localhost:27017");    $uname = $_POST["uname"];  $passwd = $_POST["passwd"];  $filter = ["uname" => $uname,            "passwd" => $passwd,  ];  $option = [      "projection" => ["_id" => 0],      "sort" => ["_id"=> -1],  ];    $query = new MongoDBDriverQuery($filter, $option);  $cursor = $server->executeQuery("sqli.users", $query);  foreach ($cursor as $doc){      if ($doc->passwd == $passwd){              echo "Login Successfully!<br>";              echo "username is: ".$doc->uname."<br>";              echo "password is: ".$doc->passwd."<br>";      } else {              echo "Invalid user and pass";                                                   }  }  ?>

index頁面為一個post方法登錄

這裡的 filter為查詢條件,語句為 db.users.find({"uname":$uname,"passwd":$passwd})

正常登錄,用戶名密碼錯誤無回顯


$ne選擇器注入,返回所有不等於的 document,傳入資料庫的語句實際是 db.users.find({"uname":{"$ne":"a"},"passwd":{"$ne":"a"}})

可以看到,返回了資料庫的全部資訊


$lt/$gt注入:

前面說過,MongoDB內置的是Javascript的解釋器,所以它在字元串的大小判斷也遵循JS的邏輯

JS的字元串大小判斷邏輯:按位元組從左比較ascii碼,假如相等則比較下一位元組,不等則返回當前位的比較結果

所以我們可以利用大小操作符來注入:


正則注入:

$regex,傳入資料庫的語句實際為 db.users.find({"uname":{"$regex":"^a"},"passwd":{"$ne":"a"}})

可以看到,返回了以a開頭的用戶資訊,實際上它和SQL的正則盲注是一樣的道理


上述的注入例子還相對更安全,PHP5版本的mongoDB庫是允許代入查詢字元串的,那樣會導致更多的注入漏洞(就像SQL注入的閉合、注入,相信大家都比我懂,就不獻醜了)


未授權訪問

MongoDB最初安裝部署後是不會添加auth選項的,一般的初始化步驟是:

  1. 不開啟auth選項時連接資料庫,添加管理員賬戶
  2. 開啟auth,利用管理員帳號登錄連接,添加資料庫賬戶

但是很多開發者並不知道這些Tips,沒有開啟auth選項,且資料庫監聽了公網,就導致了MongoDB的未授權訪問

其實MongoDB的未授權訪問和Redis資料庫是差不多的,這裡我們利用一個工具NoSQLMap來進行資料庫資訊枚舉,有SQLMap那麼也就有針對NoSQL資料庫的NoSQLMap,它可以注入以及利用未授權訪問漏洞

我將資料庫不開啟auth啟動,然後NoSQLMap的1選項設置想關資訊

接著點擊2選項,提示存在未授權訪問漏洞

點擊枚舉資料庫資訊

這個工具目前來說還不是很完善,動不動就會直接exception退出,和SQLMap相比差距還是很大。更多的功能等待大家去探索