1

ElasticSearch Groovy脚本远程代码执行漏洞分析(CVE-2015-1427) | WooYun知识库

 6 years ago
source link:
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ElasticSearch Groovy脚本远程代码执行漏洞分析(CVE-2015-1427)

作者:京东安全团队 Lupin

0x00 前言


ElasticSearch是一个JAVA开发的搜索分析引擎。2014年,曾经被曝出过一个远程代码执行漏洞(CVE-2014-3120),漏洞出现在脚本查询模块,由于搜索引擎支持使用脚本代码(MVEL),作为表达式进行数据操作,攻击者可以通过MVEL构造执行任意java代码,后来脚本语言引擎换成了Groovy,并且加入了沙盒进行控制,危险的代码会被拦截,结果这次由于沙盒限制的不严格,导致远程代码执行,目前网上还没看到公开的poc,经过一番研究,发现了利用方式,下面来看看漏洞是如何产生的。

Groovy是一种运行在JVM上的脚本语言,语法和java很像,同样可以调用java中的各种对象和方法,但是Groovy的语法更简单。

0x01 细节


首先,我们执行一段带脚本的查询代码:

POST http://127.0.0.1:9200/_search?pretty HTTP/1.1
User-Agent: es
Host: 127.0.0.1:9200
Content-Length: 184

{
"size":1,
    "script_fields": {
        "lupin": {
            "script": "1 + 6"
        }
    }
}

上面的请求中1+6,就是我们执行的脚本代码,下面的返回中的7就是执行结果:

enter image description here

下面再试着执行一下这段代码:

POST http://127.0.0.1:9200/_search?pretty HTTP/1.1
User-Agent: es
Host: 127.0.0.1:9200
Content-Length: 184

{
"size":1,
    "script_fields": {
        "lupin": {
            "script": "new java.lang.ProcessBuilder(\“calc\”)"
        }
    }
}

执行之后,报了错,从错误中可以看到,构造java.lang.ProcessBuilder对象是不允许的:

enter image description here

下面我们看看ElasticSearch沙盒的相关代码,实现沙盒的类是com.elasticsearch.script.groovy.GroovySandboxExpressionChecker,它订制了Groovy的沙盒,对表达式进行了安全检测,但是这个沙盒与JAVA的SecurityManager那种沙盒是不同的,从代码中可以看到这个沙盒,只是根据黑白名单,在表达式语义上判断表达式是否合法的,可以说是一个“浅”沙盒,简单来来说,比如沙盒设置不允许调用shell()这个方法,直接调用shell()方法,沙盒在表达式中发现了shell()这个字符串,就会报非法调用,但是如果有一个方法叫poc()这个方法中调用了shell()方法(poc(){shell()}),当调用poc()方法的时候,shell()方法也就被间接调用了沙盒并不会报错。 具体来说isAuthorized(Expression expression)方法如果返回false,说明表达式非法,true则合法,从isAuthorized方法的实现中可以看到,它根据黑白名单对方法调用和对象构造都进行了检测:

enter image description here

enter image description here

enter image description here

从上面的白名单中可以看到,允许构造对象和方法调用的类,都是一些常规类,没有我们可以利用的类,而且如果我们想要利用反射去调用我们想调用的类,方法黑名单中又限制了getClass的调用,我们无法通过getClass方法获取Class对象,但是我们可以看到方法白名单中,并没有对forName方法进行限制,也就是说,如果我们能获取到Class对象,再调用forName方法就可以获取到我们想访问的类。 那么我们如何能够获取一个Class对象呢?首先我想到的是通过java.lang.String.class这样的方式,通过class让JVM返回String类的class对象,可以看到的确获取到了Class对象:

enter image description here

那么我们在试试可不可以通过这个Class对象调用forName方法,加载java.lang.Runtime这个我们最想要的类,可惜报错了:

enter image description here

出错的原因是java.lang.String这个类是不允许进行方法调用的,只有在上面defaultReceiverWhiteList这个白名单中的类,才可以进行方法调用,这个控制是在Groovy沙盒类org.codehaus.groovy.control.customizers.SecureASTCustomizer中做的判断:

enter image description here

既然这样,思路就又有了,我们就拿这个白名单中的类获取Class对象,然后再调用forName方法,是不是就可以突破这个限制了,我这里使用java.lang.Math这个类来试试,这个类是在recevicer白名单中的,可以看到成功获取了java.lang.Runtime类:

POST http://127.0.0.1:9200/_search?pretty HTTP/1.1
User-Agent: es
Host: 127.0.0.1:9200
Content-Length: 132

{
"size":1,
    "script_fields": {
        "lupin": {
            "script": "java.lang.Math.class.forName(\“java.lang.Runtime\”)"
        }
    }
}

enter image description here

有了Runtime类,后面的事情就好办了要调用到的各种方法都不在方法黑名单里面,我这里就不公布具体POC了,看懂上面原理的同学自然明白:

enter image description here

0x02 参考


http://www.elasticsearch.org/blog/elasticsearch-1-4-3-and-1-3-8-released/

https://github.com/elasticsearch/elasticsearch/blob/4dc060527cd7d35817085a3926e65d071e3b1321/src/main/java/org/elasticsearch/script/groovy/GroovySandboxExpressionChecker.java


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK