mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 06:01:28 +00:00
New script broadcast-ospf2-discover. Closes #743
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
# Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o [NSE][GH#743] New script broadcast-ospf2-discover discovers OSPF 2 routers
|
||||
and neighbors. OSPFv2 authentication is supported. [Emiliano Ticci]
|
||||
|
||||
o [NSE][GH#740] New script http-vuln-cve2017-5638 checks for the RCE bug in
|
||||
Apache Struts. [Seth Jackson]
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
--
|
||||
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
|
||||
|
||||
local math = require "math"
|
||||
local stdnse = require "stdnse"
|
||||
local string = require "string"
|
||||
local table = require "table"
|
||||
@@ -674,6 +675,32 @@ hex_to_bin = function( hex )
|
||||
return status, result
|
||||
end
|
||||
|
||||
---
|
||||
-- Convert a CIDR subnet mask to dotted decimal notation.
|
||||
--
|
||||
-- @param subnet CIDR string representing the subnet mask.
|
||||
-- @usage
|
||||
-- local netmask = ipOps.cidr_to_subnet( "/16" )
|
||||
-- @return Dotted decimal representation of the suppliet subnet mask (e.g. "255.255.0.0")
|
||||
cidr_to_subnet = function( subnet )
|
||||
local bits = subnet:match("/(%d%d)$")
|
||||
if not bits then return nil end
|
||||
return fromdword((0xFFFFFFFF >> tonumber(bits)) ~ 0xFFFFFFFF)
|
||||
end
|
||||
|
||||
---
|
||||
-- Convert a dotted decimal subnet mask to CIDR notation.
|
||||
--
|
||||
-- @param subnet Dotted decimal string representing the subnet mask.
|
||||
-- @usage
|
||||
-- local cidr = ipOps.subnet_to_cidr( "255.255.0.0" )
|
||||
-- @return CIDR representation of the supplied subnet mask (e.g. "/16").
|
||||
subnet_to_cidr = function( subnet )
|
||||
local dword, err = todword(subnet)
|
||||
if not dword then return nil, err end
|
||||
return "/" .. tostring(32 - (math.tointeger(math.log((dword ~ 0xFFFFFFFF) + 1, 2))))
|
||||
end
|
||||
|
||||
--Ignore the rest if we are not testing.
|
||||
if not unittest.testing() then
|
||||
return _ENV
|
||||
@@ -801,5 +828,7 @@ do
|
||||
test_suite:add_test(unittest.is_nil(expand_ip("2001:db8::1", "ipv4")),
|
||||
"IPv6 to IPv4")
|
||||
end
|
||||
test_suite:add_test(unittest.equal(cidr_to_subnet("/16"), "255.255.0.0"), "cidr_to_subnet")
|
||||
test_suite:add_test(unittest.equal(subnet_to_cidr("255.255.0.0"), "/16"), "subnet_to_cidr")
|
||||
|
||||
return _ENV;
|
||||
|
||||
267
nselib/ospf.lua
267
nselib/ospf.lua
@@ -1,21 +1,25 @@
|
||||
---
|
||||
-- A minimalistic OSPF (Open Shortest Path First routing protocol) library, currently supporting IPv4 and the following
|
||||
-- OSPF message types: HELLO
|
||||
-- A limited OSPF (Open Shortest Path First routing protocol) library, currently supporting IPv4 and the following
|
||||
-- OSPF message types: HELLO, DB_DESCRIPTION, LS_REQUEST, LS_UPDATE
|
||||
--
|
||||
-- The library consists of an OSPF class that contains code to handle OSPFv2 packets.
|
||||
--
|
||||
-- @author Patrik Karlsson <patrik@cqure.net>
|
||||
-- @author Emiliano Ticci <emiticci@gmail.com>
|
||||
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
|
||||
|
||||
local bin = require "bin"
|
||||
local bit = require "bit"
|
||||
local math = require "math"
|
||||
local stdnse = require "stdnse"
|
||||
local string = require "string"
|
||||
local table = require "table"
|
||||
local ipOps = require "ipOps"
|
||||
local packet = require "packet"
|
||||
_ENV = stdnse.module("ospf", stdnse.seeall)
|
||||
|
||||
local have_ssl, openssl = pcall(require, "openssl")
|
||||
|
||||
-- The OSPF class.
|
||||
OSPF = {
|
||||
|
||||
@@ -23,13 +27,10 @@ OSPF = {
|
||||
Message = {
|
||||
HELLO = 1,
|
||||
DB_DESCRIPTION = 2,
|
||||
LS_REQUEST = 3,
|
||||
LS_UPDATE = 4,
|
||||
},
|
||||
|
||||
LSUpdate = {
|
||||
|
||||
},
|
||||
|
||||
Header = {
|
||||
size = 24,
|
||||
new = function(self, type, area_id, router_id, auth_type, auth_data)
|
||||
@@ -68,7 +69,7 @@ OSPF = {
|
||||
local _
|
||||
_, header.auth_data.keyid = bin.unpack(">C", data, pos+2)
|
||||
_, header.auth_data.length = bin.unpack(">C", data, pos+3)
|
||||
_, header.auth_data.seq = bin.unpack(">C", data, pos+4)
|
||||
_, header.auth_data.seq = bin.unpack(">I", data, pos+4)
|
||||
_, header.auth_data.hash = bin.unpack(">H"..header.auth_data.length, data, header.length+1)
|
||||
else
|
||||
-- Shouldn't happen
|
||||
@@ -102,9 +103,9 @@ OSPF = {
|
||||
if self.auth_type == 0x00 then
|
||||
auth = bin.pack(">L", 0x00)
|
||||
elseif self.auth_type == 0x01 then
|
||||
auth = bin.pack(">A8", self.auth_data.password)
|
||||
auth = bin.pack(">A", self.auth_data.password)
|
||||
elseif self.auth_type == 0x02 then
|
||||
auth = bin.pack(">A".. self.auth_data.length, self.auth_data.hash)
|
||||
auth = bin.pack(">SCCI", 0, self.auth_data.keyid, self.auth_data.length, self.auth_data.seq)
|
||||
end
|
||||
local hdr = bin.pack(">CCS", self.ver, self.type, self.length )
|
||||
.. bin.pack(">IISS", ipOps.todword(self.router_id), self.area_id, self.chksum, self.auth_type)
|
||||
@@ -169,10 +170,20 @@ OSPF = {
|
||||
data = data .. bin.pack(">I", ipOps.todword(n))
|
||||
end
|
||||
self.header:setLength(#data)
|
||||
if self.header.auth_data.hash then
|
||||
data = data .. self.header.auth_data.hash
|
||||
end
|
||||
return tostring(self.header) .. data
|
||||
end
|
||||
local data = tostr()
|
||||
self.header.chksum = packet.in_cksum(data:sub(1,12) .. data:sub(25))
|
||||
if have_ssl and self.header.auth_type == 0x02 then
|
||||
while string.len(self.header.auth_data.key) < 16 do
|
||||
self.header.auth_data.key = self.header.auth_data.key .. "\0"
|
||||
end
|
||||
self.header.auth_data.hash = openssl.md5(data .. bin.pack(">A", self.header.auth_data.key))
|
||||
else
|
||||
self.header.chksum = packet.in_cksum(data:sub(1,16) .. data:sub(25))
|
||||
end
|
||||
return tostr()
|
||||
end,
|
||||
|
||||
@@ -208,10 +219,9 @@ OSPF = {
|
||||
|
||||
},
|
||||
|
||||
DBDescription = {
|
||||
|
||||
LSAHeader = {
|
||||
|
||||
LSA = {
|
||||
Header = {
|
||||
size = 20,
|
||||
new = function(self)
|
||||
local o = {
|
||||
age = 0,
|
||||
@@ -228,8 +238,113 @@ OSPF = {
|
||||
return o
|
||||
end,
|
||||
|
||||
parse = function(data)
|
||||
local lsa_h = OSPF.LSA.Header:new()
|
||||
local pos = 1
|
||||
pos, lsa_h.age, lsa_h.options, lsa_h.type, lsa_h.id, lsa_h.adv_router, lsa_h.sequence, lsa_h.checksum, lsa_h.length = bin.unpack(">SCCIIH4H2S", data, pos)
|
||||
|
||||
lsa_h.id = ipOps.fromdword(lsa_h.id)
|
||||
lsa_h.adv_router = ipOps.fromdword(lsa_h.adv_router)
|
||||
return lsa_h
|
||||
end,
|
||||
|
||||
},
|
||||
|
||||
Link = {
|
||||
new = function(self)
|
||||
local o = {
|
||||
id = 0,
|
||||
data = 0,
|
||||
type = 2,
|
||||
num_metrics = 0,
|
||||
metric = 10,
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
parse = function(data)
|
||||
local lsa_link = OSPF.LSA.Link:new()
|
||||
local pos = 1
|
||||
pos, lsa_link.id, lsa_link.data, lsa_link.type, lsa_link.num_metrics, lsa_link.metric = bin.unpack(">IICCS", data, pos)
|
||||
lsa_link.id = ipOps.fromdword(lsa_link.id)
|
||||
lsa_link.data = ipOps.fromdword(lsa_link.data)
|
||||
return lsa_link
|
||||
end,
|
||||
},
|
||||
|
||||
Router = {
|
||||
new = function(self)
|
||||
local o = {
|
||||
header = OSPF.LSA.Header:new(),
|
||||
flags = 0,
|
||||
num_links = 0,
|
||||
links = {},
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
parse = function(data)
|
||||
local router = OSPF.LSA.Router:new()
|
||||
local pos = OSPF.LSA.Header.size + 1
|
||||
router.header = OSPF.LSA.Header.parse(data)
|
||||
pos, router.flags, router.num_links = bin.unpack(">CxS", data, pos)
|
||||
|
||||
while ( pos < router.header.length ) do
|
||||
table.insert(router.links, OSPF.LSA.Link.parse(data:sub(pos, pos + 12)))
|
||||
pos = pos + 12
|
||||
end
|
||||
|
||||
return router
|
||||
end,
|
||||
},
|
||||
|
||||
ASExternal = {
|
||||
new = function(self)
|
||||
local o = {
|
||||
header = OSPF.LSA.Header:new(),
|
||||
netmask = 0,
|
||||
ext_type = 1,
|
||||
metric = 1,
|
||||
fw_address = 0,
|
||||
ext_tag = 0,
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
parse = function(data)
|
||||
local as_ext = OSPF.LSA.ASExternal:new()
|
||||
local pos = OSPF.LSA.Header.size + 1
|
||||
as_ext.header = OSPF.LSA.Header.parse(data)
|
||||
|
||||
pos, as_ext.netmask, as_ext.metric, as_ext.fw_address, as_ext.ext_tag = bin.unpack(">IIII", data, pos)
|
||||
as_ext.netmask = ipOps.fromdword(as_ext.netmask)
|
||||
as_ext.ext_type = 1 + bit.rshift(bit.band(as_ext.metric, 0xFF000000), 31)
|
||||
as_ext.metric = bit.band(as_ext.metric, 0x00FFFFFF)
|
||||
as_ext.fw_address = ipOps.fromdword(as_ext.fw_address)
|
||||
|
||||
return as_ext
|
||||
end,
|
||||
},
|
||||
|
||||
parse = function(data)
|
||||
local header = OSPF.LSA.Header.parse(data)
|
||||
if header.type == 1 then
|
||||
return OSPF.LSA.Router.parse(data)
|
||||
elseif header.type == 5 then
|
||||
return OSPF.LSA.ASExternal.parse(data)
|
||||
end
|
||||
return header.length
|
||||
end,
|
||||
},
|
||||
|
||||
DBDescription = {
|
||||
|
||||
new = function(self)
|
||||
local o = {
|
||||
header = OSPF.Header:new(OSPF.Message.DB_DESCRIPTION),
|
||||
@@ -238,7 +353,8 @@ OSPF = {
|
||||
init = true,
|
||||
more = true,
|
||||
master = true,
|
||||
sequence = math.random(123456789)
|
||||
sequence = math.random(123456789),
|
||||
lsa_headers = {}
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
@@ -254,10 +370,20 @@ OSPF = {
|
||||
|
||||
local data = bin.pack(">SCCI", self.mtu, self.options, flags, self.sequence)
|
||||
self.header:setLength(#data)
|
||||
if self.header.auth_data.hash then
|
||||
data = data .. self.header.auth_data.hash
|
||||
end
|
||||
return tostring(self.header) .. data
|
||||
end
|
||||
local data = tostr()
|
||||
self.header.chksum = packet.in_cksum(data:sub(1,12) .. data:sub(25))
|
||||
if have_ssl and self.header.auth_type == 0x02 then
|
||||
while string.len(self.header.auth_data.key) < 16 do
|
||||
self.header.auth_data.key = self.header.auth_data.key .. "\0"
|
||||
end
|
||||
self.header.auth_data.hash = openssl.md5(data .. bin.pack(">A", self.header.auth_data.key))
|
||||
else
|
||||
self.header.chksum = packet.in_cksum(data:sub(1,16) .. data:sub(25))
|
||||
end
|
||||
return tostr()
|
||||
end,
|
||||
|
||||
@@ -265,7 +391,7 @@ OSPF = {
|
||||
local desc = OSPF.DBDescription:new()
|
||||
local pos = OSPF.Header.size + 1
|
||||
desc.header = OSPF.Header.parse(data)
|
||||
assert( #data == desc.header.length, "OSPF packet too short")
|
||||
assert( #data >= desc.header.length, "OSPF packet too short")
|
||||
|
||||
local flags = 0
|
||||
pos, desc.mtu, desc.options, flags, desc.sequence = bin.unpack(">SCCI", data, pos)
|
||||
@@ -274,6 +400,11 @@ OSPF = {
|
||||
desc.more = ( bit.band(flags, 2) == 2 )
|
||||
desc.master = ( bit.band(flags, 1) == 1 )
|
||||
|
||||
while ( pos < desc.header.length ) do
|
||||
table.insert(desc.lsa_headers, OSPF.LSA.Header.parse(data:sub(pos, pos + 20)))
|
||||
pos = pos + 20
|
||||
end
|
||||
|
||||
if ( desc.init or not(desc.more) ) then
|
||||
return desc
|
||||
end
|
||||
@@ -283,6 +414,104 @@ OSPF = {
|
||||
|
||||
},
|
||||
|
||||
LSRequest = {
|
||||
new = function(self)
|
||||
local o = {
|
||||
header = OSPF.Header:new(OSPF.Message.LS_REQUEST),
|
||||
ls_requests = {},
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
--- Adds a request to the list of requests.
|
||||
-- @param type LS Type.
|
||||
-- @param id Link State ID
|
||||
-- @param adv_router Advertising Router
|
||||
addRequest = function(self, type, id, adv_router)
|
||||
local request = {
|
||||
type = type,
|
||||
id = id,
|
||||
adv_router = adv_router
|
||||
}
|
||||
table.insert(self.ls_requests, request)
|
||||
end,
|
||||
|
||||
__tostring = function(self)
|
||||
local function tostr()
|
||||
local data = ""
|
||||
for _, req in ipairs(self.ls_requests) do
|
||||
data = data .. bin.pack(">III", req.type, ipOps.todword(req.id), ipOps.todword(req.adv_router))
|
||||
end
|
||||
self.header:setLength(#data)
|
||||
if self.header.auth_data.hash then
|
||||
data = data .. self.header.auth_data.hash
|
||||
end
|
||||
return tostring(self.header) .. data
|
||||
end
|
||||
local data = tostr()
|
||||
if have_ssl and self.header.auth_type == 0x02 then
|
||||
while string.len(self.header.auth_data.key) < 16 do
|
||||
self.header.auth_data.key = self.header.auth_data.key .. "\0"
|
||||
end
|
||||
self.header.auth_data.hash = openssl.md5(data .. bin.pack(">A", self.header.auth_data.key))
|
||||
else
|
||||
self.header.chksum = packet.in_cksum(data:sub(1,16) .. data:sub(25))
|
||||
end
|
||||
return tostr()
|
||||
end,
|
||||
|
||||
parse = function(data)
|
||||
local ls_req = OSPF.LSRequest:new()
|
||||
local pos = OSPF.Header.size + 1
|
||||
ls_req.header = OSPF.Header.parse(data)
|
||||
assert( #data >= ls_req.header.length, "OSPF packet too short")
|
||||
|
||||
while ( pos < #data ) do
|
||||
local req = {}
|
||||
pos, req.type, req.id, req.adv_router = bin.unpack(">III", data, pos)
|
||||
table.insert(ls_req.ls_requests, req)
|
||||
end
|
||||
|
||||
return ls_req
|
||||
end,
|
||||
},
|
||||
|
||||
LSUpdate = {
|
||||
new = function(self)
|
||||
local o = {
|
||||
header = OSPF.Header:new(OSPF.Message.LS_UPDATE),
|
||||
num_lsas = 0,
|
||||
lsas = {},
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
parse = function(data)
|
||||
local lsu = OSPF.LSUpdate:new()
|
||||
local pos = OSPF.Header.size + 1
|
||||
lsu.header = OSPF.Header.parse(data)
|
||||
assert( #data >= lsu.header.length, "OSPF packet too short")
|
||||
|
||||
pos, lsu.num_lsas = bin.unpack(">I", data, pos)
|
||||
|
||||
while ( pos < lsu.header.length ) do
|
||||
local lsa = OSPF.LSA.parse(data:sub(pos))
|
||||
if ( type(lsa) == "table" ) then
|
||||
table.insert(lsu.lsas, lsa)
|
||||
pos = pos + lsa.header.length
|
||||
else
|
||||
pos = pos + lsa
|
||||
end
|
||||
end
|
||||
|
||||
return lsu
|
||||
end,
|
||||
},
|
||||
|
||||
Response = {
|
||||
|
||||
parse = function(data)
|
||||
@@ -291,6 +520,10 @@ OSPF = {
|
||||
return OSPF.Hello.parse( data )
|
||||
elseif( ospf_type == OSPF.Message.DB_DESCRIPTION ) then
|
||||
return OSPF.DBDescription.parse(data)
|
||||
elseif( ospf_type == OSPF.Message.LS_REQUEST ) then
|
||||
return OSPF.LSRequest.parse(data)
|
||||
elseif( ospf_type == OSPF.Message.LS_UPDATE ) then
|
||||
return OSPF.LSUpdate.parse(data)
|
||||
end
|
||||
return
|
||||
end,
|
||||
|
||||
431
scripts/broadcast-ospf2-discover.nse
Normal file
431
scripts/broadcast-ospf2-discover.nse
Normal file
@@ -0,0 +1,431 @@
|
||||
local ipOps = require "ipOps"
|
||||
local nmap = require "nmap"
|
||||
local ospf = require "ospf"
|
||||
local packet = require "packet"
|
||||
local stdnse = require "stdnse"
|
||||
local target = require "target"
|
||||
local os = require "os"
|
||||
local string = require "string"
|
||||
local table = require "table"
|
||||
|
||||
local have_ssl, openssl = pcall(require,'openssl')
|
||||
|
||||
description = [[
|
||||
Discover IPv4 networks using Open Shortest Path First version 2(OSPFv2) protocol.
|
||||
|
||||
The script works by listening for OSPF Hello packets from the 224.0.0.5
|
||||
multicast address. The script then replies and attempts to create a neighbor
|
||||
relationship, in order to discover network database.
|
||||
|
||||
If no interface was provided as a script argument or through the -e option,
|
||||
the script will fail unless a single interface is present on the system.
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- nmap --script=broadcast-ospf2-discover
|
||||
-- nmap --script=broadcast-ospf2-discover -e wlan0
|
||||
--
|
||||
-- @args broadcast-ospf2-discover.md5_key MD5 digest key to use if message digest
|
||||
-- authentication is disclosed.
|
||||
--
|
||||
-- @args broadcast-ospf2-discover.router_id Router ID to use. Defaults to 0.0.0.1
|
||||
--
|
||||
-- @args broadcast-ospf2-discover.timeout Time in seconds that the script waits for
|
||||
-- hello from other routers. Defaults to 10 seconds, matching OSPFv2 default
|
||||
-- value for hello interval.
|
||||
--
|
||||
-- @args broadcast-ospf2-discover.interface Interface to send on (overrides -e). Mandatory
|
||||
-- if not using -e and multiple interfaces are present.
|
||||
--
|
||||
-- @output
|
||||
-- Pre-scan script results:
|
||||
-- | broadcast-ospf2-discover:
|
||||
-- | Area ID: 0.0.0.0
|
||||
-- | External Routes
|
||||
-- | 192.168.24.0/24
|
||||
-- |_ Use the newtargets script-arg to add the results as targets
|
||||
--
|
||||
|
||||
author = "Emiliano Ticci"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"broadcast", "discovery", "safe"}
|
||||
|
||||
prerule = function()
|
||||
if nmap.address_family() ~= "inet" then
|
||||
stdnse.print_verbose("is IPv4 only.")
|
||||
return false
|
||||
end
|
||||
if not nmap.is_privileged() then
|
||||
stdnse.print_verbose("not running for lack of privileges.")
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Script constants
|
||||
OSPF_ALL_ROUTERS = "224.0.0.5"
|
||||
OSPF_MSG_HELLO = 0x01
|
||||
OSPF_MSG_DBDESC = 0x02
|
||||
OSPF_MSG_LSREQ = 0x03
|
||||
OSPF_MSG_LSUPD = 0x04
|
||||
local md5_key, router_id
|
||||
|
||||
-- Convenience functions
|
||||
local function fail(err) return stdnse.format_output(false, err) end
|
||||
|
||||
-- Print OSPFv2 LSA Header packet details to debug output.
|
||||
-- @param hello OSPFv2 LSA Header packet
|
||||
local ospfDumpLSAHeader = function(lsa_h)
|
||||
if 2 > nmap.debugging() then
|
||||
return
|
||||
end
|
||||
stdnse.print_debug(2, "| LS Age: %s", lsa_h.age)
|
||||
stdnse.print_debug(2, "| Options: %s", lsa_h.options)
|
||||
stdnse.print_debug(2, "| LS Type: %s", lsa_h.type)
|
||||
stdnse.print_debug(2, "| Link State ID: %s", lsa_h.id)
|
||||
stdnse.print_debug(2, "| Advertising Router: %s", lsa_h.adv_router)
|
||||
stdnse.print_debug(2, "| Sequence: 0x%s", lsa_h.sequence)
|
||||
stdnse.print_debug(2, "| Checksum: 0x%s", lsa_h.checksum)
|
||||
stdnse.print_debug(2, "| Length: %s", lsa_h.length)
|
||||
end
|
||||
|
||||
-- Print OSPFv2 Database Description packet details to debug output.
|
||||
-- @param hello OSPFv2 Database Description packet
|
||||
local ospfDumpDBDesc = function(db_desc)
|
||||
if 2 > nmap.debugging() then
|
||||
return
|
||||
end
|
||||
stdnse.print_debug(2, "| MTU: %s", db_desc.mtu)
|
||||
stdnse.print_debug(2, "| Options: %s", db_desc.options)
|
||||
stdnse.print_debug(2, "| Init: %s", db_desc.init)
|
||||
stdnse.print_debug(2, "| More: %s", db_desc.more)
|
||||
stdnse.print_debug(2, "| Master: %s", db_desc.master)
|
||||
stdnse.print_debug(2, "| Sequence: %s", db_desc.sequence)
|
||||
if #db_desc.lsa_headers > 0 then
|
||||
stdnse.print_debug(2, "| LSA Headers:")
|
||||
for i, lsa_h in ipairs(db_desc.lsa_headers) do
|
||||
ospfDumpLSAHeader(lsa_h)
|
||||
if i < #db_desc.lsa_headers then
|
||||
stdnse.print_debug(2, "|")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Print OSPFv2 Hello packet details to debug output.
|
||||
-- @param hello OSPFv2 Hello packet
|
||||
local ospfDumpHello = function(hello)
|
||||
if 2 > nmap.debugging() then
|
||||
return
|
||||
end
|
||||
stdnse.print_debug(2, "| Router ID: %s", hello.header.router_id)
|
||||
stdnse.print_debug(2, "| Area ID: %s", ipOps.fromdword(hello.header.area_id))
|
||||
stdnse.print_debug(2, "| Checksum: %s", hello.header.chksum)
|
||||
stdnse.print_debug(2, "| Auth Type: %s", hello.header.auth_type)
|
||||
if hello.header.auth_type == 0x01 then
|
||||
stdnse.print_debug(2, "| Auth Password: %s", hello.header.auth_data.password)
|
||||
elseif hello.header.auth_type == 0x02 then
|
||||
stdnse.print_debug(2, "| Auth Crypt Key ID: %s", hello.header.auth_data.keyid)
|
||||
stdnse.print_debug(2, "| Auth Data Length: %s", hello.header.auth_data.length)
|
||||
stdnse.print_debug(2, "| Auth Crypt Seq: %s", hello.header.auth_data.seq)
|
||||
end
|
||||
stdnse.print_debug(2, "| Netmask: %s", hello.netmask)
|
||||
stdnse.print_debug(2, "| Hello interval: %s", hello.interval)
|
||||
stdnse.print_debug(2, "| Options: %s", hello.options)
|
||||
stdnse.print_debug(2, "| Priority: %s", hello.prio)
|
||||
stdnse.print_debug(2, "| Dead interval: %s", hello.router_dead_interval)
|
||||
stdnse.print_debug(2, "| Designated Router: %s", hello.DR)
|
||||
stdnse.print_debug(2, "| Backup Router: %s", hello.BDR)
|
||||
stdnse.print_debug(2, "| Neighbors: %s", table.concat(hello.neighbors, ","))
|
||||
end
|
||||
|
||||
-- Print OSPFv2 LS Request packet details to debug output.
|
||||
-- @param ls_req OSPFv2 LS Request packet
|
||||
local ospfDumpLSRequest = function(ls_req)
|
||||
if 2 > nmap.debugging() then
|
||||
return
|
||||
end
|
||||
for i, req in ipairs(ls_req.ls_requests) do
|
||||
stdnse.print_debug(2, "| LS Type: %s", req.type)
|
||||
stdnse.print_debug(2, "| Link State ID: %s", req.id)
|
||||
stdnse.print_debug(2, "| Avertising Router: %s", req.adv_router)
|
||||
if i < #ls_req.ls_requests then
|
||||
stdnse.print_debug(2, "|")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Print OSPFv2 LS Update packet details to debug output.
|
||||
-- @param ls_upd OSPFv2 LS Update packet
|
||||
local ospfDumpLSUpdate = function(ls_upd)
|
||||
stdnse.print_debug(2, "| Number of LSAs: %s", ls_upd.num_lsas)
|
||||
for i, lsa in ipairs(ls_upd.lsas) do
|
||||
-- Only Type 1 (Router-LSA) and Type 5 (AS-External-LSA) are supported at the moment
|
||||
ospfDumpLSAHeader(lsa.header)
|
||||
if lsa.header.type == 1 then
|
||||
stdnse.print_debug(2, "| Flags: %s", lsa.flags)
|
||||
stdnse.print_debug(2, "| Number of links: %s", lsa.num_links)
|
||||
for j, link in ipairs(lsa.links) do
|
||||
stdnse.print_debug(2, "| Link ID: %s", link.id)
|
||||
stdnse.print_debug(2, "| Link Data: %s", link.data)
|
||||
stdnse.print_debug(2, "| Link Type: %s", link.type)
|
||||
stdnse.print_debug(2, "| Number of Metrics: %s", link.num_metrics)
|
||||
stdnse.print_debug(2, "| 0 Metric: %s", link.metric)
|
||||
if j < #lsa.links then
|
||||
stdnse.print_debug(2, "|")
|
||||
end
|
||||
end
|
||||
if i < #ls_upd.lsas then
|
||||
stdnse.print_debug(2, "|")
|
||||
end
|
||||
elseif lsa.header.type == 5 then
|
||||
stdnse.print_debug(2, "| Netmask: %s", lsa.netmask)
|
||||
stdnse.print_debug(2, "| External Type: %s", lsa.ext_type)
|
||||
stdnse.print_debug(2, "| Metric: %s", lsa.metric)
|
||||
stdnse.print_debug(2, "| Forwarding Address: %s", lsa.fw_address)
|
||||
stdnse.print_debug(2, "| External Route Tag: %s", lsa.ext_tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Send OSPFv2 packet to specified destination.
|
||||
-- @param interface Source interface to use
|
||||
-- @param ip_dst Destination IP address
|
||||
-- @param mac_dst Destination MAC address
|
||||
-- @param ospf_packet Raw OSPF packet
|
||||
local ospfSend = function(interface, ip_dst, mac_dst, ospf_packet)
|
||||
local dnet = nmap.new_dnet()
|
||||
local probe = packet.Frame:new()
|
||||
|
||||
probe.mac_src = interface.mac
|
||||
probe.mac_dst = mac_dst
|
||||
probe.ip_bin_src = ipOps.ip_to_str(interface.address)
|
||||
probe.ip_bin_dst = ipOps.ip_to_str(ip_dst)
|
||||
probe.l3_packet = ospf_packet
|
||||
probe.ip_dsf = 0xc0
|
||||
probe.ip_p = 89
|
||||
probe.ip_ttl = 1
|
||||
|
||||
probe:build_ip_packet()
|
||||
probe:build_ether_frame()
|
||||
|
||||
dnet:ethernet_open(interface.device)
|
||||
dnet:ethernet_send(probe.frame_buf)
|
||||
dnet:ethernet_close()
|
||||
end
|
||||
|
||||
-- Prepare OSPFv2 packet header for reply.
|
||||
-- @param packet_in Source packet
|
||||
-- @param packet_out Destination packet
|
||||
local ospfSetHeader = function(packet_in, packet_out)
|
||||
packet_out.header:setRouterId(router_id)
|
||||
packet_out.header:setAreaID(packet_in.header.area_id)
|
||||
if packet_in.header.auth_type == 0x01 then
|
||||
packet_out.header.auth_type = 0x01
|
||||
packet_out.header.auth_data.password = packet_in.header.auth_data.password
|
||||
elseif packet_in.header.auth_type == 0x02 then
|
||||
packet_out.header.auth_type = 0x02
|
||||
packet_out.header.auth_data.key = md5_key
|
||||
packet_out.header.auth_data.keyid = packet_in.header.auth_data.keyid
|
||||
packet_out.header.auth_data.length = 16
|
||||
packet_out.header.auth_data.seq = os.time()
|
||||
end
|
||||
|
||||
return packet_out
|
||||
end
|
||||
|
||||
-- Reply to OSPFv2 Database Description with an LS Request.
|
||||
-- @param interface Source interface
|
||||
-- @param mac_dst Destination MAC address
|
||||
-- @param db_desc_in OSPFv2 Database Description packet to reply for
|
||||
local ospfSendLSRequest = function(interface, mac_dst, db_desc_in)
|
||||
local ls_req_out = ospf.OSPF.LSRequest:new()
|
||||
ls_req_out = ospfSetHeader(db_desc_in, ls_req_out)
|
||||
|
||||
for i, lsa_h in ipairs(db_desc_in.lsa_headers) do
|
||||
ls_req_out:addRequest(lsa_h.type, lsa_h.id, lsa_h.adv_router)
|
||||
end
|
||||
|
||||
stdnse.print_debug(2, "Crafted OSPFv2 LS Request packet with the following parameters:")
|
||||
ospfDumpLSRequest(ls_req_out)
|
||||
ospfSend(interface, db_desc_in.header.router_id, mac_dst, tostring(ls_req_out))
|
||||
end
|
||||
|
||||
-- Reply to given OSPFv2 Database Description packet.
|
||||
-- @param interface Source interface
|
||||
-- @param mac_dst Destination MAC address
|
||||
-- @param db_desc_in OSPFv2 Database Description packet to reply for
|
||||
local ospfReplyDBDesc = function(interface, mac_dst, db_desc_in)
|
||||
local reply = false
|
||||
local db_desc_out = ospf.OSPF.DBDescription:new()
|
||||
db_desc_out = ospfSetHeader(db_desc_in, db_desc_out)
|
||||
|
||||
if db_desc_in.init == true then
|
||||
db_desc_out.init = false
|
||||
db_desc_out.more = db_desc_in.more
|
||||
db_desc_out.master = false
|
||||
db_desc_out.sequence = db_desc_in.sequence
|
||||
reply = true
|
||||
elseif #db_desc_in.lsa_headers > 0 then
|
||||
ospfSendLSRequest(interface, mac_dst, db_desc_in)
|
||||
return true
|
||||
end
|
||||
|
||||
if reply then
|
||||
stdnse.print_debug(2, "Crafted OSPFv2 Database Description packet with the following parameters:")
|
||||
ospfDumpDBDesc(db_desc_out)
|
||||
ospfSend(interface, db_desc_in.header.router_id, mac_dst, tostring(db_desc_out))
|
||||
end
|
||||
|
||||
return reply
|
||||
end
|
||||
|
||||
-- Reply to given OSPFv2 Hello packet sending another Hello to
|
||||
-- "All OSPF Routers" multicast address (224.0.0.5).
|
||||
-- @param interface Source interface
|
||||
-- @param hello_in OSPFv2 Hello packet to reply for
|
||||
local ospfReplyHello = function(interface, hello_in)
|
||||
local hello_out = ospf.OSPF.Hello:new()
|
||||
hello_out = ospfSetHeader(hello_in, hello_out)
|
||||
hello_out.interval = hello_in.interval
|
||||
hello_out.router_dead_interval = hello_in.router_dead_interval
|
||||
hello_out:setDesignatedRouter(hello_in.header.router_id)
|
||||
hello_out:setNetmask(hello_in.netmask)
|
||||
hello_out:addNeighbor(hello_in.header.router_id)
|
||||
|
||||
stdnse.print_debug(2, "Crafted OSPFv2 Hello packet with the following parameters:")
|
||||
ospfDumpHello(hello_out)
|
||||
|
||||
ospfSend(interface, OSPF_ALL_ROUTERS, "\x01\x00\x5e\x00\x00\x05", tostring(hello_out))
|
||||
end
|
||||
|
||||
-- Listen for OSPF packets on a specified interface.
|
||||
-- @param interface Interface to use
|
||||
-- @param timeout Amount of time to listen in seconds
|
||||
local ospfListen = function(interface, timeout)
|
||||
local status, l2_data, l3_data, ospf_raw, _
|
||||
local start = nmap.clock_ms()
|
||||
|
||||
stdnse.print_debug("Start listening on interface %s...", interface.shortname)
|
||||
local listener = nmap.new_socket()
|
||||
listener:set_timeout(1000)
|
||||
listener:pcap_open(interface.device, 1500, true, "ip proto 89 and not (ip src host " .. interface.address .. ")")
|
||||
while (nmap.clock_ms() - start) < (timeout * 1000) do
|
||||
status, _, l2_data, l3_data = listener:pcap_receive()
|
||||
if status then
|
||||
stdnse.print_debug(2, "Packet received on interface %s.", interface.shortname)
|
||||
local p = packet.Packet:new(l3_data, #l3_data)
|
||||
local ospf_raw = string.sub(l3_data, p.ip_hl * 4 + 1)
|
||||
if ospf_raw:byte(1) == 0x02 and ospf_raw:byte(2) == OSPF_MSG_HELLO then
|
||||
stdnse.print_debug(2, "OSPFv2 Hello packet detected.")
|
||||
|
||||
local ospf_hello = ospf.OSPF.Hello.parse(ospf_raw)
|
||||
stdnse.print_debug(2, "Captured OSPFv2 Hello packet with the following parameters:")
|
||||
ospfDumpHello(ospf_hello)
|
||||
|
||||
-- Additional checks required for message digest authentication
|
||||
if ospf_hello.header.auth_type == 0x02 then
|
||||
if not md5_key then
|
||||
return fail("Argument md5_key must be present when message digest authentication is disclosed.")
|
||||
elseif not have_ssl then
|
||||
return fail("Cannot handle message digest authentication unless openssl is compiled in.")
|
||||
end
|
||||
end
|
||||
|
||||
ospfReplyHello(interface, ospf_hello)
|
||||
start = nmap.clock_ms()
|
||||
elseif ospf_raw:byte(1) == 0x02 and ospf_raw:byte(2) == OSPF_MSG_DBDESC then
|
||||
stdnse.print_debug(2, "OSPFv2 Database Description packet detected.")
|
||||
|
||||
local ospf_db_desc = ospf.OSPF.DBDescription.parse(ospf_raw)
|
||||
stdnse.print_debug(2, "Captured OSPFv2 Database Description packet with the following parameters:")
|
||||
ospfDumpDBDesc(ospf_db_desc)
|
||||
|
||||
if not ospfReplyDBDesc(interface, string.sub(l2_data, 7, 12), ospf_db_desc) then
|
||||
return
|
||||
end
|
||||
elseif ospf_raw:byte(1) == 0x02 and ospf_raw:byte(2) == OSPF_MSG_LSUPD then
|
||||
stdnse.print_debug(2, "OSPFv2 LS Update packet detected.")
|
||||
|
||||
local ospf_ls_upd = ospf.OSPF.LSUpdate.parse(ospf_raw)
|
||||
stdnse.print_debug(2, "Captured OSPFv2 LS Update packet with the following parameters:")
|
||||
ospfDumpLSUpdate(ospf_ls_upd)
|
||||
|
||||
local targets = {}
|
||||
for i, lsa in ipairs(ospf_ls_upd.lsas) do
|
||||
-- Only Type 1 (Router-LSA) and Type 5 (AS-External-LSA) are supported at the moment
|
||||
if lsa.header.type == 1 then
|
||||
for j, link in ipairs(lsa.links) do
|
||||
if link.type == 3 then
|
||||
local target = link.id .. ipOps.subnet_to_cidr(link.data)
|
||||
targets[target] = 1
|
||||
end
|
||||
end
|
||||
elseif lsa.header.type == 5 then
|
||||
local target = lsa.header.id .. ipOps.subnet_to_cidr(lsa.netmask)
|
||||
targets[target] = 1
|
||||
end
|
||||
end
|
||||
local output = stdnse.output_table()
|
||||
if next(targets) then
|
||||
local out_links = {}
|
||||
output["Area ID"] = ipOps.fromdword(ospf_ls_upd.header.area_id)
|
||||
output["External Routes"] = out_links
|
||||
for t, _ in pairs(targets) do
|
||||
table.insert(out_links, t)
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(t)
|
||||
end
|
||||
end
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
stdnse.verbose("Use the newtargets script-arg to add the results as targets")
|
||||
end
|
||||
end
|
||||
return output
|
||||
end
|
||||
end
|
||||
end
|
||||
listener:pcap_close()
|
||||
end
|
||||
|
||||
action = function()
|
||||
-- Get script arguments
|
||||
md5_key = stdnse.get_script_args(SCRIPT_NAME .. ".md5_key") or false
|
||||
router_id = stdnse.get_script_args(SCRIPT_NAME .. ".router_id") or "0.0.0.1"
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout")) or 10
|
||||
local interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
stdnse.print_debug("Value for router ID argument: %s.", router_id)
|
||||
stdnse.print_debug("Value for timeout argument: %s.", timeout)
|
||||
|
||||
-- Determine interface to use
|
||||
interface = interface or nmap.get_interface()
|
||||
if interface then
|
||||
interface = nmap.get_interface_info(interface)
|
||||
if not interface then
|
||||
return fail(("Failed to retrieve %s interface information."):format(interface))
|
||||
end
|
||||
stdnse.print_debug("Will use %s interface.", interface.shortname)
|
||||
else
|
||||
local interface_list = nmap.list_interfaces()
|
||||
local interface_good = {}
|
||||
for _, os_interface in ipairs(interface_list) do
|
||||
if os_interface.address and os_interface.link == "ethernet" and
|
||||
os_interface.address:match("%d+%.%d+%.%d+%.%d+") then
|
||||
|
||||
stdnse.print_debug(2, "Found usable interface: %s.", os_interface.shortname)
|
||||
table.insert(interface_good, os_interface)
|
||||
end
|
||||
end
|
||||
if #interface_good == 1 then
|
||||
interface = interface_good[1]
|
||||
stdnse.print_debug("Will use %s interface.", interface.shortname)
|
||||
elseif #interface_good == 0 then
|
||||
return fail("Source interface not found.")
|
||||
else
|
||||
return fail("Ambiguous source interface, please specify it with -e or interface parameter.")
|
||||
end
|
||||
end
|
||||
|
||||
return ospfListen(interface, timeout)
|
||||
end
|
||||
@@ -39,6 +39,7 @@ Entry { filename = "broadcast-ms-sql-discover.nse", categories = { "broadcast",
|
||||
Entry { filename = "broadcast-netbios-master-browser.nse", categories = { "broadcast", "safe", } }
|
||||
Entry { filename = "broadcast-networker-discover.nse", categories = { "broadcast", "safe", } }
|
||||
Entry { filename = "broadcast-novell-locate.nse", categories = { "broadcast", "safe", } }
|
||||
Entry { filename = "broadcast-ospf2-discover.nse", categories = { "broadcast", "discovery", "safe", } }
|
||||
Entry { filename = "broadcast-pc-anywhere.nse", categories = { "broadcast", "safe", } }
|
||||
Entry { filename = "broadcast-pc-duo.nse", categories = { "broadcast", "safe", } }
|
||||
Entry { filename = "broadcast-pim-discovery.nse", categories = { "broadcast", "discovery", "safe", } }
|
||||
|
||||
Reference in New Issue
Block a user