ElasticSearch Groovy指令碼遠端程式碼執行漏洞分析(CVE-2015-1427)

wyzsk發表於2020-08-19
作者: lupin · 2015/03/04 15:17

作者:京東安全團隊 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

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章