Redis未授权访问漏洞

本文详细介绍了Redis未授权访问漏洞的攻击思路,包括如何利用公私钥认证获取ROOT权限,以及漏洞利用的具体步骤。同时,提供了一个Python脚本来检测此类漏洞,通过扫描目标IP段并尝试连接,寻找可能存在风险的Redis实例。防御策略方面,建议禁止远程高危命令,以低权限运行Redis,限制外网访问,并确保authorized_keys文件权限安全。
摘要由CSDN通过智能技术生成

攻击思路

Redis是一种使用ANSIC语言编写的开源Key-Value型数据库。Redis为了保证效率,将数据缓存在内存中,周期性地把更新的数据写入磁盘或者把修改操作写入追加的记录文件中,在此基础上实现了master-slave(主从)同步。

对Redis配置不当将会导致未授权访问漏洞,从而被攻击者恶意利用。如果Redis以root身份运行,攻击者可以用root权限写入SSH公钥文件,通过SSH登录目标服务器,进而导致服务器权限被获取、泄露或发生加密勒索事件,为正常服务带来严重危害。

漏洞利用

漏洞利用的方式有很多,这里我们介绍其中的一种——利用公私钥认证获取ROOT权限

首先我们先在靶机上以root身份启动redis服务,命令为redis-server /etc/redis.conf,安装redis请读者上网查阅资料,这里只讲述攻击方法
在这里插入图片描述
然后我们在kali上生成一个ssh空密钥
在这里插入图片描述
进入/root/.ssh目录下查看生成结果,并将公钥导入到txt文件中
在这里插入图片描述
将txt文件中的公钥导入Redis缓存中
在这里插入图片描述
连接目标主机,更改配置文件路径为/root/.ssh,设定文件名称为authorized-keys
在这里插入图片描述
通过SSH协议连接到远程目标主机
在这里插入图片描述

检测方法

先编写程序的起始部分

#程序起始部分
if __name__ == '__main__':
    try:
        start(sys.argv[1:])
    except KeyboardInterrupt:
        print("interrupted by user, killing all threads...")

然后编写命令行参数处理功能,opts为一个两元组列表,如果没有附加参数则为空串

#编写命令行参数处理功能
def start(argv):
    dict = {}
    url = ""
    type = ""
    if len(sys.argv) < 2:
        print("-h 帮助信息; \n")
        sys.exit()
    #定义异常处理
    try:
        banner()
        opts,args = getopt.getopt(argv, "-u:-p:-s:-h")
    except getopt.GetoptError:
        print('Error an argument!')
        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(usage())
    launcher(url,type,port)

编写帮助信息

#banner信息
def banner():
    print('\033[1;34m########################################################################################\033[0m\n'
          '\033[1;34m######################################\033[1;32mRedis未授权访问漏洞\033[1;34m####################################\033[0m\n'
          '\033[1;34m########################################################################################\033[0m\n')

接下来是Redis漏洞检测的核心代码,此处通过socket函数尝试连接远程主机的IP及端口号,发送payload字符串。利用recvdata函数接收目标主机返回的数据

#未授权函数检测
def redis_unauthored(url,port):
    result = []
    s = socket.socket()
    payload = "\x2a\x31\x0d\x0a\x24\x34\x0d\x0a\x69\x6e\x66\x6f\x0d\x0a"
    socket.setdefaulttimeout(10)
    for ip in url:
        try:
            s.connect((ip, int(port)))
            s.sendall(payload.encode())
            recvdata = s.recv(1024).decode()
            if recvdata and 'redis_version' in recvdata:
                result.append(str(ip)+':'+str(port)+':'+'\033[1;32;34msuccess\033[0m')
        except:
            pass
            result.append(str(ip) + ':' + str(port) + ':' + '\033[1:31;34mfailed \033[0m')
        s.close()
    return(result)

然后针对该IP段进行网络主机检测。该部分代码主要以特殊字符"-"为目标进行分隔,将分隔后的字符进行for循环存入列表中,以便被函数redis_unauthored调用

#执行URL
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 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 usage():
    print('-h: --help 帮助;')
    print('-p: --port 端口;')
    print('-u: --url 域名;')
    print('-s: --type Redis')
    
#输出结果格式设计
def output_exec(output,type):
    print("\033[1;32;40m"+type+"......\033[0m")
    print("++++++++++++++++++++++++++++++++++++++++++++++++")
    print("|         ip         |    port   |     status  |")
    for li in output:
        print("+-----------------+-----------+--------------+")
        print("|   "+li.replace(":","   |    ")+"  | ")
    print("+----------------+------------+---------------+\n")
    print("[*] shutting down....")

最后设置一个漏洞回调函数

#漏洞回调函数
def launcher(url,type,port):
    #未授权访问类型
    if type == "Redis":
        output=redis_unauthored(url_exec(url),port)
        output_exec(output,type)

附上完整代码以免疏漏

import sys
import getopt
import socket

#编写命令行参数处理功能
def start(argv):
    dict = {}
    url = ""
    type = ""
    if len(sys.argv) < 2:
        print("-h 帮助信息; \n")
        sys.exit()
    #定义异常处理
    try:
        banner()
        opts,args = getopt.getopt(argv, "-u:-p:-s:-h")
    except getopt.GetoptError:
        print('Error an argument!')
        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(usage())
    launcher(url,type,port)

#banner信息
def banner():
    print('\033[1;34m########################################################################################\033[0m\n'
          '\033[1;34m######################################\033[1;32mRedis未授权访问漏洞\033[1;34m####################################\033[0m\n'
          '\033[1;34m########################################################################################\033[0m\n')

#使用规则
def usage():
    print('-h: --help 帮助;')
    print('-p: --port 端口;')
    print('-u: --url 域名;')
    print('-s: --type Redis')

#未授权函数检测
def redis_unauthored(url,port):
    result = []
    s = socket.socket()
    payload = "\x2a\x31\x0d\x0a\x24\x34\x0d\x0a\x69\x6e\x66\x6f\x0d\x0a"
    socket.setdefaulttimeout(10)
    for ip in url:
        try:
            s.connect((ip, int(port)))
            s.sendall(payload.encode())
            recvdata = s.recv(1024).decode()
            if recvdata and 'redis_version' in recvdata:
                result.append(str(ip)+':'+str(port)+':'+'\033[1;32;34msuccess\033[0m')
        except:
            pass
            result.append(str(ip) + ':' + str(port) + ':' + '\033[1:31;34mfailed \033[0m')
        s.close()
    return(result)

#执行URL
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 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 output_exec(output,type):
    print("\033[1;32;40m"+type+"......\033[0m")
    print("++++++++++++++++++++++++++++++++++++++++++++++++")
    print("|         ip         |    port   |     status  |")
    for li in output:
        print("+-----------------+-----------+--------------+")
        print("|   "+li.replace(":","   |    ")+"  | ")
    print("+----------------+------------+---------------+\n")
    print("[*] shutting down....")

#漏洞回调函数
def launcher(url,type,port):
    #未授权访问类型
    if type == "Redis":
        output=redis_unauthored(url_exec(url),port)
        output_exec(output,type)


#程序起始部分
if __name__ == '__main__':
    try:
        start(sys.argv[1:])
    except KeyboardInterrupt:
        print("interrupted by user, killing all threads...")

实现效果如下图所示
在这里插入图片描述

防御策略

  • 禁止远程使用高危命令
  • 低权限运行Redis服务
  • 禁止外网访问Redis
  • 阻止其他用户添加新的公钥,将authorized_keys的权限设置为对拥有者只读
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平凡的学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值