diff --git a/CHANGELOG b/CHANGELOG
index 9429786d0..84ac822f9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -36,6 +36,9 @@ o [Windows] Add support for the new loopback behavior in Npcap 0.9983. This
Adapter to be installed, which was a source of problems for some users.
[Daniel Miller]
+o [NSE][GH#1838] Scripts dhcp-discover and broadcast-dhcp-discover now support
+ new argument "mac" to force a specific client MAC address [nnposter]
+
o [NSE] Code improvements in RPC Dump, benefitting NFS-related scripts
[nnposter]
diff --git a/scripts/broadcast-dhcp-discover.nse b/scripts/broadcast-dhcp-discover.nse
index bb36e9621..977f27cfa 100644
--- a/scripts/broadcast-dhcp-discover.nse
+++ b/scripts/broadcast-dhcp-discover.nse
@@ -49,11 +49,18 @@ The script needs to be run as a privileged user, typically root.
-- 192.168.1.1
-- localdomain
--
+-- @args broadcast-dhcp-discover.mac Set to random or a specific
+-- client MAC address in the DHCP request. "DE:AD:C0:DE:CA:FE"
+-- is used by default. Setting it to random will
+-- possibly cause the DHCP server to reserve a new IP address
+-- each time.
-- @args broadcast-dhcp-discover.timeout time in seconds to wait for a response
-- (default: 10s)
--
--- Version 0.1
+-- Created 01/14/2020 - v0.2 - updated by nnposter
+-- o Implemented script argument "mac" to force a specific MAC address
+--
-- Created 07/14/2011 - v0.1 - created by Patrik Karlsson
author = "Patrik Karlsson"
@@ -148,10 +155,16 @@ action = function()
local timeout = stdnse.parse_timespec(stdnse.get_script_args("broadcast-dhcp-discover.timeout"))
timeout = (timeout or 10) * 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 = "\xDE\xAD\xC0\xDE\xCA\xFE"
+ local macaddr = (stdnse.get_script_args(SCRIPT_NAME .. ".mac") or "DE:AD:C0:DE:CA:FE"):lower()
+ if macaddr:find("^ra?nd") then
+ macaddr = rand.random_string(6)
+ else
+ macaddr = macaddr:gsub(":", "")
+ if not (#macaddr == 12 and macaddr:find("^%x+$")) then
+ return stdnse.format_output(false, "Invalid MAC address")
+ end
+ macaddr = stdnse.fromhex(macaddr)
+ end
local interfaces
@@ -174,7 +187,7 @@ action = function()
-- we need 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, nil, request_options, overrides, lease_time, transaction_id)
+ local status, packet = dhcp.dhcp_build(request_type, ip_address, macaddr, nil, request_options, overrides, lease_time, transaction_id)
if (not(status)) then return fail("Failed to build packet") end
local threads = {}
diff --git a/scripts/dhcp-discover.nse b/scripts/dhcp-discover.nse
index ac825d519..65e6cff22 100644
--- a/scripts/dhcp-discover.nse
+++ b/scripts/dhcp-discover.nse
@@ -5,6 +5,7 @@ local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
+local ipOps = require "ipOps"
description = [[
Sends a DHCPINFORM request to a host on UDP port 67 to obtain all the local configuration parameters
@@ -33,14 +34,18 @@ Some of the more useful fields:
-- @see broadcast-dhcp6-discover.nse
-- @see broadcast-dhcp-discover.nse
--
--- @args dhcptype The type of DHCP request to make. By default, DHCPINFORM is sent, but this
--- argument can change it to DHCPOFFER, DHCPREQUEST, DHCPDECLINE, DHCPACK, DHCPNAK,
--- DHCPRELEASE or DHCPINFORM. Not all types will evoke a response from all servers,
--- and many require different fields to contain specific values.
--- @args randomize_mac Set to true or 1 to send a random MAC address with
--- the request (keep in mind that you may not see the response). This should
--- cause the router to reserve a new IP address each time.
--- @args requests Set to an integer to make up to that many requests (and display the results).
+-- @args dhcp-discover.dhcptype The type of DHCP request to make. By default,
+-- DHCPINFORM is sent, but this argument can change it to DHCPOFFER,
+-- DHCPREQUEST, DHCPDECLINE, DHCPACK, DHCPNAK, DHCPRELEASE or
+-- DHCPINFORM. Not all types will evoke a response from all servers,
+-- and many require different fields to contain specific values.
+-- @args dhcp-discover.mac Set to native (default) or
+-- random or a specific client MAC address in the DHCP
+-- request. Keep in mind that you may not see the response if
+-- a non-native address is used. Setting it to random will
+-- possibly cause the DHCP server to reserve a new IP address each time.
+-- @args dhcp-discover.requests Set to an integer to make up to that many
+-- requests (and display the results).
--
-- @usage
-- nmap -sU -p 67 --script=dhcp-discover
@@ -68,6 +73,10 @@ Some of the more useful fields:
--
--
+--
+-- 2020-01-14 - Revised by nnposter
+-- o Added script argument "mac" to prescribe a specific MAC address
+-- o Deprecated argument "randomize_mac" in favor of "mac=random"
--
-- 2011-12-28 - Revised by Patrik Karlsson
-- o Removed DoS code and placed script into discovery and safe categories
@@ -94,43 +103,6 @@ function portrule(host, port)
return shortport.portnumber(67, "udp")(host, port)
end
-local function go(host, port)
-
- -- Build and send a DHCP request using the specified request type, or DHCPINFORM
- local requests = tonumber(nmap.registry.args.requests or 1)
- local results = {}
- for i = 1, requests, 1 do
- -- Decide which type of request to make
- local request_type = dhcp.request_types[nmap.registry.args.dhcptype or "DHCPINFORM"]
- if(request_type == nil) then
- return false, "Valid request types: " .. table.concat(dhcp.request_types_str, ", ")
- end
-
- -- Generate the MAC address, if it's random
- local mac_addr = host.mac_addr_src
- if(nmap.registry.args.randomize_mac == 'true' or nmap.registry.args.randomize_mac == '1') then
- stdnse.debug2("Generating a random MAC address")
- mac_addr = rand.random_string(6)
- end
-
- local iface, err = nmap.get_interface_info(host.interface)
- if ( not(iface) or not(iface.address) ) then
- return false, "Couldn't determine local ip for interface: " .. host.interface
- end
-
- local status, result = dhcp.make_request(host.ip, request_type, iface.address, mac_addr)
- if( not(status) ) then
- stdnse.debug1("Couldn't send DHCP request: %s", result)
- return false, result
- end
-
- table.insert(results, result)
- end
-
- -- Done!
- return true, results
-end
-
local commasep = {
__tostring = function (t)
return table.concat(t, ", ")
@@ -138,21 +110,69 @@ local commasep = {
}
action = function(host, port)
- local status, results = go(host, port)
-
-
- if(not(status)) then
- return stdnse.format_output(false, results)
+ local dhcptype = (stdnse.get_script_args(SCRIPT_NAME .. ".dhcptype") or "DHCPINFORM"):upper()
+ local dhcptypeid = dhcp.request_types[dhcptype]
+ if not dhcptypeid then
+ return stdnse.format_output(false, "Invalid request type (use "
+ .. table.concat(dhcp.request_types_str, " / ")
+ .. ")")
end
- if(not(results)) then
+ local reqcount = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".requests") or 1)
+ if not reqcount then
+ return stdnse.format_output(false, "Invalid request count")
+ end
+
+ local iface, err = nmap.get_interface_info(host.interface)
+ if not (iface and iface.address) then
+ return stdnse.format_output(false, "Couldn't determine local IP for interface: " .. host.interface)
+ end
+
+ local overrides = {}
+
+ local macaddr = (stdnse.get_script_args(SCRIPT_NAME .. ".mac") or "native"):lower()
+ -- Support for legacy argument "randomize_mac"
+ local randomize = (stdnse.get_script_args(SCRIPT_NAME .. ".randomize_mac") or "false"):lower()
+ if randomize == "true" or randomize == "1" then
+ stdnse.debug1("Use %s.mac=random instead of %s.randomize_mac=%s", SCRIPT_NAME, SCRIPT_NAME, randomize)
+ macaddr = "random"
+ end
+ if macaddr ~= "native" then
+ -- Set the scanner as a relay agent
+ overrides.giaddr = string.unpack("