Restlet XMLDecoder 远程代码执行漏洞分析(CVE-2013-4221)
Posted raul17的安全思考空间
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Restlet XMLDecoder 远程代码执行漏洞分析(CVE-2013-4221)相关的知识,希望对你有一定的参考价值。
本文编辑于:2018-05-03
原文:https://zhuanlan.zhihu.com/p/36002604
0x00 写在前面
最近在整理Java Web组件相关的命令执行漏洞的时候,看到个2013年Restlet的一个CVE漏洞,网上没看到相关的漏洞分析。该漏洞是XMLDecoder反序列化漏洞,跟Weblogic CVE-2017-10271的漏洞原理 ()类似,故准备简单分析一下。
Restlet 2.1.2版本用XMLDecoder类反序列化用户控制的XML数据和二进制数据,这可使远程攻击者通过提交特制的XML数据或二进制数据,利用此漏洞执行任意Java代码,或调用Java对象上的任意反序列化方法。
51CTO上有个XMLDecoder反序列化漏洞的demo:
http://blog.51cto.com/duallay/1961598
关键代码为:
java.io.File file = new java.io.File("d:/tmp/xmldecoder.xml");
java.beans.XMLDecoder xd = null;
try {
xd = new java.beans.XMLDecoder(new BufferedInputStream(new FileInputStream(file)));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Object s2 = xd.readObject();
xd.close();
相同的漏洞原理,信任用户输入,在调用XMLDecoder解析XML文件的时候,存在命令执行漏洞。
0x01 漏洞环境
https://github.com/o2platform/DefCon_RESTing/tree/master/Demos/RestletXMLDecoder
Linux上进入代码目录直接运行如下命令做Idea远程调试相关的配置,进行远程调试。
java -cp ./lib/org.restlet.jar:./lib/groovy-all-1.8.9.jar:. ServerRestlets
Windows将代码导入Idea,直接debug运行ServerRestlets,本地开启8182
端口,即可进行调试。
0x02 漏洞分析
参见链接(https://github.com/restlet/restlet-framework-java/commit/ae3ef64e28e877282822b79f894deedc2f9f8c06)版本更新中的更新日志。针对该漏洞的修复方案:作者将框架提供的默认转换器内的ObjectRepresentation
删除了对XML序列化的JavaBean的默认支持。并在ObjectRepresentation
类中更新了相关的注释,添加了相关的安全警告。
根据测试demo,简单分析执行过程。
当运行ServerRestlets
类,代码将Restlet服务当作单独的Java程序进行部署,启动8182端口。在createInboundRoot()
方法中绑定资源路径到对应的处理资源类CustomerRessource
(当对/customer发起请求时,处理CustomerRessource
类)。
CustomerRessource
类中明确了如何对Get和Put请求进行处理。
我们看Put请求时的处理过程,相关代码为:
@Put
public void storeItem(Representation entity) throws IOException {
// GroovyShell
GroovyShell shell = new GroovyShell();
Object value = shell.evaluate("println 'Hello World!';");
ObjectRepresentation<Customer> repObject;
try {
//System.out.println(entity.getStream());
repObject = new ObjectRepresentation<Customer>(entity);
Customer customer;
customer = repObject.getObject();
System.out.println("name : " + customer.name);
System.out.println("firstname : " + customer.firstName);
setStatus(Status.SUCCESS_OK);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
我们发送poc时,其中的报文body被Representation
类进行了封装。可以通过entity.getText()
可以获取到当前的body内容。
当我们打印entity.getText()
的内容的时候:System.out.println(entity.getStream());
控制台可看到我们发送的xml报文:
PS: 但是注意,
getText()
方法不能重复执行,否则会报错,通过查源码得知:在http响应时Representation
封装了一个io流,只能读取一次。
封装了报文body的Representation
类传入到ObjectRepresentation
类。查看该类的构造方法,代码如下:
public ObjectRepresentation(Representation serializedRepresentation) throws IOException, ClassNotFoundException, IllegalArgumentException {
super(MediaType.APPLICATION_JAVA_OBJECT);
InputStream is;
if(serializedRepresentation.getMediaType().equals(MediaType.APPLICATION_JAVA_OBJECT)) {
this.setMediaType(MediaType.APPLICATION_JAVA_OBJECT);
is = serializedRepresentation.getStream();
ObjectInputStream decoder = new ObjectInputStream(is);
this.object = (Serializable)decoder.readObject();
if(is.read() != -1) {
throw new IOException("The input stream has not been fully read.");
}
decoder.close();
} else {
if(!serializedRepresentation.getMediaType().equals(MediaType.APPLICATION_JAVA_OBJECT_XML)) {
throw new IllegalArgumentException("The serialized representation must have this media type: " + MediaType.APPLICATION_JAVA_OBJECT.toString() + " or this one: " + MediaType.APPLICATION_JAVA_OBJECT_XML.toString());
}
this.setMediaType(MediaType.APPLICATION_JAVA_OBJECT_XML);
is = serializedRepresentation.getStream();
XMLDecoder decoder1 = new XMLDecoder(is);
this.object = (Serializable)decoder1.readObject();
if(is.read() != -1) {
throw new IOException("The input stream has not been fully read.");
}
decoder1.close();
}
}
该构造方法会先判断我们Put请求中的Content-Type是application/x-java-serialized-object
,还是application/x-java-serialized-object+xml
。
查看MediaType
中的定义,代码如下:
public static final MediaType APPLICATION_JAVA_OBJECT = register("application/x-java-serialized-object", "Java serialized object");
public static final MediaType APPLICATION_JAVA_OBJECT_GWT = register("application/x-java-serialized-object+gwt", "Java serialized object (using GWT-RPC encoder)");
public static final MediaType APPLICATION_JAVA_OBJECT_XML = register("application/x-java-serialized-object+xml", "Java serialized object (using JavaBeans XML encoder)");
当请求中的Content-Type为application/x-java-serialized-object
时,直接使用ObjectInputStream
类序列化对象;当Content-Type为application/x-java-serialized-object+xml
时,使用XMLEncoder序列化对象。
故我们的poc中,需将Context-Type修改为application/x-java-serialized-object+xml
。
回到ObjectRepresentation
类的构造方法,当我们请求中的Content-Type为application/x-java-serialized-object+xml
时,执行else{}
中的代码,关键代码为:
else {
......
this.setMediaType(MediaType.APPLICATION_JAVA_OBJECT_XML);
is = serializedRepresentation.getStream();
XMLDecoder decoder1 = new XMLDecoder(is);
this.object = (Serializable)decoder1.readObject();
if(is.read() != -1) {
throw new IOException("The input stream has not been fully read.");
}
decoder1.close();
}
通过getStream()
取出Representation
类中封装的流,通过XMLDecoder
类进行反序列化操作。执行readObject()
方法,导致反序列化远程代码执行。
0x03 后记
整个漏洞分析过程还是比较简单吧。就是研究Restlet花了挺长时间。爬了挺多坑,但是对XMLDecoder反序列化和Restlet的项目有了更深的了解。
0x04 参考链接
-
http://blog.diniscruz.com/2013/08/using-xmldecoder-to-execute-server-side.html -
https://github.com/restlet/restlet-framework-java/issues/774 -
https://github.com/o2platform/DefCon_RESTing/tree/master/Demos/RestletXMLDecoder
以上是关于Restlet XMLDecoder 远程代码执行漏洞分析(CVE-2013-4221)的主要内容,如果未能解决你的问题,请参考以下文章
Weblogic XMLDecoder反序列化漏洞(CVE-2017-10271)