luanmap

Server sockets using the Nmap Scripting Engine (NSE)


I am writing a script using the Nmap Scripting Engine (NSE) to extract the product name and the article number through a Lookup Request using DCE/RPC Endpoint Mapper. However, while testing the script, I encountered something unusual. Here is a snippet from my experiments:

Wireshark

First, the machine scanning the network (IP: 192.168.10.56) sends a request with a random source port 51336 to the UDP destination port 34964. The first scanned Siemens device (IP: 192.168.10.12) responds as expected, using port 34964 as source port for the reply. However, the second scanned device (IP: 192.168.10.21) behaves differently. It also replies to the request but uses a random source port for the reply, in this case, 49344. NMAP receives the response from IP 192.168.10.12 but not from IP 192.168.10.21 due to this unexpected new source port. Is there a way to open a socket using NSE to accept all incoming messages on a specific port? Something like a "server port" accepting all incoming requests? Right now I am using the funtion socket:connect(ip, 34964, "udp") specifing the port on the scanned hosts causing this problem. Here a snippet from the script I am using:

send_udp_payload = function(ip, timeout, payload)
    local socket, try, catch

    -- create a new udp socket
    local socket = nmap.new_socket("udp")

    -- set timeout
    socket:set_timeout(tonumber(timeout))

    catch = function()
        socket:close()
    end

    -- create new try
    try = nmap.new_try(catch)

    -- connect to port on host
    try(socket:connect(ip, 34964, "udp"))

    -- send lookup packet with PNIO Interface UUID
    try(socket:send(payload))

    -- receive response
    local rcvstatus, response = socket:receive()

    -- close socket
    socket:close()

    if rcvstatus then
        return response
    else
        return nil
    end
end


Solution

  • Using pcap:receive() could solve my problem:

    lookup_request = function(host, port, src_port_number, payload, timeout)
      local socket, try, catch
      local socket = nmap.new_socket("udp")
      local pcap = nmap.new_socket()
      socket:set_timeout(tonumber(timeout))
      catch = function()
        socket:close()
      end
      try = nmap.new_try(catch)
      socket:bind(nil, src_port_number)
      try(socket:connect(host.ip, port["number"], "udp"))
      pcap:pcap_open(host.interface, 1500, false, "udp dst port " .. src_port_number .. " and src host " .. host.ip)
      pcap:set_timeout(host.times.timeout * 1000)
      try(socket:send(payload))
      local status, len, _, layer3 = pcap:pcap_receive()
      pcap:close()
      socket:close()
    
      -- parse response
      -- ...
    end