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
127
128
129
130
131
|
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'
--
-- @see dns-nsec3-enum.nse
-- @see dns-nsec-enum.nse
-- @see dns-brute.nse
-- @see dns-zone-transfer.nse
--
-- @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
|