summaryrefslogtreecommitdiffstats
path: root/scripts/broadcast-rip-discover.nse
blob: 1aa19dd6fcd8f6645bdf2367b46c484ab286e3ff (plain)
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
local ipOps = require "ipOps"
local nmap = require "nmap"
local stdnse = require "stdnse"
local string = require "string"
local tab = require "tab"
local table = require "table"

description=[[
Discovers hosts and routing information from devices running RIPv2 on the
LAN. It does so by sending a RIPv2 Request command and collects the responses
from all devices responding to the request.
]]

---
-- @usage
-- nmap --script broadcast-rip-discover
--
-- @output
-- Pre-scan script results:
-- | broadcast-rip-discover:
-- | Discovered RIPv2 devices
-- |   10.0.200.107
-- |     ip           netmask        nexthop       metric
-- |     10.46.100.0  255.255.255.0  0.0.0.0       1
-- |     10.46.110.0  255.255.255.0  0.0.0.0       1
-- |     10.46.120.0  255.255.255.0  0.0.0.0       1
-- |     10.46.123.0  255.255.255.0  10.0.200.123  1
-- |     10.46.124.0  255.255.255.0  10.0.200.124  1
-- |     10.46.125.0  255.255.255.0  10.0.200.125  1
-- |   10.0.200.101
-- |     ip       netmask  nexthop     metric
-- |_    0.0.0.0  0.0.0.0  10.0.200.1  1
--
-- @args broadcast-rip-discover.timeout timespec defining how long to wait for
--       a response. (default 5s)

--
-- Version 0.1
-- Created 29/10/2011 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
--

author = "Patrik Karlsson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"broadcast", "safe"}


prerule = function() return not( nmap.address_family() == "inet6") end

RIPv2 = {

  Command = {
    Request = 1,
    Response = 2,
  },

  AddressFamily = {
    IP = 2,
  },

  -- The Request class contains functions to build a RIPv2 Request
  Request = {

    -- Creates a new Request instance
    --
    -- @param command number containing the RIPv2 Command to use
    -- @return o instance of request
    new = function(self, command)
      local o = {
        version = 2,
        command = command,
        domain = 0,
        family = 0,
        tag =  0,
        address = 0,
        subnet = 0,
        nexthop = 0,
        metric = 16
      }
      setmetatable(o, self)
      self.__index = self
      return o
    end,

    -- Converts the whole request to a string
    __tostring = function(self)
      assert(self.command, "No command was supplied")
      assert(self.metric, "No metric was supplied")
      assert(self.address, "No address was supplied")
      local RESERVED = 0
      -- RIPv2 stuff, should be 0 for RIPv1
      local tag, subnet, nexthop = 0, 0, 0

      local data = string.pack(">BB I2 I2 I2 I4 I4 I4 I4",
        self.command, self.version, self.domain, self.family, self.tag,
        self.address, self.subnet, self.nexthop, self.metric)

      return data
    end,

  },

  -- The Response class contains code needed to parse a RIPv2 response
  Response = {

    -- Creates a new Response instance based on raw socket data
    --
    -- @param data string containing the raw socket response
    -- @return o Response instance
    new = function(self, data)
      local o = { data = data }

      if ( not(data) or #data < 3 ) then
        return
      end
      local pos, _
      o.command, o.version, _, pos = string.unpack(">BBI2", data)
      if ( o.command ~= RIPv2.Command.Response or o.version ~= 2 ) then
        return
      end

      local routes = tab.new(2)
      tab.addrow(routes, "ip", "netmask", "nexthop", "metric")

      while( #data - pos >= 20 ) do
        local family, address, metric, netmask, nexthop
        family, _, address, netmask, nexthop,
          metric, pos = string.unpack(">I2 I2 I4 I4 I4 I4", data, pos)

        if ( family == RIPv2.AddressFamily.IP ) then
          local ip = ipOps.fromdword(address)
          netmask = ipOps.fromdword(netmask)
          nexthop = ipOps.fromdword(nexthop)
          tab.addrow(routes, ip, netmask, nexthop, metric)
        end
      end

      if ( #routes > 1 ) then o.routes = routes end

      setmetatable(o, self)
      self.__index = self
      return o
    end,

  }

}


action = function()
  local timeout = stdnse.parse_timespec(stdnse.get_script_args('broadcast-rip-discover.timeout'))
  timeout = (timeout or 5) * 1000

  local socket = nmap.new_socket("udp")
  socket:set_timeout(timeout)

  local rip = RIPv2.Request:new(RIPv2.Command.Request)
  local status, err = socket:sendto("224.0.0.9",
    { number = 520, protocol = "udp" },
    tostring(rip))
  local result = {}
  repeat
    local data
    status, data = socket:receive()
    if ( status ) then
      local status, _, _, rhost, _ = socket:get_info()
      local response = RIPv2.Response:new(data)
      table.insert(result, rhost)

      if ( response and response.routes and #response.routes > 0 ) then
        --response.routes.name = "Routes"
        table.insert(result, { tab.dump(response.routes) } )
      end

    end
  until( not(status) )

  if ( #result > 0 ) then
    result.name = "Discovered RIPv2 devices"
  end
  return stdnse.format_output(true, result)
end