1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-17 13:09:02 +00:00

o [NSE] Removed DoS code from dhcp-discover and placed it into the discover and

safe categories. Added support for adding options to DHCP requests in the
  dhcp library. [Patrik]
This commit is contained in:
patrik
2011-12-29 08:07:26 +00:00
parent d93f068e3f
commit 9ff471d906
3 changed files with 74 additions and 67 deletions

View File

@@ -1,5 +1,9 @@
# Nmap Changelog ($Id$); -*-text-*-
o [NSE] Removed DoS code from dhcp-discover and placed it into the discover and
safe categories. Added support for adding options to DHCP requests in the
dhcp library. [Patrik]
o [NSE] Added a telnet-encryption script which detects if a remote
telnet server supports the (weak) encryption option. This is
particularly interesting due to a remotely exploitable root

View File

@@ -5,6 +5,14 @@
-- which have a trivial one-function interface, can send out DHCP packets of many
-- types and parse the responses.
--
-- @author "Ron Bowes"
--
-- 2011-12-28 - Revised by Patrik Karlsson <patrik@cqure.net>
-- o Split dhcp_send into dhcp_send, dhcp_receive
-- o Added basic support for adding options to requests
-- o Added possibility to ovverride transaction id
-- o Added WPAD action
module(... or "dhcp", package.seeall)
@@ -354,23 +362,13 @@ actions[61] = {name="Client Identifier (client)", func=read_string,
actions[252]= {name="WPAD", func=read_string, default=false}
--- Does the send/receive, doesn't build/parse anything.
local function dhcp_send(host, packet, transaction_id)
local socket
local status, err, data
socket = nmap.new_socket("udp")
socket:bind(nil, 68)
socket:set_timeout(5000)
if(status == false) then
return false, "Couldn't create socket: " .. err
end
stdnse.print_debug(1, "dhcp: Created UDP socket")
local function dhcp_send(socket, host, packet)
-- Send out the packet
socket:sendto(host, { number=67, protocol="udp" }, packet)
return socket:sendto(host, { number=67, protocol="udp" }, packet)
end
local function dhcp_receive(socket, transaction_id)
-- Read the response
local status, data = socket:receive()
if ( not(status) ) then
socket:close()
@@ -380,14 +378,10 @@ local function dhcp_send(host, packet, transaction_id)
-- This pulls back 4 bytes in the packet that correspond to the transaction id. This should be randomly
-- generated and different for every instance of a script (to prevent collisions)
while status and data:sub(5, 8) ~= transaction_id do
local status, data = socket:receive()
status, data = socket:receive()
end
-- Close our sockets
socket:close()
-- Finally, return the data
return true, data
return status, data
end
--- Builds a DHCP packet
@@ -400,6 +394,11 @@ end
--@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 options [optional] A table of additional request options where each option is a table containing the
-- following fields:
-- * <code>number</code> - The option number
-- * <code>type</code> - The option type ("string" or "ip")
-- * <code>value</code> - The option value
--@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
@@ -414,7 +413,7 @@ end
--
--@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)
function dhcp_build(request_type, ip_address, mac_address, options, request_options, overrides, lease_time, transaction_id)
local packet = ''
-- Set up the default overrides
@@ -438,7 +437,7 @@ function dhcp_build(request_type, ip_address, mac_address, request_options, over
-- Header
packet = packet .. bin.pack(">CCCC", overrides['op'] or 1, overrides['htype'] or 1, overrides['hlen'] or 6, overrides['hops'] or 0) -- BOOTREQUEST, 10mb ethernet, 6 bytes long, 0 hops
packet = packet .. transaction_id -- Transaction ID
packet = packet .. ( overrides['xid'] or transaction_id ) -- Transaction ID =
packet = packet .. bin.pack(">SS", overrides['secs'] or 0, overrides['flags'] or 0x0000) -- Secs, flags
packet = packet .. bin.pack("A", ip_address) -- Client address
packet = packet .. bin.pack("<I", overrides['yiaddr'] or 0) -- yiaddr
@@ -451,6 +450,16 @@ function dhcp_build(request_type, ip_address, mac_address, request_options, over
-- Options
packet = packet .. bin.pack(">CCC", 0x35, 1, request_type) -- Request type
for _, option in ipairs(options or {}) do
packet = packet .. bin.pack(">C", option.number)
if ( "string" == option.type ) then
packet = packet .. bin.pack("p", option.value)
elseif( "ip" == option.type ) then
packet = packet .. bin.pack(">CI", 4, option.value)
end
end
packet = packet .. bin.pack(">CCA", 0x37, #request_options, request_options) -- Request options
packet = packet .. bin.pack(">CCI", 0x33, 4, lease_time or 1) -- Lease time
@@ -586,6 +595,11 @@ end
--@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 options [optional] A table of additional request options where each option is a table containing the
-- following fields:
-- * <code>number</code> - The option number
-- * <code>type</code> - The option type ("string" or "ip")
-- * <code>value</code> - The option value
--@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
@@ -598,22 +612,34 @@ end
--@param lease_time [optional] The lease time used when requestint an IP. Default: 1 second.
--@return status (true or false)
--@return The parsed response, as a table.
function make_request(target, request_type, ip_address, mac_address, request_options, overrides, lease_time)
function make_request(target, request_type, ip_address, mac_address, options, request_options, overrides, lease_time)
-- A unique id that identifies this particular session (and lets us filter out what we don't want to see)
local transaction_id = bin.pack("<I", math.random(0, 0x7FFFFFFF))
local transaction_id = overrides and overrides['xid'] or bin.pack("<I", math.random(0, 0x7FFFFFFF))
-- Generate the packet
local status, packet = dhcp_build(request_type, bin.pack(">I", ipOps.todword(ip_address)), mac_address, request_options, overrides, lease_time, transaction_id)
local status, packet = dhcp_build(request_type, bin.pack(">I", ipOps.todword(ip_address)), mac_address, options, request_options, overrides, lease_time, transaction_id)
if(not(status)) then
stdnse.print_debug(1, "dhcp: Couldn't build packet: " .. packet)
return false, "Couldn't build packet: " .. packet
end
local socket = nmap.new_socket("udp")
socket:bind(nil, 68)
socket:set_timeout(5000)
-- Send the packet and get the response
local status, response = dhcp_send(target, packet, transaction_id)
local status, response = dhcp_send(socket, target, packet)
if(not(status)) then
stdnse.print_debug(1, "dhcp: Couldn't send packet: " .. response)
return false, "Couldn't send/receive packet: " .. response
return false, "Couldn't send packet: " .. response
end
status, response = dhcp_receive(socket, transaction_id)
socket:close()
if ( not(status) ) then
stdnse.print_debug(1, "dhcp: Couldn't receive packet: " .. response)
return false, "Couldn't receive packet: " .. response
end
-- Parse the response

View File

@@ -22,7 +22,7 @@ Some of the more useful fields:
]]
---
-- @args dhcptype The type of DHCP request to make. By default, DHCPDISCOVER is sent, but this
-- @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.
@@ -30,17 +30,13 @@ Some of the more useful fields:
-- 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 fake_requests Set to an integer to make that many fake requests before the real one(s).
-- This could be useful, for example, if you also use <code>randomize_mac</code>
-- and you want to try exhausting all addresses.
--
-- @output
-- Interesting ports on 192.168.1.1:
-- PORT STATE SERVICE
-- 67/udp open dhcps
-- | dhcp-discover:
-- | | IP Offered: 192.168.1.101
-- | | DHCP Message Type: DHCPOFFER
-- | | DHCP Message Type: DHCPACK
-- | | Server Identifier: 192.168.1.1
-- | | IP Address Lease Time: 1 day, 0:00:00
-- | | Subnet Mask: 255.255.255.0
@@ -48,16 +44,24 @@ Some of the more useful fields:
-- |_ |_ Domain Name Server: 208.81.7.10, 208.81.7.14
--
--
-- 2011-12-28 - Revised by Patrik Karlsson <patrik@cqure.net>
-- o Removed DoS code and placed script into discovery and safe categories
--
-- 2011-12-27 - Revised by Patrik Karlsson <patrik@cqure.net>
-- o Changed script to use DHCPINFORM instead of DHCPDISCOVER
--
author = "Ron Bowes"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "intrusive"}
categories = {"discovery", "safe"}
require 'bin'
require 'bit'
require 'dhcp'
require 'ipOps'
require 'shortport'
require 'stdnse'
@@ -73,34 +77,7 @@ end
local function go(host, port)
-- Create fake requests if the user asked to. These are fired and forgotten, we ignore the responses.
if(nmap.registry.args.fake_requests) then
for i=1, tonumber(nmap.registry.args.fake_requests), 1 do
-- Build and send a DHCP request using the specified request type, or DHCPDISCOVER
local request_type = dhcp.request_types[nmap.registry.args.dhcptype or "DHCPDISCOVER"]
if(request_type == nil) then
return false, "Valid request types: " .. stdnse.strjoin(", ", dhcp.request_types_str)
end
-- Generate the MAC address, if it's random (TODO: if I can enumerate interfaces, I should fall back to that instead)
local mac_addr = host.mac_addr_src
if(nmap.registry.args.randomize_mac == 'true' or nmap.registry.args.randomize_mac == '1') then
stdnse.print_debug(2, "dhcp-discover: Generating a random MAC address")
mac_addr = ""
for j=1, 6, 1 do
mac_addr = mac_addr .. string.char(math.random(1, 255))
end
end
local status, result = dhcp.make_request(host.ip, host.interface, request_type, "0.0.0.0", mac_addr)
if(status == false) then
stdnse.print_debug(1, "dhcp-discover: Couldn't send DHCP request: %s", result)
return false, "Couldn't send DHCP request: " .. result
end
end
end
-- Build and send a DHCP request using the specified request type, or DHCPDISCOVER
-- 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
@@ -142,11 +119,11 @@ action = function(host, port)
local status, results = go(host, port)
if(status == false) then
if(not(status)) then
return stdnse.format_output(false, results)
end
if(results == nil) then
if(not(results)) then
return nil
end