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
|
local io = require "io"
local nmap = require "nmap"
local slaxml = require "slaxml"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
local target = require "target"
description = [[
Loads addresses from an Nmap XML output file for scanning.
Address type (IPv4 or IPv6) is determined according to whether -6 is specified to nmap.
]]
---
--@args targets-xml.iX Filename of an Nmap XML file to import
--@args targets-xml.state Only hosts with this status will have their addresses
-- input. Default: "up"
--
--@usage
-- nmap --script targets-xml --script-args newtargets,iX=oldscan.xml
--
--@output
--Pre-scan script results:
--|_targets-xml: Added 16 ipv4 addresses
--
--@xmloutput
--16
-- TODO: more filtering options: port status, string search, etc.
author = "Daniel Miller"
categories = {"safe"}
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
local filename = stdnse.get_script_args(SCRIPT_NAME .. ".iX")
prerule = function ()
if not filename then
stdnse.verbose1("Need to supply a file name with the %s.iX argument", SCRIPT_NAME);
return false
end
return true
end
local startElement = {
host = function (state)
state.addresses = {}
state.up = nil
end,
status = function (state)
state.parser._call.attribute = function (name, attribute)
if name == "state" then
state.up = attribute == state.status
end
end
end,
address = function (state)
state.parser._call.attribute = function (name, attribute)
if name == "addrtype" then
state.valid = attribute == state.addrtype
elseif name == "addr" then
state.address = attribute
end
end
end,
}
local closeElement = {
host = function (state)
if state.up then
state.added = state.added + #state.addresses
if target.ALLOW_NEW_TARGETS then
target.add(table.unpack(state.addresses))
end
end
state.up = nil
end,
status = function (state)
state.parser._call.attribute = nil
end,
address = function (state)
if state.valid and state.address then
table.insert(state.addresses, state.address)
end
state.parser._call.attribute = nil
state.address = nil
state.valid = false
end,
}
action = function ()
local status = stdnse.get_script_args(SCRIPT_NAME .. ".state") or "up"
local input, err = io.open(filename, "r")
if not input then
stdnse.debug1("Couldn't open %s: %s", filename, err)
return nil
end
local state = {
status = status,
addrtype = "ipv4",
added = 0,
}
if nmap.address_family() == "inet6" then
state.addrtype = "ipv6"
end
state.parser = slaxml.parser:new({
startElement = function (name)
return startElement[name] and startElement[name](state) or nil
end,
closeElement = function (name)
return startElement[name] and closeElement[name](state) or nil
end,
})
local buf = ""
local function next_chunk()
local read, starts, ends
repeat
read = input:read(8192)
if not read then
return buf, true
end
starts, ends = string.find(read, ">.-$")
if not starts then
buf = buf .. read
end
until starts
local ret = buf .. string.sub(read, 1, starts)
buf = string.sub(read, starts+1)
return ret, false
end
local chunk
local eof = false
while not eof do
chunk, eof = next_chunk()
state.parser:parseSAX(chunk)
end
return state.added, ("Found %s %s addresses"):format(state.added, state.addrtype)
end
|