CVE-2019-0230: S2-059 远程代码执行漏洞分析

  • A+
所属分类:安全文章

更多全球网络安全资讯尽在邑安全

CVE-2019-0230: S2-059 远程代码执行漏洞分析

0x01 漏洞简述

2020年08月13日, 360CERT监测发现Apache官方发布了Struts2远程代码执行漏洞的风险通告,该漏洞编号为CVE-2019-0230,漏洞等级:高危。漏洞评分:8.5

攻击者可以通过构造恶意的OGNL表达式,并将其设置到可被外部输入进行修改,且会执行OGNL表达式的Struts2标签的属性值,引发OGNL表达式解析,最终造成远程代码执行的影响。

对此,360CERT建议广大用户及时将Apache Struts2进行升级完成漏洞修复。与此同时,请做好资产自查以及预防工作,以免遭受黑客攻击。

0x02 风险等级

360CERT对该漏洞的评定结果如下

评定方式 等级
威胁等级 中危
影响面 一般
360CERT评分 8.5分

0x03 影响版本

- Apache Struts2:2.0.0-2.5.20

0x04 漏洞详情

根据官方发布的信息来看,漏洞产生的主要原因是因为Apache Struts框架在强制执行时,会对分配给某些标签属性(如id)的属性值执行二次ognl解析,对于精心设计的请求,这可能导致远程代码执行(RCE)。

官方给出的利用场景如下:

<s:url var="url" namespace="/employee" action="list"/><s:a id="%{skillName}" href="%{url}">List available Employees</s:a>

这里的id属性里的值使用了ognl表达式进行包裹,同时该值如果可控,那么就会因为两次的解析而造成ognl表达式执行。

该分析在较低版本中进行测试,仅对漏洞产生原理进行分析,如在高版本中进行执行命令,需绕过沙箱。

拦截器处理请求值

首先,我们需要明确jsp是如何进行取值的。 在com.opensymphony.xwork2.interceptor.ParametersInterceptor这个拦截器里,会获取我们传入的值。ActionContext存储着当前上下文的请求信息。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

ParametersInterceptor里,会把请求的值存入ValueStack类型的栈里,这里是OgnlValueStack,具体在setParameters里。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

会调用stacksetValue方法进行赋值。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

接着就会进行一系列复杂的处理,获取action指向的jsp。然后开始处理对应的jsp标签。

id标签解析

org.apache.struts2.views.jsp.ComponentTagSupport#doStartTag方法中。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

valueStack里获取相关的请求值,然后调用populateParams方法,该方法对标签进行处理,其中包括id标签。

于是跟进org.apache.struts2.components.Component#setId方法。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

id属性不为null,于是继续跟进findString方法。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

然后跟入findValue,由于altSyntax默认为true(这个功能是将标签内的内容当作`OGNL`表达式解析,关闭了之后标签内的内容就不会当作`OGNL`表达式解析了),所以最终进入translateVariables方法(高版本对应的是evaluate方法)。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

这里会对id,也就是%{skillName}进行ognl表达式执行,执行完剩余部分就是skillNametranslateVariables代码如下:

public static Object translateVariables(char[] openChars, String expression, ValueStack stack, Class asType, TextParseUtil.ParsedValueEvaluator evaluator, int maxLoopCount) {
Object result = expression;
char[] arr$ = openChars;
int len$ = openChars.length;


for(int i$ = 0; i$ < len$; ++i$) {
char open = arr$[i$];
int loopCount = 1;
int pos = 0;
String lookupChars = open + "{";


while(true) {
int start = expression.indexOf(lookupChars, pos);
if (start == -1) {
int pos = false;
++loopCount;
start = expression.indexOf(lookupChars);
}


//防止递归解析
if (loopCount > maxLoopCount) {
break;
}


int length = expression.length();
int x = start + 2;
int count = 1;


while(start != -1 && x < length && count != 0) {
char c = expression.charAt(x++);
if (c == '{') {
++count;
} else if (c == '}') {
--count;
}
}


int end = x - 1;
if (start == -1 || end == -1 || count != 0) {
break;
}


String var = expression.substring(start + 2, end);
Object o = stack.findValue(var, asType);
if (evaluator != null) {
o = evaluator.evaluate(o);
}


String left = expression.substring(0, start);
String right = expression.substring(end + 1);
String middle = null;
if (o != null) {
middle = o.toString();
if (StringUtils.isEmpty(left)) {
result = o;
} else {
result = left.concat(middle);
}


if (StringUtils.isNotEmpty(right)) {
result = result.toString().concat(right);
}


expression = left.concat(middle).concat(right);
} else {
expression = left.concat(right);
result = expression;
}


pos = (left != null && left.length() > 0 ? left.length() - 1 : 0) + (middle != null && middle.length() > 0 ? middle.length() - 1 : 0) + 1;
pos = Math.max(pos, 1);
}
}


XWorkConverter conv = (XWorkConverter)((Container)stack.getContext().get("com.opensymphony.xwork2.ActionContext.container")).getInstance(XWorkConverter.class);
return conv.convertValue(stack.getContext(), result, asType);
}

然后调用stack.findValue方法从stackValuecontext上下文中获取之前传入的该参数的具体值。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

接着将expression值进行重新赋值,赋值为request请求传入的skillName参数的值,比如%{1+1},这里由于有如下判断:

        if (loopCount > maxLoopCount) {
break;
}

此段代码用来防止递归解析ognl,所以最终将%{1+1}赋值给result后,会跳出循环,不再继续解析传入的值,接着往下执行:

        XWorkConverter conv = (XWorkConverter)((Container)stack.getContext().get("com.opensymphony.xwork2.ActionContext.container")).getInstance(XWorkConverter.class);
return conv.convertValue(stack.getContext(), result, asType);

会获取上下文中的ContainerImpl,并实例化XWorkConverter:

CVE-2019-0230: S2-059 远程代码执行漏洞分析

然后调用convertValue,该方法里也没有做ognl解析,判断value不为null并且类型和一开始预定的类型一致,就返回value

CVE-2019-0230: S2-059 远程代码执行漏洞分析

id值二次解析

回退到setId,执行完findString后,将Ancohr.id重新赋值。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

继续returndoStartTag,执行完populateParams方法。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

跟入Anchor.start

CVE-2019-0230: S2-059 远程代码执行漏洞分析

继续跟进evaluateParams,这里会跟据标签做进一步操作。 在判断完一系列标签的值为null之后,会调用populateComponentHtmlId方法。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

该方法再次获取id值,而这里的id值已经是之前经过populateParams方法处理过后的值。

CVE-2019-0230: S2-059 远程代码执行漏洞分析

接着调用findStringIfAltSyntax,传入id值。 根据altSyntax值,判断是否执行findString方法,进入findString之后的步骤就和前面对标签的ognl表达式执行一样,在translateVariables方法执行最终的ognl表达式。

CVE-2019-0230: S2-059 远程代码执行漏洞分析CVE-2019-0230: S2-059 远程代码执行漏洞分析

总结

该漏洞主要是id标签的二次ognl解析产生的,第一次是在解析id标签属性的时候,这时候id的值已经被替换为用户输入,而第二次再次获取id值,此时的id值已经是用户可控的值,这时候就会解析用户输入的ognl表达式。

该漏洞限制条件较多:

1. Struts2标签的属性值可执行OGNL表达式(比如id)。 2. Struts2标签的属性值可被外部输入修改。 3. Struts2标签的属性值未经安全验证。 4. 高版本需绕过沙箱执行命令。 5. useAltSyntaxtrue

0x05 时间线

2020-08-13 Apache Struts2官方发布安全通告

2020-08-13 360CERT发布通告

2020-09-01 360-CERT 发布分析

转自360CERT

欢迎收藏并分享朋友圈,让五邑人网络更安全

CVE-2019-0230: S2-059 远程代码执行漏洞分析

欢迎扫描关注我们,及时了解最新安全动态、学习最潮流的安全姿势!


推荐文章

1

新永恒之蓝?微软SMBv3高危漏洞(CVE-2020-0796)分析复现

2

重大漏洞预警:ubuntu最新版本存在本地提权漏洞(已有EXP) 




发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: