1
0
mirror of https://github.com/nmap/nmap.git synced 2026-01-01 04:19:02 +00:00

New scripts for geo mapping. Closes #606

This commit is contained in:
dmiller
2016-12-17 14:37:35 +00:00
parent 916ce30d0f
commit c12c2eb1c9
9 changed files with 566 additions and 7 deletions

View File

@@ -1,3 +1,4 @@
local geoip = require "geoip"
local http = require "http"
local ipOps = require "ipOps"
local json = require "json"
@@ -39,24 +40,35 @@ end
local geoplugin = function(ip)
local response = http.get("www.geoplugin.net", 80, "/json.gp?ip="..ip, {any_af=true})
local stat, loc = json.parse(response.body)
if not stat then return nil end
if not stat then
return false, loc
end
local output = {}
table.insert(output, "coordinates (lat,lon): "..loc.geoplugin_latitude..","..loc.geoplugin_longitude)
local regionName = (loc.geoplugin_regionName == json.NULL) and "Unknown" or loc.geoplugin_regionName
table.insert(output,"state: ".. regionName ..", ".. loc.geoplugin_countryName)
return output
geoip.add(ip, loc.geoplugin_latitude, loc.geoplugin_longitude)
return true, output
end
action = function(host,port)
local output = geoplugin(host.ip)
local output = stdnse.output_table()
if(#output~=0) then
output.name = host.ip
if host.targetname then
output.name = output.name.." ("..host.targetname..")"
local status, result = geoplugin(host.ip)
if not status then
if result == "syntax error" then
result = "The geoPlugin service has likely blocked you due to excessive usage, but the response received was 'syntax error'."
end
output.ERROR = result
return output, output.ERROR
end
output.name = host.ip
if host.targetname then
output.name = output.name.." ("..host.targetname..")"
end
return stdnse.format_output(true,output)

View File

@@ -1,3 +1,4 @@
local geoip = require "geoip"
local http = require "http"
local ipOps = require "ipOps"
local json = require "json"
@@ -70,6 +71,8 @@ local ipinfodb = function(ip)
table.insert(output, "coordinates (lat,lon): "..loc.latitude..","..loc.longitude)
table.insert(output,"city: ".. loc.cityName..", ".. loc.regionName..", ".. loc.countryName)
geoip.add(ip, loc.latitude, loc.longitude)
return output
end

View File

@@ -0,0 +1,177 @@
local http = require "http"
local geoip = require "geoip"
local io = require "io"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
local url = require "url"
description = [[
This script queries the Nmap registry for the GPS coordinates of targets stored
by previous geolocation scripts and renders a Bing Map of markers representing
the targets.
Additional information for the Bing Maps REST Services API can be found at:
- https://msdn.microsoft.com/en-us/library/ff701724.aspx
]]
---
-- @usage
-- nmap -sn -Pn --script ip-geolocation-geoplugin,ip-geolocation-map-bing --script-args ip-geolocation-map-bing.api_key=[redacted],ip-geolocation-map-bing.map_path=map.png <target>
--
-- @output
-- | ip-geolocation-map-bing:
-- |_ The map has been saved at 'map.png'.
--
-- @args ip-geolocation-map-bing.api_key The required Bing Maps API key for your
-- account. An API key can be generated at https://www.bingmapsportal.com/
--
-- ip-geolocation-map-bing.center GPS coordinates defining the center of the
-- image. If omitted, Bing Maps will choose a center that shows all the
-- markers.
--
-- @args ip-geolocation-map-bing.format The default value is 'jpeg', 'png', and
-- 'gif' are also allowed.
--
-- @args ip-geolocation-map-bing.language The default value is 'en', but other
-- two-letter language codes are accepted.
--
-- @args ip-geolocation-map-bing.layer The default value is 'Road',
-- 'Aerial', and 'AerialWithLabels' are also allowed.
--
-- @args ip-geolocation-map-bing.map_path The path at which the rendered
-- Bing Map will be saved to the local filesystem.
--
-- @args ip-geolocation-map-bing.marker_style This argument can apply styling
-- to the markers.
-- https://msdn.microsoft.com/en-us/library/ff701719.aspx
--
-- @args ip-geolocation-map-bing.size The default value is '640x640' pixels, but
-- can be changed so long as the width is between 80 and 2000 pixels and the
-- height is between 80 and 1500 pixels.
author = "Mak Kolybabi <mak@kolybabi.com>"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"external", "safe"}
local render = function(params, options)
-- Format marker style for inclusion in parameters.
local style = ""
if options["marker_style"] then
style = ";" .. options["marker_style"]
end
-- Add in a marker for each host.
local markers = {}
for coords, ip in pairs(geoip.get_all_by_gps(100)) do
table.insert(markers, "pp=" .. coords .. style)
end
local body = table.concat(markers, "&")
-- Format the parameters into a properly encoded URL.
local query = "/REST/v1/Imagery/Map/" .. options["layer"] .. "?" .. url.build_query(params)
stdnse.debug1("The query URL is: %s", query)
stdnse.debug1("The query body is: %s", body)
local headers = {
["header"] = {
["Content-Type"] = "text/plain; charset=utf-8"
}
}
local res = http.post("dev.virtualearth.net", 80, query, headers, nil, body)
if not res or res.status ~= 200 then
stdnse.debug1("Error %d from API: %s", res.status, res.body)
return false, ("Failed to recieve map using query '%s'."):format(query)
end
local f = io.open(options["map_path"], "w")
if not f then
return false, ("Failed to open file '%s'."):format(options["map_path"])
end
if not f:write(res.body) then
return false, ("Failed to write file '%s'."):format(options["map_path"])
end
f:close()
local msg
return true, ("The map has been saved at '%s'."):format(options["map_path"])
end
local parse_args = function()
local options = {}
local params = {}
local api_key = stdnse.get_script_args(SCRIPT_NAME .. '.api_key')
if not api_key then
return false, "Need to specify an API key, get one at https://www.bingmapsportal.com/."
end
params["key"] = api_key
local center = stdnse.get_script_args(SCRIPT_NAME .. ".center")
if center then
params["centerPoint"] = center
end
local format = stdnse.get_script_args(SCRIPT_NAME .. ".format")
if format then
params["format"] = format
end
local language = stdnse.get_script_args(SCRIPT_NAME .. ".language")
if language then
params["language"] = language
end
local layer = stdnse.get_script_args(SCRIPT_NAME .. ".layer")
if not layer then
layer = "Road"
end
options["layer"] = layer
local map_path = stdnse.get_script_args(SCRIPT_NAME .. '.map_path')
if map_path then
options["map_path"] = map_path
else
return false, "Need to specify a path for the map."
end
local size = stdnse.get_script_args(SCRIPT_NAME .. ".size")
if not size then
-- This size is arbitrary, and is chosen to match the default that Google
-- Maps will produce.
size = "640x640"
end
size = string.gsub(size, "x", ",")
params["mapSize"] = size
return true, params, options
end
postrule = function()
-- Only run if a previous script has registered geolocation data.
return not geoip.empty()
end
action = function()
local output = stdnse.output_table()
-- Parse and sanity check the command line arguments.
local status, params, options = parse_args()
if not status then
output.ERROR = params
return output, output.ERROR
end
-- Render the map.
local status, msg = render(params, options)
if not status then
output.ERROR = msg
return output, output.ERROR
end
return output, stdnse.format_output(true, msg)
end

View File

@@ -0,0 +1,182 @@
local http = require "http"
local geoip = require "geoip"
local io = require "io"
local stdnse = require "stdnse"
local table = require "table"
local url = require "url"
description = [[
This script queries the Nmap registry for the GPS coordinates of targets stored
by previous geolocation scripts and renders a Google Map of markers representing
the targets.
Additional information for the Google Static Maps API can be found at:
- https://developers.google.com/maps/documentation/static-maps/intro
]]
---
-- @usage
-- nmap -sn -Pn --script ip-geolocation-geoplugin,ip-geolocation-map-google --script-args ip-geolocation-map-google.api_key=[redacted],ip-geolocation-map-google.map_path=map.png <target>
--
-- @output
-- | ip-geolocation-map-google:
-- |_ The map has been saved at 'map.png'.
--
-- @args ip-geolocation-map-google.api_key The required Google Maps API key for
-- your account. An API key can be generated at
-- https://developers.google.com/maps/documentation/static-maps/." @args
--
-- ip-geolocation-map-google.center GPS coordinates defining the center of the
-- image. If omitted, Google Maps will choose a center that shows all the
-- markers.
--
-- @args ip-geolocation-map-google.format The default value is 'png' (alias for
-- 'png8'), 'png32', 'gif', 'jpg', and 'jpg-baseline' are also allowed.
-- https://developers.google.com/maps/documentation/static-maps/intro#ImageFormats
--
-- @args ip-geolocation-map-google.language The default value is 'en', but other
-- two-letter language codes are accepted.
--
-- @args ip-geolocation-map-google.layer The default value is 'roadmap',
-- 'satellite', 'hybrid', and 'terrain' are also allowed.
-- https://developers.google.com/maps/documentation/static-maps/intro#MapTypes
--
-- @args ip-geolocation-map-google.map_path The path at which the rendered
-- Google Map will be saved to the local filesystem.
--
-- @args ip-geolocation-map-google.marker_style This argument can apply styling
-- to the markers.
-- https://developers.google.com/maps/documentation/static-maps/intro#MarkerStyles
--
-- @args ip-geolocation-map-google.scale The default value is 1, but values 2
-- and 4 are permitted. Scale level 4 is only available to Google Maps Premium
-- customers.
-- https://developers.google.com/maps/documentation/static-maps/intro#scale_values
--
-- @args ip-geolocation-map-google.size The default value is '640x640' pixels,
-- but can be increased by Google Maps Premium customers.
-- https://developers.google.com/maps/documentation/static-maps/intro#Imagesizes
author = "Mak Kolybabi <mak@kolybabi.com>"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"external", "safe"}
local render = function(params, options)
-- Add in a marker for each GPS coordinate.
local markers = {}
for coords, ips in pairs(geoip.get_all_by_gps()) do
table.insert(markers, coords)
end
params["markers"] = options["marker_style"] .. "|" .. table.concat(markers, "|")
-- Format the parameters into a properly encoded URL.
local query = "/maps/api/staticmap?" .. url.build_query(params)
stdnse.debug1("The query URL is: %s", query)
-- Check that the query string is below the 8192 character limit after
-- URL-encoding.
if #query > 8192 then
return false, ("Refused to send query since URL path is %d chararacters, but Google Maps limits to 8192."):format(#query)
end
local res = http.get("maps.googleapis.com", 80, query)
if not res or res.status ~= 200 then
return false, ("Failed to recieve map using query '%s'."):format(query)
end
local f = io.open(options["map_path"], "w")
if not f then
return false, ("Failed to open file '%s'."):format(options["map_path"])
end
if not f:write(res.body) then
return false, ("Failed to write file '%s'."):format(options["map_path"])
end
f:close()
return true, ("The map has been saved at '%s'."):format(options["map_path"])
end
local parse_args = function()
local options = {}
local params = {}
local api_key = stdnse.get_script_args(SCRIPT_NAME .. '.api_key')
if not api_key then
return false, "Need to specify an API key, get one at https://developers.google.com/maps/documentation/static-maps/."
end
params["key"] = api_key
local center = stdnse.get_script_args(SCRIPT_NAME .. ".center")
if center then
params["center"] = center
end
local format = stdnse.get_script_args(SCRIPT_NAME .. ".format")
if format then
params["format"] = format
end
local language = stdnse.get_script_args(SCRIPT_NAME .. ".language")
if language then
params["language"] = language
end
local layer = stdnse.get_script_args(SCRIPT_NAME .. ".layer")
if layer then
params["layer"] = layer
end
local map_path = stdnse.get_script_args(SCRIPT_NAME .. '.map_path')
if map_path then
options["map_path"] = map_path
else
return false, "Need to specify a path for the map."
end
local marker_style = stdnse.get_script_args(SCRIPT_NAME .. ".marker_style")
stdnse.debug1('--> [%s]', marker_style)
if not marker_style then
marker_style = ""
end
options["marker_style"] = marker_style
local scale = stdnse.get_script_args(SCRIPT_NAME .. ".scale")
if scale then
params["scale"] = scale
end
local size = stdnse.get_script_args(SCRIPT_NAME .. ".size")
if not size then
size = "640x640"
end
params["size"] = size
return true, params, options
end
postrule = function()
-- Only run if a previous script has registered geolocation data.
return not geoip.empty()
end
action = function()
local output = stdnse.output_table()
-- Parse and sanity check the command line arguments.
local status, params, options = parse_args()
if not status then
output.ERROR = params
return output, output.ERROR
end
-- Render the map.
local status, msg = render(params, options)
if not status then
output.ERROR = msg
return output, output.ERROR
end
return output, stdnse.format_output(true, msg)
end

View File

@@ -0,0 +1,91 @@
local http = require "http"
local geoip = require "geoip"
local io = require "io"
local stdnse = require "stdnse"
local table = require "table"
description = [[
This script queries the Nmap registry for the GPS coordinates of targets stored
by previous geolocation scripts and produces a KML file of points representing
the targets.
]]
---
-- @usage
-- nmap -sn -Pn --script ip-geolocation-geoplugin,ip-geolocation-map-kml --script-args ip-geolocation-map-kml.map_path=map.kml <target>
--
-- @output
-- | ip-geolocation-map-kml:
-- |_ The map has been saved at 'map.kml'.
--
-- @args ip-geolocation-map-kml.map_path (REQUIRED)
author = "Mak Kolybabi <mak@kolybabi.com>"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"safe"}
local render = function(path)
local kml = {'<?xml version="1.0" encoding="UTF-8"?>\n<kml xmlns="http://www.opengis.net/kml/2.2">\n <Document>'}
for ip, coords in pairs(geoip.get_all_by_ip()) do
table.insert(kml, ([[
<Placemark>
<name>%s</name>
<Point>
<coordinates>%s,%s</coordinates>
</Point>
</Placemark>]]):format(ip, coords["longitude"], coords["latitude"])
)
end
table.insert(kml, ' </Document>\n</kml>\n')
kml = table.concat(kml, "\n")
local f = io.open(path, "w")
if not f then
return false, ("Failed to open file '%s'."):format(path)
end
if not f:write(kml) then
return false, ("Failed to write file '%s'."):format(path)
end
f:close()
return true, ("The map has been saved at '%s'."):format(path)
end
local parse_args = function()
local map_path = stdnse.get_script_args(SCRIPT_NAME .. '.map_path')
if not map_path then
return false, "Need to specify a path for the map."
end
return true, map_path
end
postrule = function()
-- Only run if a previous script has registered geolocation data.
return not geoip.empty()
end
action = function()
local output = stdnse.output_table()
-- Parse and sanity check the command line arguments.
local status, path = parse_args()
if not status then
output.ERROR = path
return output, output.ERROR
end
-- Render the map.
local status, msg = render(path)
if not status then
output.ERROR = msg
return output, output.ERROR
end
return msg
end

View File

@@ -1,4 +1,5 @@
local bit = require "bit"
local geoip = require "geoip"
local io = require "io"
local ipOps = require "ipOps"
local math = require "math"
@@ -492,6 +493,7 @@ local GeoIP = {
output_record_by_addr = function(self,addr)
local loc = self:record_by_addr(addr)
if not loc then return nil end
geoip.add(addr, loc.latitude, loc.longitude)
setmetatable(loc, record_metatable)
return loc
end,

View File

@@ -279,6 +279,9 @@ Entry { filename = "informix-tables.nse", categories = { "auth", "intrusive", }
Entry { filename = "ip-forwarding.nse", categories = { "discovery", "safe", } }
Entry { filename = "ip-geolocation-geoplugin.nse", categories = { "discovery", "external", "safe", } }
Entry { filename = "ip-geolocation-ipinfodb.nse", categories = { "discovery", "external", "safe", } }
Entry { filename = "ip-geolocation-map-bing.nse", categories = { "external", "safe", } }
Entry { filename = "ip-geolocation-map-google.nse", categories = { "external", "safe", } }
Entry { filename = "ip-geolocation-map-kml.nse", categories = { "safe", } }
Entry { filename = "ip-geolocation-maxmind.nse", categories = { "discovery", "external", "safe", } }
Entry { filename = "ip-https-discover.nse", categories = { "default", "discovery", "safe", } }
Entry { filename = "ipidseq.nse", categories = { "discovery", "safe", } }