File: dns-ip6-arpa-scan.nse

package info (click to toggle)
nmap 7.40-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 50,080 kB
  • ctags: 26,777
  • sloc: ansic: 98,862; cpp: 64,063; python: 17,751; sh: 14,584; xml: 11,448; makefile: 2,635; perl: 2,585; yacc: 660; lex: 457; asm: 372; java: 45; objc: 43
file content (126 lines) | stat: -rw-r--r-- 3,695 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
local coroutine = require "coroutine"
local dns = require "dns"
local ipOps = require "ipOps"
local nmap = require "nmap"
local stdnse = require "stdnse"
local string = require "string"
local tab = require "tab"
local table = require "table"

description = [[
Performs a quick reverse DNS lookup of an IPv6 network using a technique
which analyzes DNS server response codes to dramatically reduce the number of queries needed to enumerate large networks.

The technique essentially works by adding an octet to a given IPv6 prefix
and resolving it. If the added octet is correct, the server will return
NOERROR, if not a NXDOMAIN result is received.

The technique is described in detail on Peter's blog:
http://7bits.nl/blog/2012/03/26/finding-v6-hosts-by-efficiently-mapping-ip6-arpa
]]

---
-- @usage
-- nmap --script dns-ip6-arpa-scan --script-args='prefix=2001:0DB8::/48'
--
-- @output
-- Pre-scan script results:
-- | dns-ip6-arpa-scan:
-- | ip                                 ptr
-- | 2001:0DB8:0:0:0:0:0:2              resolver1.example.com
-- |_2001:0DB8:0:0:0:0:0:3              resolver2.example.com
--
-- @args prefix the ip6 prefix to scan
-- @args mask the ip6 mask to start scanning from

author = "Patrik Karlsson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"intrusive", "discovery"}


local arg_prefix = stdnse.get_script_args(SCRIPT_NAME .. ".prefix")
local arg_mask = stdnse.get_script_args(SCRIPT_NAME .. ".mask")

-- Return a prefix and mask based on script arguments. First checks for "/"
-- netmask syntax; then looks for a "mask" script argument if that fails. The
-- "/" syntax wins over "mask" if both are present.
local function get_prefix_mask(arg_prefix, arg_mask)
  if not arg_prefix then
    return
  end
  local prefix, mask = string.match(arg_prefix, "^(.*)/(.*)$")
  if not mask then
    prefix, mask = arg_prefix, arg_mask
  end
  return prefix, mask
end

prerule = function()
  local prefix, mask = get_prefix_mask(arg_prefix, arg_mask)
  return prefix and mask
end

local function query_prefix(query, result)
  local condvar = nmap.condvar(result)
  local status, res = dns.query(query, { dtype='PTR' })
  if ( not(status) and res == "No Answers") then
    table.insert(result, query)
  elseif ( status ) then
    local ip = query:sub(1, -10):gsub('%.',''):reverse():gsub('(....)', '%1:'):sub(1, -2)
    ip = ipOps.bin_to_ip(ipOps.ip_to_bin(ip))
    table.insert(result, { ptr = res, query = query, ip = ip } )
  end
  condvar "signal"
end

action = function()

  local prefix, mask = get_prefix_mask(arg_prefix, arg_mask)
  local query = dns.reverse(prefix)

  -- cut the query name down to the length of the prefix
  local len = (( mask / 8 ) * 4) + #(".ip6.arpa") - 1

  local found = { query:sub(-len) }
  local threads = {}

  local i = 20

  local result
  repeat
    result = {}
    for _, f in ipairs(found) do
      for q in ("0123456789abcdef"):gmatch("(%w)") do
        local co = stdnse.new_thread(query_prefix, q .. "." .. f, result)
        threads[co] = true
      end
    end

    local condvar = nmap.condvar(result)
    repeat
      for t in pairs(threads) do
        if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
      end
      if ( next(threads) ) then
        condvar "wait"
      end
    until( next(threads) == nil )

    if ( 0 == #result ) then
      return
    end

    found = result
    i = i + 1
  until( 128 == i * 2 + mask )

  table.sort(result, function(a,b) return (a.ip < b.ip) end)
  local output = tab.new(2)
  tab.addrow(output, "ip", "ptr")

  for _, item in ipairs(result) do
    tab.addrow(output, item.ip, item.ptr)
  end

  return "\n" .. tab.dump(output)
end