1
0
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:
dmiller
2017-03-14 00:15:22 +00:00
parent 2091ce3199
commit 84a824c2cf
5 changed files with 714 additions and 17 deletions

View File

@@ -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]

View File

@@ -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;

View File

@@ -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,

View 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

View File

@@ -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", } }