Skip to content

Instantly share code, notes, and snippets.

@emyl
Created October 24, 2016 20:40
Show Gist options
  • Select an option

  • Save emyl/8b2e8d69d6b0c6dc91fc16340196883c to your computer and use it in GitHub Desktop.

Select an option

Save emyl/8b2e8d69d6b0c6dc91fc16340196883c to your computer and use it in GitHub Desktop.
NSE script for discovering IPv4 networks using Open Shortest Path First version 2(OSPFv2) protocol.
local bin = require "bin"
local ipOps = require "ipOps"
local nmap = require "nmap"
local ospf = require "ospf"
local packet = require "packet"
local stdnse = require "stdnse"
local target = require "target"
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=ospf2-networks <targets>
-- nmap --script=ospf2-networks <targets> -e wlan0
--
-- @args ospf2-networks.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 ospf2-networks.interface Interface to send on (overrides -e). Mandatory
-- if not using -e and multiple interfaces are present.
--
-- @output
-- Pre-scan script results:
-- | ospf2-networks:
-- | 192.168.2.2
-- | Area ID: 0.0.0.0
-- | External Route
-- | Protocol: OSPF
-- | Originating Router ID: 192.168.31.1
-- | Destination: 192.168.24.0/24
-- | Next hop: 192.168.31.1
-- |_ 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
-- Convenience functions
local function fail(err) return stdnse.format_output(false, err) end
-- Print OSPFv2 Database Description packet details to debug output.
-- @param hello OSPFv2 Database Description packet
local ospfDumpDBDesc = function(db_desc)
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)
end
-- Print OSPFv2 Hello packet details to debug output.
-- @param hello OSPFv2 Hello packet
local ospfDumpHello = function(hello)
stdnse.print_debug(2, "| Router ID: %s", hello.header.router_id)
stdnse.print_debug(2, "| Area ID: %s", hello.header.area_id)
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
-- 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
-- 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 db_desc_out = ospf.OSPF.DBDescription:new()
db_desc_out.header:setRouterId(interface.address)
db_desc_out.header:setAreaID(db_desc_in.header.area_id)
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
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
-- 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.header:setRouterId(interface.address)
hello_out.header:setAreaID(hello_in.header.area_id)
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, 1024, 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)
ospfReplyHello(interface, ospf_hello)
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)
ospfReplyDBDesc(interface, string.sub(l2_data, 7, 12), ospf_db_desc)
end
end
end
listener:pcap_close()
end
action = function()
-- Get script arguments
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 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
ospfListen(interface, timeout)
print("End of ospf2-networks script.")
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment