Java RMI远程反序列化任意类及远程代码执行解析(CVE-2017-3241
本打算慢慢写出来的,但前几天发现国外有研究员发了一篇关于这个CVE的文章,他和我找到的地方很相似。然而不知道是不是Oracle认为是同一个漏洞然后合并了CVE,还是说我找错了CVE。
总之,先简单描述一下漏洞:对于任何一个以对象为参数的RMI接口,你都可以发一个自己构建的对象,迫使服务器端将这个对象按任何一个存在于class path中的可序列化类来反序列化。听起来可能有点绕,请往下看。
就直接上问题代码了。在Java RMI的sun.rmi.server.UnicastRef类中,有如下一段代码:
300 protected static Object More ...unmarshalValue(Class type, ObjectInput in)
301 throws IOException, ClassNotFoundException
302 {
303 if (type.isPrimitive()) {
304 if (type == int.class) {
305 return Integer.valueOf(in.readInt());
306 } else if (type == boolean.class) {
307 return Boolean.valueOf(in.readBoolean());
308 } else if (type == byte.class) {
309 return Byte.valueOf(in.readByte());
310 } else if (type == char.class) {
311 return Character.valueOf(in.readChar());
312 } else if (type == short.class) {
313 return Short.valueOf(in.readShort());
314 } else if (type == long.class) {
315 return Long.valueOf(in.readLong());
316 } else if (type == float.class) {
317 return Float.valueOf(in.readFloat());
318 } else if (type == double.class) {
319 return Double.valueOf(in.readDouble());
320 } else {
321 throw new Error("Unrecognized primitive type: " + type);
322 }
323 } else {
324 return in.readObject();
325 }
326 }
看324行,如果你熟悉java反序列化漏洞,看到此你应该就可以激动了。该代码直接调用readObject,且在原生Java类里。结合2016 black hat上那个spring-tx.jar或者之前apache common中的类,都可以实现远程代码执行。spring-tx里的那个我实验成功了,且Spring rmi中继承了这个漏洞。但Spring team表示不修,和他们没关系。。。
其实写到这,很多技术大牛已经可以自己找出怎么黑了。下面只是简单写写我如何通过正常Java RMI程序来攻击的,因为我觉得这招还是比较淫荡的。
以下是一个正常的服务器端接口,接口参数为Message对象,Message对象是要被序列化的对象:
public interface Services extends java.rmi.Remote
{
String sendMessage(Message msg) throws RemoteException;
}
public class Message implements Serializable {
private String msg;
public Message()
{
}
public String getMessage() {
System.out.println("Processing message: "+msg);
return msg;
}
public void setMessage(String msg) {
this.msg = msg;
}
/*
* server will tell the serialVersionUID for first run, then just put it below
*/
private final static long serialVersionUID = 1311618551071721443L;
}
服务器端程序,sendMessage接口实现只是调用getMessage打印字符串:
public class RMIServer
implements Services {
public RMIServer() throws RemoteException {
}
本打算慢慢写出来的,但前几天发现国外有研究员发了一篇关于这个CVE的文章,他和我找到的地方很相似。然而不知道是不是Oracle认为是同一个漏洞然后合并了CVE,还是说我找错了CVE。
总之,先简单描述一下漏洞:对于任何一个以对象为参数的RMI接口,你都可以发一个自己构建的对象,迫使服务器端将这个对象按任何一个存在于class path中的可序列化类来反序列化。听起来可能有点绕,请往下看。
就直接上问题代码了。在Java RMI的sun.rmi.server.UnicastRef类中,有如下一段代码:
300 protected static Object More ...unmarshalValue(Class type, ObjectInput in)
301 throws IOException, ClassNotFoundException
302 {
303 if (type.isPrimitive()) {
304 if (type == int.class) {
305 return Integer.valueOf(in.readInt()); 无奈人生安全网
306 } else if (type == boolean.class) {
307 return Boolean.valueOf(in.readBoolean());
308 } else if (type == byte.class) {
309 return Byte.valueOf(in.readByte());
310 } else if (type == char.class) {
311 return Character.valueOf(in.readChar());
312 } else if (type == short.class) {
313 return Short.valueOf(in.readShort());
314 } else if (type == long.class) {
315 return Long.valueOf(in.readLong());
316 } else if (type == float.class) {
317 return Float.valueOf(in.readFloat());
318 } else if (type == double.class) {
319 return Double.valueOf(in.readDouble());
320 } else {
321 throw new Error("Unrecognized primitive type: " + type);
322 }
323 } else {
324 return in.readObject(); 内容来自无奈安全网
325 }
326 }
看324行,如果你熟悉java反序列化漏洞,看到此你应该就可以激动了。该代码直接调用readObject,且在原生Java类里。结合2016 black hat上那个spring-tx.jar或者之前apache common中的类,都可以实现远程代码执行。spring-tx里的那个我实验成功了,且Spring rmi中继承了这个漏洞。但Spring team表示不修,和他们没关系。。。
其实写到这,很多技术大牛已经可以自己找出怎么黑了。下面只是简单写写我如何通过正常Java RMI程序来攻击的,因为我觉得这招还是比较淫荡的。
以下是一个正常的服务器端接口,接口参数为Message对象,Message对象是要被序列化的对象:
public interface Services extends java.rmi.Remote
{
String sendMessage(Message msg) throws RemoteException;
}
public class Message implements Serializable {
private String msg;
public Message()
{
}
public String getMessage() {
内容来自无奈安全网
System.out.println("Processing message: "+msg);
return msg;
}
public void setMessage(String msg) {
this.msg = msg;
}
/*
* server will tell the serialVersionUID for first run, then just put it below
*/
private final static long serialVersionUID = 1311618551071721443L;
}
服务器端程序,sendMessage接口实现只是调用getMessage打印字符串:
public class RMIServer
implements Services {
public RMIServer() throws RemoteException {
}
无奈人生安全网