mirror of
https://github.com/nmap/nmap.git
synced 2025-12-10 09:49:05 +00:00
o [NSE] Added the script broadcast-dhcp-discover that sends a DHCP discover
message to the broadcast address and collects and reports the network information received from the DHCP server. [Patrik]
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o [NSE] Added the script broadcast-dhcp-discover that sends a DHCP discover
|
||||
message to the broadcast address and collects and reports the network
|
||||
information received from the DHCP server. [Patrik]
|
||||
|
||||
o [NSE] Added the script smtp-brute that performs brute force password
|
||||
auditing against SMTP servers. [Patrik]
|
||||
|
||||
|
||||
@@ -402,7 +402,31 @@ local function dhcp_send(interface, host, packet, transaction_id)
|
||||
return true, data
|
||||
end
|
||||
|
||||
local function dhcp_build(request_type, ip_address, mac_address, request_options, overrides, lease_time, transaction_id)
|
||||
--- Builds a DHCP packet
|
||||
--
|
||||
--@param request_type The type of request as an integer (use the <code>request_types</code> table at the
|
||||
-- top of this file).
|
||||
--@param ip_address Your ip address (as a dotted-decimal string). This tells the DHCP server where to
|
||||
-- send the response. Setting it to "255.255.255.255" or "0.0.0.0" is generally acceptable (if not,
|
||||
-- host.ip_src can work).
|
||||
--@param mac_address Your mac address (as a string up to 16 bytes) where the server will send the response. Like
|
||||
-- <code>ip_address</code>, setting to the broadcast address (FF:FF:FF:FF:FF:FF) is
|
||||
-- common (host.mac_addr_src works).
|
||||
--@param request_options [optional] The options to request from the server, as an array of integers. For the
|
||||
-- acceptable options, see the <code>actions</code> table above or have a look at rfc2132.
|
||||
-- Some DHCP servers (such as my Linksys WRT54g) will ignore this list and send whichever
|
||||
-- information it wants. Default: all options marked as 'default' in the <code>actions</code>
|
||||
-- table above are requested (the typical interesting ones) if no verbosity is given.
|
||||
-- If any level of verbosity is on, get all types.
|
||||
--@param overrides [optional] A table of overrides. If a field in the table matches a field in the DHCP
|
||||
-- packet (see rfc2131 section 2 for a list of possible fields), the value in the table
|
||||
-- will be sent instead of the default value.
|
||||
--@param lease_time [optional] The lease time used when requestint an IP. Default: 1 second.
|
||||
--@param transaction_id The identity of the transaction.
|
||||
--
|
||||
--@return status (true or false)
|
||||
--@return The parsed response, as a table.
|
||||
function dhcp_build(request_type, ip_address, mac_address, request_options, overrides, lease_time, transaction_id)
|
||||
local packet = ''
|
||||
|
||||
-- Set up the default overrides
|
||||
@@ -455,7 +479,7 @@ end
|
||||
--
|
||||
--@param data The DHCP packet data. Any padding at the end of the packet will be ignored (by default,
|
||||
-- DHCP packets are padded with \x00 bytes).
|
||||
local function dhcp_parse(data, transaction_id)
|
||||
function dhcp_parse(data, transaction_id)
|
||||
local pos = 1
|
||||
local result = {}
|
||||
|
||||
|
||||
197
scripts/broadcast-dhcp-discover.nse
Normal file
197
scripts/broadcast-dhcp-discover.nse
Normal file
@@ -0,0 +1,197 @@
|
||||
description = [[
|
||||
Sends a DHCP request to the broadcast address (255.255.255.255) and reports
|
||||
the results. The script uses a static MAC address (DE:AD:CO:DE:CA:FE) while
|
||||
doing so in order to prevent scope exhaustion.
|
||||
|
||||
The script reads the response using pcap by opening a listening pcap socket
|
||||
on all available ethernet interfaces that are reported up. If no response
|
||||
has been received before the timeout has been reached (default 10 seconds)
|
||||
the script will abort execution.
|
||||
|
||||
The script needs to be run as a privileged user, typically root.
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- sudo nmap --script broadcast-dhcp-discover
|
||||
--
|
||||
-- @output
|
||||
-- | broadcast-dhcp-discover:
|
||||
-- | IP Offered: 192.168.1.114
|
||||
-- | DHCP Message Type: DHCPOFFER
|
||||
-- | Server Identifier: 192.168.1.1
|
||||
-- | IP Address Lease Time: 1 day, 0:00:00
|
||||
-- | Subnet Mask: 255.255.255.0
|
||||
-- | Router: 192.168.1.1
|
||||
-- | Domain Name Server: 192.168.1.1
|
||||
-- |_ Domain Name: localdomain
|
||||
--
|
||||
-- @args broadcast-dhcp-discover.timeout time in seconds to wait for a response
|
||||
-- (default: 10s)
|
||||
--
|
||||
|
||||
-- Version 0.1
|
||||
-- Created 07/14/2011 - v0.1 - created by Patrik Karlsson
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"broadcast", "safe"}
|
||||
|
||||
prerule = function() return not( nmap.address_family() == "inet6") end
|
||||
|
||||
require 'dhcp'
|
||||
require 'ipOps'
|
||||
require 'packet'
|
||||
|
||||
-- Creates a random MAC address
|
||||
--
|
||||
-- @return mac_addr string containing a random MAC
|
||||
local function randomizeMAC()
|
||||
mac_addr = ""
|
||||
for j=1, 6 do
|
||||
mac_addr = mac_addr .. string.char(math.random(1, 255))
|
||||
end
|
||||
return mac_addr
|
||||
end
|
||||
|
||||
-- Gets a list of available interfaces based on link and up filters
|
||||
--
|
||||
-- @param link string containing the link type to filter
|
||||
-- @param up string containing the interface status to filter
|
||||
-- @return result table containing the matching interfaces
|
||||
local function getInterfaces(link, up)
|
||||
if( not(nmap.list_interfaces) ) then return end
|
||||
local interfaces, err = nmap.list_interfaces()
|
||||
local result
|
||||
if ( not(err) ) then
|
||||
for _, iface in ipairs(interfaces) do
|
||||
if ( iface.link == link and iface.up == up ) then
|
||||
result = result or {}
|
||||
result[iface.device] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Listens for an incoming dhcp response
|
||||
--
|
||||
-- @param iface string with the name of the interface to listen to
|
||||
-- @param timeout number of ms to wait for a response
|
||||
-- @param xid the DHCP transaction id
|
||||
-- @param result a table to which the result is written
|
||||
local function dhcp_listener(iface, timeout, xid, result)
|
||||
local sock = nmap.new_socket()
|
||||
local condvar = nmap.condvar(result)
|
||||
|
||||
sock:set_timeout(100)
|
||||
sock:pcap_open(iface, 1500, false, "ip && udp && port 68")
|
||||
|
||||
local start_time = nmap.clock_ms()
|
||||
while( nmap.clock_ms() - start_time < timeout ) do
|
||||
local status, _, _, data = sock:pcap_receive()
|
||||
-- abort, once another thread has picked up our response
|
||||
if ( #result > 0 ) then
|
||||
sock:close()
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
local p = packet.Packet:new( data, #data )
|
||||
if ( p and p.udp_dport ) then
|
||||
local data = data:sub(p.udp_offset + 9)
|
||||
local status, response = dhcp.dhcp_parse(data, xid)
|
||||
if ( status ) then
|
||||
table.insert( result, response )
|
||||
sock:close()
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
sock:close()
|
||||
condvar "signal"
|
||||
end
|
||||
|
||||
|
||||
action = function()
|
||||
|
||||
if not nmap.is_privileged() then
|
||||
return ("\n ERROR: %s needs to be run as a privileged user (root)."):format(SCRIPT_NAME)
|
||||
end
|
||||
|
||||
local host, port = "255.255.255.255", 67
|
||||
local timeout = stdnse.get_script_args("broadcast-dhcp-discover.timeout")
|
||||
timeout = tonumber(timeout) or 10
|
||||
|
||||
-- convert from seconds to ms
|
||||
timeout = timeout * 1000
|
||||
|
||||
-- randomizing the MAC could exhaust dhcp servers with small scopes
|
||||
-- if ran multiple times, so we should probably refrain from doing
|
||||
-- this?
|
||||
local mac = string.char(0xDE,0xAD,0xC0,0xDE,0xCA,0xFE)--randomizeMAC()
|
||||
|
||||
local interfaces
|
||||
|
||||
-- first check if the user supplied an interface
|
||||
if ( nmap.get_interface() ) then
|
||||
interfaces = { [nmap.get_interface()] = true }
|
||||
else
|
||||
-- As the response will be sent to the "offered" ip address we need
|
||||
-- to use pcap to pick it up. However, we don't know what interface
|
||||
-- our packet went out on, so lets get a list of all interfaces and
|
||||
-- run pcap on all of them, if they're a) up and b) ethernet.
|
||||
interfaces = getInterfaces("ethernet", "up")
|
||||
end
|
||||
|
||||
if( not(interfaces) ) then return "\n ERROR: Failed to retrieve interfaces (try setting one explicitly using -e)" end
|
||||
|
||||
local transaction_id = bin.pack("<I", math.random(0, 0x7FFFFFFF))
|
||||
local request_type = dhcp.request_types["DHCPDISCOVER"]
|
||||
local ip_address = bin.pack(">I", ipOps.todword("0.0.0.0"))
|
||||
|
||||
-- we nead to set the flags to broadcast
|
||||
local request_options, overrides, lease_time = nil, { flags = 0x8000 }, nil
|
||||
local status, packet = dhcp.dhcp_build(request_type, ip_address, mac, request_options, overrides, lease_time, transaction_id)
|
||||
if (not(status)) then return "\n ERROR: Failed to build packet" end
|
||||
|
||||
local socket = nmap.new_socket("udp")
|
||||
socket:bind(nil, 68)
|
||||
socket:sendto( host, port, packet )
|
||||
socket:close()
|
||||
|
||||
local threads = {}
|
||||
local result = {}
|
||||
local condvar = nmap.condvar(result)
|
||||
|
||||
-- start a listening thread for each interface
|
||||
for iface, _ in pairs(interfaces) do
|
||||
local co = stdnse.new_thread( dhcp_listener, iface, timeout, transaction_id, result )
|
||||
threads[co] = true
|
||||
end
|
||||
|
||||
-- wait until all threads are done
|
||||
repeat
|
||||
condvar "wait"
|
||||
for thread in pairs(threads) do
|
||||
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
||||
end
|
||||
until next(threads) == nil
|
||||
|
||||
local response = {}
|
||||
-- Display the results
|
||||
for i, r in ipairs(result) do
|
||||
table.insert(response, string.format("IP Offered: %s", r.yiaddr_str))
|
||||
for _, v in ipairs(r.options) do
|
||||
if(type(v['value']) == 'table') then
|
||||
table.insert(response, string.format("%s: %s", v['name'], stdnse.strjoin(", ", v['value'])))
|
||||
else
|
||||
table.insert(response, string.format("%s: %s\n", v['name'], v['value']))
|
||||
end
|
||||
end
|
||||
end
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
@@ -11,6 +11,7 @@ Entry { filename = "backorifice-info.nse", categories = { "default", "discovery"
|
||||
Entry { filename = "banner.nse", categories = { "discovery", "safe", } }
|
||||
Entry { filename = "broadcast-avahi-dos.nse", categories = { "broadcast", "dos", "intrusive", "vuln", } }
|
||||
Entry { filename = "broadcast-db2-discover.nse", categories = { "broadcast", "safe", } }
|
||||
Entry { filename = "broadcast-dhcp-discover.nse", categories = { "broadcast", "safe", } }
|
||||
Entry { filename = "broadcast-dns-service-discovery.nse", categories = { "broadcast", "safe", } }
|
||||
Entry { filename = "broadcast-dropbox-listener.nse", categories = { "broadcast", "safe", } }
|
||||
Entry { filename = "broadcast-ms-sql-discover.nse", categories = { "broadcast", "safe", } }
|
||||
|
||||
Reference in New Issue
Block a user