From e4162e677178433b2e0503de7b81a13d449c1bd0 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 12 Jun 2010 02:25:44 +0000 Subject: [PATCH] o [NSE] Added dns-cache-snoop.nse by Eugene Alexeev. This script does cache snooping by either sending non-recursive queries or by measuring response times. --- CHANGELOG | 4 + scripts/dns-cache-snoop.nse | 251 ++++++++++++++++++++++++++++++++++++ scripts/script.db | 1 + 3 files changed, 256 insertions(+) create mode 100644 scripts/dns-cache-snoop.nse diff --git a/CHANGELOG b/CHANGELOG index 172696868..c06b06437 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added dns-cache-snoop.nse by Eugene Alexeev. This script does + cache snooping by either sending non-recursive queries or by measuring + response times. + o Ports are now considered open during a SYN scan if a SYN packet (without the ACK flag) is received in response. This can be due to an extremely rare TCP feature known as a simultaneous open or split diff --git a/scripts/dns-cache-snoop.nse b/scripts/dns-cache-snoop.nse new file mode 100644 index 000000000..8be751fe1 --- /dev/null +++ b/scripts/dns-cache-snoop.nse @@ -0,0 +1,251 @@ +description = [[ +Performs DNS cache snooping against a DNS server. + +There are two modes of operation, controlled by the +dns-cache-snoop.mode script argument. In +nonrecursive mode (the default), queries are sent to the +server with the RD (recursion desired) flag set to 0. The server should +respond positively to these only if it has the domain cached. In +timed mode, the mean and standard deviation response times +for a cached domain are calculated by sampling the resolution of a name +(www.google.com) several times. Then, each domain is resolved and the +time taken compared to the mean. If it is less than one standard +deviation over the mean, it is considered cached. The timed +mode inserts entries in the cache and can only be used reliably once. + +The default list of domains to check consists of the top 50 most popular +sites, each site being listed twice, once with "www." and once without. +Use the dns-cache-snoop.domains script argument to use a +different list. +]] + +--- +-- @args dns-cache-snoop.mode which of two supported snooping methods to +-- use: +-- * nonrecursive (default): checks if the server returns results for non-recursive queries. Some servers may disable this. +-- * timed: measures the difference in time taken to resolve cached and non-cached hosts. This mode will pollute the DNS cache and can only be used once reliably. +-- @args dns-cache-snoop.domains an array of domain to check in place of +-- the default list. +-- +-- @output +-- PORT STATE SERVICE REASON +-- 53/udp open domain udp-response +-- | dns-cache-snoop: 10 of 100 tested domains are cached. +-- | www.google.com +-- | facebook.com +-- | www.facebook.com +-- | www.youtube.com +-- | yahoo.com +-- | twitter.com +-- | www.twitter.com +-- | www.google.com.hk +-- | www.google.co.uk +-- |_www.linkedin.com +-- +-- @usage +-- nmap -sU -p 53 --script dns-cache-snoop.nse --script-args 'dns-cache-snoop.mode=timed,dns-cache-snoop.domains={host1,host2,host3}' + +require("shortport") +require("dns") +require("stdnse") +require("nmap") +require("math") + +author = "Eugene V. Alexeev" + +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" + +categories = {"intrusive", "discovery"} + +portrule = shortport.port_or_service(53, "domain", "udp") + +local DOMAINS = {} +local MODE = "nonrecursive" +-- This domain is used as a default cached entry in timed mode. +local TIMED_DUMMY_DOMAIN = "www.google.com" +-- How many samples to collect for the time taken to resolve the dummy domain. +local TIMED_NUM_SAMPLES = 25 +-- In timed mode, times below mean + TIMED_MULTIPLIER * stddev are +-- accepted as cached. Using one standard deviation gives us a roughly +-- 84% chance that a domain with the same response time as the reference +-- domain will be detected as cached. +local TIMED_MULTIPLIER = 1.0 + +-- This list is the first 50 entries of +-- http://s3.amazonaws.com/alexa-static/top-1m.csv.zip on 2010-06-11. +local ALEXA_DOMAINS = { + "google.com", + "facebook.com", + "youtube.com", + "yahoo.com", + "live.com", + "wikipedia.org", + "baidu.com", + "blogger.com", + "msn.com", + "qq.com", + "twitter.com", + "yahoo.co.jp", + "google.co.in", + "taobao.com", + "google.de", + "google.com.hk", + "wordpress.com", + "amazon.com", + "sina.com.cn", + "google.co.uk", + "microsoft.com", + "bing.com", + "google.fr", + "ebay.com", + "myspace.com", + "yandex.ru", + "google.co.jp", + "linkedin.com", + "163.com", + "google.com.br", + "mail.ru", + "flickr.com", + "craigslist.org", + "google.it", + "fc2.com", + "conduit.com", + "rapidshare.com", + "vkontakte.ru", + "google.es", + "googleusercontent.com", + "bbc.co.uk", + "imdb.com", + "soso.com", + "doubleclick.com", + "go.com", + "livejasmin.com", + "apple.com", + "aol.com", + "bp.blogspot.com", + "youku.com", +} + +-- Construct the default list of domains. +for _, domain in ipairs(ALEXA_DOMAINS) do + DOMAINS[#DOMAINS + 1] = domain + if not string.match(domain, "^www\.") then + DOMAINS[#DOMAINS + 1] = "www." .. domain + end +end + +-- Return the mean and sample standard deviation of an array, using the +-- algorithm from Knuth Vol. 2, Section 4.2.2. +function mean_stddev(t) + local i, m, s, sigma + + if #t == 0 then + return 0, nil + end + + m = t[1] + s = 0 + for i = 2, #t do + local mp = m + m = m + (t[i] - m) / i + s = s + (t[i] - mp) * (t[i] - m) + end + sigma = math.sqrt(s / (#t - 1)) + + return m, sigma +end + +local function nonrecursive_mode(host, port, domains) + local cached = {} + + for _,domain in ipairs(domains) do + if dns.query(domain, {host = host.ip, port = port.number, tries = 0, norecurse=true}) then + cached[#cached + 1] = domain + end + end + + return cached +end + +-- Return the time taken (in seconds) to resolve the given domain, or nil if +-- it could not be resolved. +local function timed_query(host, port, domain) + local start, stop + + start = nmap.clock_ms() + if dns.query(domain, {host = host.ip, port = port.number, tries = 0, norecurse = false}) then + stop = nmap.clock_ms() + return (stop - start) / 1000 + else + return nil + end +end + +local function timed_mode(host, port, domains) + local cached = {} + local i, t + + -- Insert in the cache. + timed_query(host, port, TIMED_DUMMY_DOMAIN) + + -- Measure how long it takes to resolve on average. + local times = {} + local mean, stddev + local cutoff + for i = 1, TIMED_NUM_SAMPLES do + t = timed_query(host, port, TIMED_DUMMY_DOMAIN) + if t then + times[#times + 1] = t + end + end + mean, stddev = mean_stddev(times) + cutoff = mean + stddev * TIMED_MULTIPLIER + stdnse.print_debug(1, "dns-cache-snoop: reference %s: mean %g stddev %g cutoff %g", TIMED_DUMMY_DOMAIN, mean, stddev, cutoff) + + -- Now try all domains one by one. + for _, domain in ipairs(domains) do + t = timed_query(host, port, domain) + if t then + if t < cutoff then + stdnse.print_debug(1, "dns-cache-snoop: %s: %g is cached (cutoff %g)", domain, t, cutoff) + cached[#cached + 1] = domain + else + stdnse.print_debug(1, "dns-cache-snoop: %s: %g not cached (cutoff %g)", domain, t, cutoff) + end + end + end + + return cached +end + +action = function(host, port) + local domains = DOMAINS + local mode = MODE + + local args = nmap.registry.args + if args then + if args["dns-cache-snoop.mode"] then + mode = args["dns-cache-snoop.mode"] + end + if args["dns-cache-snoop.domains"] then + domains = args["dns-cache-snoop.domains"] + end + end + + local cached + + mode = string.lower(mode) + if mode == "nonrecursive" then + cached = nonrecursive_mode(host, port, domains) + elseif mode == "timed" then + cached = timed_mode(host, port, domains) + else + return string.format("Error: \"%s\" is not a known mode. Use \"nonrecursive\" or \"timed\".") + end + + if #cached > 0 then + nmap.set_port_state(host, port, "open") + end + + return string.format("%d of %d tested domains are cached.\n", #cached, #domains) .. stdnse.strjoin("\n", cached) +end diff --git a/scripts/script.db b/scripts/script.db index 1b14ba0b8..0434bd57b 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -19,6 +19,7 @@ Entry { filename = "db2-brute.nse", categories = { "auth", "intrusive", } } Entry { filename = "db2-das-info.nse", categories = { "discovery", "safe", "version", } } Entry { filename = "db2-info.nse", categories = { "discovery", "safe", "version", } } Entry { filename = "dhcp-discover.nse", categories = { "default", "discovery", "intrusive", } } +Entry { filename = "dns-cache-snoop.nse", categories = { "discovery", "intrusive", } } Entry { filename = "dns-fuzz.nse", categories = { "fuzzer", "intrusive", } } Entry { filename = "dns-random-srcport.nse", categories = { "external", "intrusive", } } Entry { filename = "dns-random-txid.nse", categories = { "external", "intrusive", } }