1. 介绍
Redis是一个广泛使用的Key-Value存储系统,主要用于处理各个高并发场景下的数据读取加速,与MemCache不同的是,Redis具有多种数据结构和灵活管理,而且还支持数据持久化。在Redis中,我们可以通过灵活地动态修改持久化的存储路径的特性,该特性也带来一个问题,当我们的数据中存在一些恶意代码时,那么该恶意代码就能存储到我们指定的路径,从而实现:SSH免密、定时任务启动、修改指定文件信息。
在本文,我们将通过Redis这个特性来放置一段恶意代码,并成功获取我们需要的数据来展示该特性带来的一些问题,然后我们通过 “SeLinux理论在Redis下实践:第1部分
” 介绍的Selinux来限制该特性带来的问题,最后我将列出在这个过程中遇到的一些问题回答和总结,希望通过该文让大家认识到Selinux的先进安全管理机制,如果我们运用得恰当,它将能将许多0 Day 漏洞尽可能限制在一定的范围。
2. 实践目的
目前有一些linux教程,经常指导我们将selinux默认关闭,其实对于linux的整体安全就会大打折扣。在这个实践中,我主要通过利用Redis持久化数据到任意目录文件的漏洞,在一个没有开启selinux的环境中,将一个可以读取/etc/passwd的脚本写到网站的运行目录,从而验证没有selinux下的我们可以读取一些敏感数据的风险点,然后通过一步步地实践如何在selinux下进行防护该漏洞,以让大家对selinux有更深的一个认识。
3. 实验环境与背景
操作系统: | Centos7 |
---|---|
WEB服务器: | Apache/2.4.6 |
PHP: | 5.4.16 |
Redis: | 4.0.9 |
网站目录: | /var/www/html/ |
192.168.1.18:Redis与web服务器放置在同一台机器。
192.168.1.19:客户端
4. 如何进行越权操作
为了达到实验目的,Redis 开启远程访问的配置,并且无密码,并且selinux是处于permissive或者disabled状态。我们先检查确认服务端环境的就绪情况:
root@192.168.1.18# getenforce
Permissive
root@192.168.1.18# ss -antp|grep 6379
LISTEN 0 511 *:6379 *:* users:(("redis-server",pid=3934,fd=6))
在客户端使用redis-cli连接到服务器,并设置保存路径及保存文件,将恶意代码写入到某个test中,最后save把该代码保存到设定的文件。
root@192.168.1.19 # redis-cli -h 192.168.1.18
192.168.1.18:6379>CONFIG SET dir /var/www/html
192.168.1.18:6379>CONFIG set dbfilename test.php
192.168.1.18:6379>set test '<?php var_dump(file_get_contents("/etc/passwd"));?>'
192.168.1.18:6379>save
然后,我们运行该文件,可以查看到/etc/passwd内容
以上就是我们通过redis这个漏洞获取到敏感数据的过程。
通过上面的实验我们可以实现很多非法操作,比如文件读写,定时任务启动等等,虽然可以通过其他方式来规避这个问题,比如加密码之类,但是有一些场景如cluster模式,必须无密码,而且还担心密码泄露,没有能基本上解决非法读取的问题。
5. 如何通过SeLinux来防护:
在服务端将selinux开启,将能将该漏洞限制在有限的范围内,而不会造成敏感数据泄露。
root@192.168.1.18# setenforce 1
root@192.168.1.18# getenforce
Enforcing
此时,我们在客户端就无法再通过该redis漏洞来获取敏感数据了。
192.168.1.18:6379> save
(error) ERR
我们查看一下服务端的selinux日志,可以发现以下内容,证明我们selinux已经实际拦截了。audit2why是一个selinux的日志分析工具,当开启selinux日志服务时(默认开启),日志文件默认存放在/var/log/audit/audit.log:
root@192.168.1.18# audit2why < /var/log/audit/audit.log
type=AVC msg=audit(1553166802.997:6749): avc: denied { dac_override } for pid=3934 comm="redis-server" capability=1 scontext=system_u:system_r:redis_t:s0 tcontext=system_u:system_r:redis_t:s0 tclass=capability
Was caused by:
Missing type enforcement (TE) allow rule.
You can use audit2allow to generate a loadable module to allow this access.
以上就是selinux的强大威力,但是selinux如何更好地为我所用呢?下面以如何将redis持久化数据保存到我们指定目录为例,给大家更加直观、深刻体会selinux的配置灵活性。
如果我们想把数据文件保存到/data/redis/redis.db,下面提供两种方式进行配置
- 在服务端通过chcon 对该文件的selinux 类型修改为redis_var_run_t。
root@192.168.1.18# mkdir -p /opt/redis/
root@192.168.1.18# touch /opt/redis/redis.db
root@192.168.1.18# ls -Zl /opt/redis/redis.db
-rw-r--r--. 1 unconfined_u:object_r:usr_t:s0 root root 0 Mar 22 11:26 /opt/redis/redis.db
root@192.168.1.18# chcon -t redis_var_run_t /opt/redis/redis.db
root@192.168.1.18# ls -Zl /opt/redis/redis.db
-rw-r--r--. 1 unconfined_u:object_r:redis_var_run_t:s0 root root 0 Mar 22 11:26 /opt/redis/redis.db
然后在客户端,我们再尝试一下保持数据
root@192.168.1.19 # redis-cli -h 192.168.1.18
192.168.1.18:6379>CONFIG SET dir /opt/redis/
192.168.1.18:6379>CONFIG set dbfilename redis.db
192.168.1.18:6379>save
OK
- 通过semange fcontext进行添加后并重置权限:
root@192.168.1.18# semanage fcontext -a -t redis_var_run_t "/opt/redis(/.*)?"
root@192.168.1.18# restorecon -Rv /opt/redis/
6. 常见问题:
- 为何开启selinux后,我的redis还是无法防护呢?
通过ps -auxZ 查看redis-server 的类型是否为:unconfined_troot@192.168.1.18# ps auxZ|grep redis unconfined_u:unconfined_r:unconfined_t:s0 ...
如果是该提示,则是redis-server启动不能通过bash进行启动,默认的 bash 环境是不受 SELinux 管制的~因为 bash 并不是什么特别的网络服务!因此,在这个不受 SELinux 所限制的 bash 程序所产生的文件, 其身份识别大多就是 >unconfined_u 这个“不受限”用户啰!
因此,需要通过systemctl start redis 来启动。
- 添加的上下文(context)在哪里呢?
默认目录在:/etc/selinux/targeted/contexts/files
这里有系统自带的一些默认的上下文资源,而我们自定义的上下文context放置到文件:file_contexts.local
7. 总结:
通过以上的实验,我们可以体验到一个漏洞的利用威力及其影响面是巨大的。也由于selinux配置灵活性,导致Selinux的配置并不直观,在一些概念上理解比较抽象,因此很多人都默认选择了关闭该功能。希望通过这两篇文章来帮助大家重新认识selinux,并希望一起打造更加安全的linux服务环境。
8. 参考:
Redis漏洞,远程攻击:https://www.cnblogs.com/shuaiandjun/p/8425994.html
由于时间关系,行文可能存在错误或者有其他建议及问题,欢迎大家与我联系:一虚道长(lailaiji@163.com)