summaryrefslogtreecommitdiffstats
path: root/scripts/targets-ipv6-map4to6.nse
blob: f588e7523ffdb4ee4b2d4e34e8ce25b3fb5a7339 (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
local ipOps = require "ipOps"
local nmap = require "nmap"
local stdnse = require "stdnse"
local string = require "string"
local stringaux = require "stringaux"
local table = require "table"
local target = require "target"

description = [[
This script runs in the pre-scanning phase to map IPv4 addresses onto IPv6
networks and add them to the scan queue.

The technique is more general than what is technically termed "IPv4-mapped IPv6
addresses." The lower 4 bytes of the IPv6 network address are replaced with the
4 bytes of IPv4 address. When the IPv6 network is ::ffff:0:0/96, then the
script generates IPv4-mapped IPv6 addresses. When the network is ::/96, then it
generates IPv4-compatible IPv6 addresses.
]]

---
-- @usage
-- nmap -6 --script targets-ipv6-map4to6 --script-args newtargets,targets-ipv6-map4to6.IPv4Hosts={192.168.1.0/24},targets-ipv6-subnet={2001:db8:c0ca::/64}
--
-- @output
-- Pre-scan script results:
-- | targets-ipv6-map4to6:
-- |   node count: 256
-- |   addresses:
-- |_    2001:db8:c0ca:0:0:0:c0a8:100/120
--
-- @args targets-ipv6-map4to6.IPv4Hosts  This must have at least one IPv4
--                                   Host for the script be able to work
--                                   (Ex. 192.168.1.1 or
--                                   { 192.168.1.1, 192.168.2.2 } ) or Subnet
--                                   Addresses ( 192.168.1.0/24 or
--                                   { 192.168.1.0/24, 192.168.2.0/24 } )
--
-- @args targets-ipv6-subnet  Table/single IPv6 address with prefix
--                                  (Ex. 2001:db8:c0ca::/48 or
--                                  { 2001:db8:c0ca::/48, 2001:db8:FEA::/48 })
--
-- @xmloutput
-- <elem key="node count">256</elem>
-- <table key="addresses">
--   <elem>2001:db8:c0ca:0:0:0:c0a8:100/120</elem>
-- </table>

--
-- Version 1.4
-- Update  01/12/2014 - V 1.4 Update for inclusion in Nmap by Daniel Miller
-- Update  05/05/2014 - V 1.3 Eliminate the Host phase.
-- Update  05/05/2014 - V 1.2 Minor corrections and standardization.
-- Update  18/10/2013 - V 1.1 Added     SaveMemory option
-- Update  29/03/2013 - V 1.0 Functional script
-- Created 28/03/2013 - v0.1  Created by Raúl Fuentes <ra.fuentess.sam+nmap@gmail.com>
--

author = "Raúl Armando Fuentes Samaniego"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {
  "discovery",
}

local function split_prefix (net)
  local split = stringaux.strsplit("/", net)
  return split[1], tonumber(split[2])
end
---
-- This function will add all the list of IPv4 host to IPv6
--
-- The most normal is returning X:X:X:X::Y.Y.Y.Y/128
-- The conversion is going to be totally IPv6 syntax (we are going to
-- concatenate strings).
-- @param  IPv6_Network A IPv6 Address  ( X:X:X:X::/YY )
-- @param  IPv4SHosts   A IPv4 String can be: X.X.X.X or X.X.X.X/YY
-- @param  addr_table   A table to hold the generated addresses.
-- @return  Number   Total successfully nodes added to the scan.
-- @return  Error    A warning if something happened. (Nil otherwise)
local From_4_to_6 = function (IPv6_Network, IPv4SHosts, addr_table)

  --We check if the PRefix are OK, anything less than 96 is fine
  local v6_base, IPv6_Prefix = split_prefix(IPv6_Network)
  if IPv6_Prefix > 96 then
    return 0, string.format("The IPv6 subnet %s can't support a direct Mapping 4 to 6.", IPv6_Network)
  end

  local sBin6, sError = ipOps.ip_to_bin(v6_base)
  if sBin6 == nil then
    return 0, sError
  end

  -- two options: String or Table,  the bes thing to do:  make string Table
  local tTabla
  if type(IPv4SHosts) == "table" then
    tTabla = IPv4SHosts
  else
    tTabla = { IPv4SHosts }
  end

  stdnse.debug1("Total IPv4 objects to analyze: %d for IPv6 subnet %s",
    #tTabla, IPv6_Network)

  local iTotal = 0
  for _, Host in ipairs(tTabla) do


    stdnse.debug2("IPv4 Object: %s", Host)

    local v4base, prefix = split_prefix(Host)

    local sBin4
    sBin4, sError = ipOps.ip_to_bin(v4base)
    if sBin4 == nil then
      return 0, sError
    end

    local IPAux
    IPAux, sError = ipOps.bin_to_ip(sBin6:sub(1, 96) .. sBin4)
    if prefix then
      prefix = prefix + (128 - 32) -- adjust for different address lengths
      IPAux = string.format("%s/%d", IPAux, prefix)
    else
      prefix = 128
    end

    stdnse.debug2("IPv6 address: %s", IPAux)

    addr_table[#addr_table+1] = IPAux
    if target.ALLOW_NEW_TARGETS then
      local bool
      bool, sError = target.add(IPAux)
      if bool then
        iTotal = iTotal + 2^(128 - prefix)
      else
        stdnse.debug1("Error adding node %s: %s", IPAux, sError)
      end
    else
      iTotal = iTotal + 2^(128 - prefix)
    end

  end

  return iTotal
end

local IPv4Sub = stdnse.get_script_args(SCRIPT_NAME .. ".IPv4Hosts")
local IPv6User = stdnse.get_script_args("targets-ipv6-subnet")
---
-- We populated the host discovery list.
local Prescanning = function ()

  local errors = {}
  local tSalida = {
    Nodos = 0,
    addrs = {},
  }
  local Grantotal = 0

  stdnse.debug2("Beginning the work.")

  if type(IPv6User) == "string" then
    IPv6User = { IPv6User }
  end

  -- TODO: Gather IPv6 subnets from other sources.
  -- This was implemented in the original version of the script, but stripped
  -- for now until the other scripts are integrated.
  -- http://seclists.org/nmap-dev/2013/q4/285
  for _, IPv6_Subnet in ipairs(IPv6User) do
    stdnse.debug1("Processing %s", IPv6_Subnet)
    local IPv6Host, sError = From_4_to_6(IPv6_Subnet, IPv4Sub, tSalida.addrs)
    if sError ~= nil then
      stdnse.debug1( "ERROR: One IPv6 subnet wasn't translated")
      errors[#errors+1] = sError
    end
    if IPv6Host then
      -- We need to concatenate the new nodes
      Grantotal = Grantotal + IPv6Host
    end
  end

  tSalida.Nodos = Grantotal
  if #errors > 0 then
    tSalida.Error = table.concat(errors, "\n")
  end
  return true, tSalida
end

---
-- The script need to be working with IPv6
--
--(To bad can't do it with both at same time )
function prerule ()

  if not (nmap.address_family() == "inet6") then
    stdnse.verbose1("This script is IPv6 only.")
    return false
  end

  -- Because Nmap current limitation of working ONE single IP family we must
  -- be sure to have everything for work the Mapped IPv4 to IPv6
  if IPv4Sub == nil then
    stdnse.verbose1( "There are no IPv4 addresses to map!\z
    You must provide it using the %s.IPv4Hosts script-arg.", SCRIPT_NAME)
    return false
  end

  -- Now we need to have based IPv6 Prefix, the most important is the previous
  -- known but we have a last-option too .
  if IPv6User == nil then
    stdnse.verbose1("There are no IPv6 subnets to scan!\z
    You must provide it using the targets-ipv6-subnet script-arg.")
    return false
  end

  return true
end

function action ()
  --Vars for created the final report
  local tOutput = stdnse.output_table()
  local bExito = false
  local tSalida

  bExito, tSalida = Prescanning()

  -- Now we adapt the exit to tOutput and add the hosts to the target!
  tOutput.warning = tSalida.Error

  if bExito then
    --Final report of the Debug Lvl of Prescanning
    stdnse.debug1("Successful Mapped IPv4 to IPv6 added to the scan: %d",
      tSalida.Nodos)

    tOutput["node count"] = tSalida.Nodos
    tOutput["addresses"] = tSalida.addrs

    if tSalida.Error then
      stdnse.debug1("Warnings: %s", tSalida.Error)
    end
  else
    stdnse.debug1("Was unable to add nodes to the scan list due this error: %s",
      tSalida.Error)
  end

  return tOutput
end