Skip to content

Instantly share code, notes, and snippets.

@abn
Created July 10, 2014 01:51
Show Gist options
  • Save abn/f082ec2144066d140346 to your computer and use it in GitHub Desktop.
Save abn/f082ec2144066d140346 to your computer and use it in GitHub Desktop.
Implementation of how to poison the positive cache used by java.net.InetAddress (DNS Poisoning)
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
/**
* This class provides a sample implementation of how to poison the positive
* cache used by InetAddress.
*
* @author Arun Babu Neelicattu (abn)
*
*/
public class DnsPoison {
private static final int INADDRSZ = 4;
/**
* Converts IPv4 address in its textual presentation form into its numeric
* binary form.
*
* Source: org.ngrinder.dns.DnsUtils
*
* @param src
* a String representing an IPv4 address in standard format
* @return a byte array representing the IPv4 numeric address
*/
public static byte[] IpTextToNumericFormat(String src) {
if (src == null || src.length() == 0) {
return null;
}
int octets;
char ch;
byte[] dst = new byte[INADDRSZ];
char[] srcb = src.toCharArray();
boolean sawDigit = false;
octets = 0;
int i = 0;
int cur = 0;
while (i < srcb.length) {
ch = srcb[i++];
if (Character.isDigit(ch)) {
int sum = dst[cur] * 10 + (Character.digit(ch, 10) & 0xff);
if (sum > 255) {
return null;
}
dst[cur] = (byte) (sum & 0xff);
if (!sawDigit) {
if (++octets > INADDRSZ) {
return null;
}
sawDigit = true;
}
} else if (ch == '.' && sawDigit) {
if (octets == INADDRSZ) {
return null;
}
cur++;
dst[cur] = 0;
sawDigit = false;
} else {
return null;
}
}
if (octets < INADDRSZ) {
return null;
}
return dst;
}
/**
*
* Add a cache entry mapping the provided hostName to ipaddr.
*
* @param hostName
* @param ipaddr
* @throws Exception
*/
public static void poisonLocalInetAddressCache(String hostName,
String ipaddr) throws Exception {
String cacheClassName = "java.net.InetAddress.Cache";
Class<?> CacheClass = null;
for (Class<?> cls : InetAddress.class.getDeclaredClasses()) {
if (cls.getCanonicalName().equals(cacheClassName)) {
CacheClass = cls;
break;
}
}
if (CacheClass == null) {
throw new NoClassDefFoundError(cacheClassName);
}
Field addressCacheField = InetAddress.class
.getDeclaredField("addressCache");
addressCacheField.setAccessible(true);
Method put = CacheClass.getDeclaredMethod("put", String.class,
InetAddress[].class);
put.setAccessible(true);
// inject the entry
put.invoke(addressCacheField.get(null), hostName,
new InetAddress[] { InetAddress
.getByAddress(IpTextToNumericFormat(ipaddr)) });
// this only done to verify the entry has been persisted
Method get = CacheClass.getDeclaredMethod("get", String.class);
get.setAccessible(true);
if (get.invoke(addressCacheField.get(null), hostName) == null) {
throw new IOException("Could not inject dns cache entry");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment