diff --git a/CHANGELOG b/CHANGELOG index bfa4ef531..c4fd85be2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added cccam-version.nse. It detects the CCcam TV card sharing + system. [David] + o [NSE] Added the scripts xdmcp-discover, broadcast-xdmcp-discover and the X Display Manager Control Protocol (xdmcp) library. The scripts discover hosts either using unicast or broadcast and try to detect supported diff --git a/scripts/cccam-version.nse b/scripts/cccam-version.nse new file mode 100644 index 000000000..ad33ff32e --- /dev/null +++ b/scripts/cccam-version.nse @@ -0,0 +1,134 @@ +description = [[Detects the CCcam service. + +CCcam is a way of sharing subscription TV among multiple receivers. The service +normally runs on port 12000. It distinguishes itself by printing 16 +random-looking bytes upon receiving a connection. + +Because the script attempts to detect "random-looking" bytes, it has a small +chance of failing to detect the service when the data do not seem random +enough.]] + +categories = {"version"} + +author = "David Fifield" + +require("bin") +require("shortport") + +-- A chi-square test for the null hypothesis that the members of data are drawn +-- from a uniform distribution over num_cats categories. +local function chi2(data, num_cats) + local bins = {} + local x2, delta, expected + + for _, x in ipairs(data) do + bins[x] = bins[x] or 0 + bins[x] = bins[x] + 1 + end + + expected = #data / num_cats + x2 = 0.0 + for _, n in pairs(bins) do + delta = n - expected + x2 = x2 + delta * delta + end + x2 = x2 / expected + + return x2 +end + +-- Split a string into a sequence of bit strings of the given length. +-- splitbits("abc", 5) --> {"01100", "00101", "10001", "00110"} +-- Any short final group is omitted. +local function splitbits(s, n) + local bits, seq + + _, bits = bin.unpack("B" .. #s, s) + seq = {} + for i = 1, #bits - n, n do + seq[#seq + 1] = bits:sub(i, i + n - 1) + end + + return seq +end + +-- chi-square cdf table at 0.95 confidence for different degrees of freedom. +-- >>> import scipy.stats, scipy.optimize +-- >>> scipy.optimize.newton(lambda x: scipy.stats.chi2(dof).cdf(x) - 0.95, dof) +local CHI2_CDF = { + [3] = 7.8147279032511738, + [15] = 24.99579013972863, + [255] = 293.2478350807001, +} + +local function looks_random(data) + local x2 + + -- Because our sample is so small (only 16 bytes), do a chi-square + -- goodness of fit test across groups of 2, 4, and 8 bits. If using only + -- 8 bits, for example, any sample whose bytes are all different would + -- pass the test. Using 2 bits will tend to catch things like pure + -- ASCII, where one out of every four samples never has its high bit + -- set. + + x2 = chi2(splitbits(data, 2), 4) + if x2 > CHI2_CDF[3] then + return false + end + + x2 = chi2(splitbits(data, 4), 16) + if x2 > CHI2_CDF[15] then + return false + end + + x2 = chi2({string.byte(data, 1, -1)}, 256) + if x2 > CHI2_CDF[255] then + return false + end + + return true +end + +local NUM_TRIALS = 2 + +local function trial(host, port) + local status, data, s + + s = nmap.new_socket() + status, data = s:connect(host, port) + if not status then + return + end + + status, data = s:receive_bytes(0) + if not status then + s:close() + return + end + s:close() + + return data +end + +portrule = shortport.version_port_or_service(12000, "cccam") + +function action(host, port) + local seen = {} + + -- Try a couple of times to see that the response isn't constant. (But + -- more trials also increase the chance that we will reject a legitimate + -- cccam service.) + for i = 1, NUM_TRIALS do + local data + + data = trial(host, port) + if not data or seen[data] or #data ~= 16 or not looks_random(data) then + return + end + seen[data] = true + end + + port.version.name = "cccam" + port.version.version = "CCcam DVR card sharing system" + nmap.set_port_version(host, port, "hardmatched") +end diff --git a/scripts/script.db b/scripts/script.db index 30954ad9b..8314815d3 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -37,6 +37,7 @@ Entry { filename = "broadcast-wake-on-lan.nse", categories = { "broadcast", "saf Entry { filename = "broadcast-wpad-discover.nse", categories = { "broadcast", "safe", } } Entry { filename = "broadcast-wsdd-discover.nse", categories = { "broadcast", "safe", } } Entry { filename = "broadcast-xdmcp-discover.nse", categories = { "broadcast", "safe", } } +Entry { filename = "cccam-version.nse", categories = { "version", } } Entry { filename = "citrix-brute-xml.nse", categories = { "auth", "intrusive", } } Entry { filename = "citrix-enum-apps-xml.nse", categories = { "discovery", "safe", } } Entry { filename = "citrix-enum-apps.nse", categories = { "discovery", "safe", } }