package com.netifera.platform.net.dns.tools; import java.io.IOException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.concurrent.atomic.AtomicInteger; import org.xbill.DNS.AAAARecord; import org.xbill.DNS.ARecord; import org.xbill.DNS.Lookup; import org.xbill.DNS.NSRecord; import org.xbill.DNS.Name; import org.xbill.DNS.Record; import org.xbill.DNS.TextParseException; import org.xbill.DNS.Type; import com.netifera.platform.api.probe.IProbe; import com.netifera.platform.api.tools.ITool; import com.netifera.platform.api.tools.IToolContext; import com.netifera.platform.api.tools.ToolException; import com.netifera.platform.net.dns.internal.tools.Activator; import com.netifera.platform.net.dns.service.DNS; import com.netifera.platform.net.dns.service.client.AsynchronousLookup; import com.netifera.platform.net.dns.service.nameresolver.INameResolver; import com.netifera.platform.net.sockets.CompletionHandler; import com.netifera.platform.tools.RequiredOptionMissingException; import com.netifera.platform.util.addresses.inet.IPv4Address; import com.netifera.platform.util.addresses.inet.IPv6Address; import com.netifera.platform.util.addresses.inet.InternetAddress; public class HostNamesBruteforcer implements ITool { private final static boolean DEBUG = false; private DNS dns; private Name domain; private INameResolver resolver; private IToolContext context; private long realm; private InternetAddress ignoreAddress = null; private AtomicInteger activeRequests; private static String[] commonNames = {"www", "www2", "web", "ssl", "static", "main", "home", "go", "ftp", "image", "images", "photo", "photos", "img", "pictures", "search", "mail", "webmail", "email", "mymail", "mx", "snmp", "pop", "pop3", "imap", "exchange", "ns", "dns", "mdns", "nameserver", "fw", "firewall", "router", "register", "login", "id", "passport", "vpn", "proxy", "cache", "upload", "download", "private", "partner", "partners", "customer", "customers", "member", "members", "user", "users", "b2b", "erp", "global", "us", "mx", "fr", "it", "usa", "de", "uk", "ca", "tw", "cn", "kr", "jp", "hk", "sg", "th", "my", "is", "in", "forum", "forums", "bbs", "blog", "blogs", "weblog", "weblogs", "wiki", "media", "video", "videos", "movie", "movies", "music", "tv", "community", "support", "network", "admin", "security", "secure", "sec", "manager", "manage", "management", "news", "feeds", "service", "sevices", "game", "games", "help", "list", "lists", "archive", "archives", "file", "files", "database", "data", "db", "oracle", "sql", "mysql", "cvs", "svn", "irc", "dc", "domain", "update", "buy", "sell", "sells", "pay", "payment", "payments", "shop", "shopping", "store", "webstore", "products", "product", "order", "orders", "report", "reports", "test", "research", "job", "jobs", "careers", "sms", "mobile", "phone", "stats", "localhost" }; public void toolRun(IToolContext context) throws ToolException { this.context = context; // XXX hardcode local probe as realm IProbe probe = Activator.getInstance().getProbeManager().getLocalProbe(); realm = probe.getEntity().getId(); setupToolOptions(); context.setTitle("Bruteforce host names *."+domain); context.setTotalWork(commonNames.length); if (dns != null) try { resolver = dns.createNameResolver(Activator.getInstance().getSocketEngine()); } catch (IOException e) { context.exception("I/O Exception", e); context.done(); return; } else resolver = Activator.getInstance().getNameResolver(); context.setStatus("Get authoritative name servers"); if (!getNS()) { context.error("Authoritative name servers not found, "+domain+" might not be a real domain"); context.done(); return; } context.setStatus("Lookup *."+domain); try { try { ignoreAddress = resolver.getAddressByName("kjhakjsd."+domain.toString()); } catch (UnknownHostException e) { } if (ignoreAddress != null) context.warning("The DNS server resolves non-existent names"); activeRequests = new AtomicInteger(0); resolveHostName(domain.toString()); for (String each : commonNames) { resolveHostName(each + "." + domain.toString()); Thread.sleep(100); } while (activeRequests.get() > 0) { if(DEBUG) { context.debug("activeRequests = "+activeRequests.get()); } Thread.sleep(500); } } catch (InterruptedException e) { context.warning("Interrupted"); } finally { try { if (dns != null) resolver.shutdown(); } catch (IOException e) { context.exception("I/O Exception", e); }; context.done(); } } // TODO only bruteforcing Type.A, what about Type.AAAA? private void resolveHostName(final String fqdm) { try { final AsynchronousLookup lookup = new AsynchronousLookup(fqdm); lookup.setResolver(resolver.getExtendedResolver()); CompletionHandler<Record[],Void> handler = new CompletionHandler<Record[],Void>() { int retries = 0; public void cancelled(Void attachment) { activeRequests.decrementAndGet(); context.warning(fqdm + " lookup cancelled"); context.worked(1); } public void completed(Record[] result, Void attachment) { if (lookup.getResult() == AsynchronousLookup.TRY_AGAIN && retries < 3) { retries = retries + 1; context.debug("Retrying: "+fqdm+" ("+retries+")"); lookup.run(attachment, this); return; } activeRequests.decrementAndGet(); context.worked(1); if (result == null) { context.error(fqdm+" lookup failed: "+lookup.getErrorString()); return; } for (Record record: result) processRecord(record); } public void failed(Throwable exc, Void attachment) { activeRequests.decrementAndGet(); context.worked(1); if(lookup.getResult() == AsynchronousLookup.HOST_NOT_FOUND || lookup.getResult() == AsynchronousLookup.TYPE_NOT_FOUND) { return; } if(exc instanceof SocketTimeoutException) { context.warning("Timeout looking up " + fqdm); return; } context.exception(fqdm+" lookup failed", exc); }}; activeRequests.incrementAndGet(); lookup.run(null,handler); } catch (TextParseException e) { context.warning("Malformed host name: " + fqdm); } } private void processRecord(Record record) { if (record instanceof ARecord) { IPv4Address addr = IPv4Address.fromInetAddress(((ARecord)record).getAddress()); if (addr != null) { if (ignoreAddress == null || !ignoreAddress.equals(addr)) { context.info(record.toString()); Activator.getInstance().getDomainEntityFactory().createARecord(realm, context.getSpaceId(), ((ARecord)record).getName().toString(), addr); } } } else if (record instanceof AAAARecord) { IPv6Address addr = IPv6Address.fromInetAddress(((AAAARecord)record).getAddress()); if (addr != null) { if (ignoreAddress == null || !ignoreAddress.equals(addr)) { context.info(record.toString()); Activator.getInstance().getDomainEntityFactory().createAAAARecord(realm, context.getSpaceId(), ((AAAARecord)record).getName().toString(), addr); } } } } private boolean getNS() { Lookup lookup = new Lookup(domain, Type.NS); lookup.setResolver(resolver.getExtendedResolver()); lookup.setSearchPath((Name[])null); Record[] records = lookup.run(); if (records == null) { context.info("No NS records found for " + domain); return false; } // if the domain has ns records, it exists Activator.getInstance().getDomainEntityFactory().createDomain(realm, context.getSpaceId(), domain.toString()); for (int i = 0; i < records.length; i++) { NSRecord ns = (NSRecord) records[i]; Activator.getInstance().getDomainEntityFactory().createNSRecord(realm, context.getSpaceId(), domain.toString(), ns.getTarget().toString()); } return true; } private void setupToolOptions() throws ToolException { dns = (DNS) context.getConfiguration().get("dns"); String domainString = (String) context.getConfiguration().get("domain"); if (domainString == null || domainString.length() == 0) throw new RequiredOptionMissingException("domain"); if (domainString.endsWith(".")) { domainString = domainString.substring(0, domainString.length()-1); } try { domain = new Name(domainString); } catch (TextParseException e) { throw new ToolException("Malformed domain name: '"+domainString+"'", e); } } }