diff --git a/CHANGELOG b/CHANGELOG index 78f124293..be2c52f40 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Changed the dhcp-discover script to use the DHCPINFORM request to query + dhcp servers instead of DHCPDISCOVER. Cleaned up some code in the DHCP + library. [Patrik] + o [NSE] Added script dns-blacklist that performs DNSBL checks of given or scanned IP addresses against multiple DNSBL services. [Patrik] diff --git a/nselib/dhcp.lua b/nselib/dhcp.lua index 89a1efd0a..aaafad5db 100644 --- a/nselib/dhcp.lua +++ b/nselib/dhcp.lua @@ -359,44 +359,33 @@ local function dhcp_send(interface, host, packet, transaction_id) local result local results = {} - - local bind_socket = nmap.new_socket("udp") - bind_socket:bind(nil, 68) - bind_socket:set_timeout(5000) - stdnse.print_debug(1, "dhcp: Starting listener") - -- Create the UDP socket (TODO: enable SO_BROADCAST if we need to) - socket = nmap.new_socket() - status, err = socket:connect(host, 67, "udp") + socket = nmap.new_socket("udp") + socket:bind(nil, 68) + socket:set_timeout(5000) + -- status, err = socket:connect(host, 67, "udp") if(status == false) then return false, "Couldn't create socket: " .. err end stdnse.print_debug(1, "dhcp: Created UDP socket") -- Send out the packet - socket:send(packet) + socket:sendto(host, { number=67, protocol="udp" }, packet) -- Read the response - local status, data = bind_socket:receive() + local status, data = socket:receive() + if ( not(status) ) then + return false, data + end + -- 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 = bind_socket:receive() - end - if(status == false) then - stdnse.print_debug(1, "dhcp: Error calling bind_socket:receive(): %s", err) - return false, "Error calling bind_socket:receive(): " .. err + while status and data:sub(5, 8) ~= transaction_id do + local status, data = socket:receive() end - - -- If no data was captured (ie, a timeout), return an error - if(data == nil) then - stdnse.print_debug(1, "dhcp: Error calling pcap_receive(): TIMEOUT") - return false, "TIMEOUT" - end - + -- Close our sockets socket:close() - bind_socket:close() -- Finally, return the data return true, data @@ -625,7 +614,7 @@ function make_request(target, interface, request_type, ip_address, mac_address, local status, response = dhcp_send(interface, target, packet, transaction_id) if(not(status)) then stdnse.print_debug(1, "dhcp: Couldn't send packet: " .. response) - return false, "Couldn't send packet: " .. response + return false, "Couldn't send/receive packet: " .. response end -- Parse the response diff --git a/scripts/dhcp-discover.nse b/scripts/dhcp-discover.nse index 5309887f5..c710ea2f2 100644 --- a/scripts/dhcp-discover.nse +++ b/scripts/dhcp-discover.nse @@ -1,23 +1,18 @@ description = [[ -Sends a DHCPDISCOVER request to a host on UDP port 67. The response -comes back to UDP port 68, and -is read using pcap (due to the inability for a script to choose its source port at the moment). +Sends a DHCPINFORM request to a host on UDP port 67 to obtain all the local configuration parameters +without allocating a new address. -DHCPDISCOVER is a DHCP request that returns useful information from a DHCP server. The request sends -a list of which fields it wants to know (a handful by default, every field if verbosity is turned on), and -the server responds with the fields that were requested. It should be noted that the server doesn't have -to return every field, nor does it have to return them in the same order, or honour the request at -all. A Linksys WRT54g, for example, completely ignores the list of requested fields and returns a few -standard ones. This script displays every field it receives. +DHCPINFORM is a DHCP request that returns useful information from a DHCP server, without allocating an IP +address. The request sends a list of which fields it wants to know (a handful by default, every field if +verbosity is turned on), and the server responds with the fields that were requested. It should be noted +that the server doesn't have to return every field, nor does it have to return them in the same order, +or honour the request at all. A Linksys WRT54g, for example, completely ignores the list of requested +fields and returns a few standard ones. This script displays every field it receives. With script arguments, the type of DHCP request can be changed, which can lead to interesting results. -Additionally, the MAC address can be randomized, which should override the cache on the DHCP server and +Additionally, the MAC address can be randomized, which in should override the cache on the DHCP server and assign a new IP address. Extra requests can also be sent to exhaust the IP address range more quickly. -DHCPINFORM is another type of DHCP request that requests the same information, but doesn't reserve -an address. Unfortunately, because many home routers simply ignore DHCPINFORM requests, we opted -to use DHCPDISCOVER instead. - Some of the more useful fields: * DHCP Server (the address of the server that responded) * Subnet Mask @@ -76,19 +71,7 @@ function portrule(host, port) return shortport.portnumber(67, "udp")(host, port) end --- We will want to run as a prerule any time ---prerule = function() --- return true ---end - local function go(host, port) - -- Set up a fake host for prerule - if(not(host)) then - host = {} - host.mac_addr_src = string.char(0xFF) .. string.char(0xFF) .. string.char(0xFF) .. string.char(0xFF) .. string.char(0xFF) .. string.char(0xFF) - host.ip = "255.255.255.255" - host.interface = "eth0" -- TODO: I'd like to have a better way of doing this - end -- Create fake requests if the user asked to. These are fired and forgotten, we ignore the responses. if(nmap.registry.args.fake_requests) then @@ -122,7 +105,7 @@ local function go(host, port) 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 "DHCPDISCOVER"] + local request_type = dhcp.request_types[nmap.registry.args.dhcptype or "DHCPINFORM"] if(request_type == nil) then return false, "Valid request types: " .. stdnse.strjoin(", ", dhcp.request_types_str) end @@ -136,12 +119,16 @@ local function go(host, port) mac_addr = mac_addr .. string.char(math.random(1, 255)) end end - - -- Receive the result - local status, result = dhcp.make_request(host.ip, host.interface, request_type, "0.0.0.0", mac_addr) - if(status == false) then + + 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, host.interface, request_type, iface.address, mac_addr) + if( not(status) ) then stdnse.print_debug(1, "dhcp-discover: Couldn't send DHCP request: %s", result) - return false, "Couldn't send DHCP request: " .. result + return false, result end table.insert(results, result) @@ -174,7 +161,10 @@ action = function(host, port) for i, result in ipairs(results) do local result_table = {} - table.insert(result_table, string.format("IP Offered: %s", result.yiaddr_str)) + if ( nmap.registry.args.dhcptype and + "DHCPINFORM" ~= nmap.registry.args.dhcptype ) then + table.insert(result_table, string.format("IP Offered: %s", result.yiaddr_str)) + end for _, v in ipairs(result.options) do if(type(v['value']) == 'table') then table.insert(result_table, string.format("%s: %s", v['name'], stdnse.strjoin(", ", v['value'])))