Nmap Development mailing list archives

Segfault with Nmap 4.85BETA8


From: Lionel Cons <lionel.cons () cern ch>
Date: Fri, 24 Apr 2009 10:44:18 +0200

I'm sometimes getting a segfault while running Nmap 4.85BETA8. Here is
a backtrace:

Starting Nmap 4.85BETA8 ( http://nmap.org ) at 2009-04-23 16:39 CEST

Program received signal SIGSEGV, Segmentation fault.
0x080d7dae in lua_pushnil ()
(gdb) bt
#0  0x080d7dae in lua_pushnil ()
#1  0x080b257e in ncap_restore_lua ()
#2  0x080da35b in lua_getinfo ()
#3  0x080e2b37 in lua_close ()
#4  0x080d9c58 in lua_getinfo ()
#5  0x080da850 in lua_resume ()
#6  0x080e85c6 in luaL_openlibs ()
#7  0x080e86a8 in luaL_openlibs ()
#8  0x080da35b in lua_getinfo ()
#9  0x080e2b37 in lua_close ()
#10 0x080da66f in lua_getinfo ()
#11 0x080d85cf in lua_call ()
#12 0x080d9c58 in lua_getinfo ()
#13 0x080da936 in lua_yield ()
#14 0x080d8624 in lua_pcall ()
#15 0x080af9fb in ScriptResult::set_output ()
#16 0x080da35b in lua_getinfo ()
#17 0x080da634 in lua_getinfo ()
#18 0x080d86c8 in lua_pcall ()
#19 0x080d9c58 in lua_getinfo ()
#20 0x080da936 in lua_yield ()
#21 0x080d870d in lua_cpcall ()
#22 0x080af411 in script_scan ()
#23 0x08061ec7 in nmap_main ()
#24 0x0805d518 in main ()

The bug seems to be triggered by an NSE script of mine (see attached).
The script may be buggy but IMHO it should not make Nmap segfault.
Also, this script worked fine in previous versions of Nmap, up to SVN
revision 12857 at least.

Finally, the problem is tricky. I can reproduce it when scanning many
ports on some sets of hosts. Changing the ports or the hosts scanned
sometimes makes the problem disappear, maybe a timing or race
condition problem?

Any help to improve the NSE script and/or make Nmap more robust would
be welcome.

Cheers,

Lionel

--
-- This script sends RMCP Ping requests and, if successful, IPMI Get
-- Channel Auth Capabilities commands in order to detect RMCP and IPMI
-- support. Note that we have to use PCAP to receive the replies to our
-- probes since the RMCP support may be at hardware level (BMC) so the
-- operating system may send us "Port unreachable" ICMP messages.
--
-- In case you wonder:
--  * RMCP = Remote Management Control Protocol
--  * IPMI = Intelligent Platform Management Interface
--  * BMC  = Baseboard Management Controller
--

--
-- script definition
--

id          = "RMCP and IPMI Support"
description = "Sends probes to detect RMCP and IPMI support."
author      = "Lionel Cons (http://cern.ch/lionel.cons)"
license     = "See Nmap's COPYING for license"
categories  = { "safe", "version" }

--
-- used modules
--

require('stdnse')
require('packet')

--
-- we do not use a portrule as the port may appear to be closed
-- (we do however check that the RMCP port has been scanned)
--

hostrule = function(host)
        local result

        -- initialise only once
        if (not nmap.registry.rmcp_port) then
                init()
        end

        -- run only if the RMCP port was scanned, but regardless of its state
        result = nmap.get_port_state(host, nmap.registry.rmcp_port, nmap.registry.rmcp_port.protocol)
        if (not result) then
                stdnse.print_debug("[RMCP-IPMI] RMCP port not scanned")
                return false
        end

        -- so far so good
        stdnse.print_debug("[RMCP-IPMI] RMCP port scanned and found to be %s", result.state)
        return true
end

--
-- store port information and our probes in the registry
--

init = function()
        stdnse.print_debug("[RMCP-IPMI] initialisation")
        -- RMCP port in binary format
        nmap.registry.rmcp_bin_port = string.char(0x02, 0x6f)
        -- RMCP port information
        nmap.registry.rmcp_port = {
                number   = 623,
                protocol = "udp",
                version  = {
                        name       = "rmcp",
                        version    = "6",
                        confidence = 10,
                },
        }
        -- IPMI port information
        nmap.registry.ipmi_port = {
                number   = 623,
                protocol = "udp",
                version  = {
                        name       = "rmcp",
                        version    = "6",
                        extrainfo  = "ipmi",
                        confidence = 10,
                },
        }
        -- RMCP probes
        local tag = math.random(192)
        nmap.registry.rmcp_probes = {
          string.char(0x06, 0x00, 0xff, 0x06, 0x00, 0x00, 0x11, 0xbe,
                      0x80, tag+0, 0x00, 0x00),
          string.char(0x06, 0x00, 0xff, 0x06, 0x00, 0x00, 0x11, 0xbe,
                      0x80, tag+1, 0x00, 0x00),
          string.char(0x06, 0x00, 0xff, 0x06, 0x00, 0x00, 0x11, 0xbe,
                      0x80, tag+2, 0x00, 0x00),
        }
        -- RMCP data
        nmap.registry.rmcp_data_1 =
          string.char(0x06, 0x00, 0xff, 0x06, 0x00, 0x00, 0x11, 0xbe, 0x40)
        nmap.registry.rmcp_data_2 =
          string.char(0x06, 0x00, 0xff, 0x06, 0xbe, 0x11, 0x00, 0x00, 0x40)
        -- IPMI probes
        local seq = (16 + math.random(32)) * 4
        nmap.registry.ipmi_probes = {
          string.char(0x06, 0x00, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00,
                      0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x20, 0x18,
                      0xc8, 0x81, seq+0, 0x38, 0x0e, 0x02, 311 - (seq+0)),
          string.char(0x06, 0x00, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00,
                      0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x20, 0x18,
                      0xc8, 0x81, seq+4, 0x38, 0x0e, 0x02, 311 - (seq+4)),
          string.char(0x06, 0x00, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00,
                      0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x20, 0x18,
                      0xc8, 0x81, seq+8, 0x38, 0x0e, 0x02, 311 - (seq+8)),
        }
        -- IPMI data
        nmap.registry.ipmi_data_1 =
          string.char(0x06, 0x00, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00,
                      0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x81, 0x1c,
                      0x63, 0x20)
        nmap.registry.ipmi_data_2 =
          string.char(0x38, 0x00)
end

--
-- send the probes one by one and stop if we receive a reply
--

function send_probes(host, socket, pcap, probes)
        local status, result, layer2, layer3, i, nprobes, hash

        hash = host.bin_ip .. host.bin_ip_src .. nmap.registry.rmcp_bin_port
        nprobes = table.getn(probes)
        for i = 1, nprobes do
                stdnse.print_debug("[RMCP-IPMI] send probe: %d/%d", i, nprobes)

                -- register the hash
                pcap:pcap_register(hash)

                -- (re)connect the socket
                status, result = socket:connect(host.ip,
                        nmap.registry.rmcp_port.number, nmap.registry.rmcp_port.protocol)
                if (not status) then
                        stdnse.print_debug("[RMCP-IPMI] socket:connect failed: %s", result)
                        return false, result
                end

                -- send one probe
                status, result = socket:send(probes[i])
                if (not status) then
                        stdnse.print_debug("[RMCP-IPMI] socket:send failed: %s", result)
                        return false, result
                end

                -- check for a reply packet
                status, result, layer2, layer3 = pcap:pcap_receive()
                if (status) then
                        -- we found one so we do not send any more probes
                        stdnse.print_debug("[RMCP-IPMI] packet has %d bytes", result)
                        result = string.sub(layer3, 29)
                        stdnse.print_debug("[RMCP-IPMI] packet is %s", packet.bintohex(result))
                        -- check the reply length
                        if (string.len(result) < 12 or string.len(result) > 32) then
                                -- packet is too small or too big
                                return false, result
                        end
                        -- so far so good
                        return true, result
                else
                        stdnse.print_debug("[RMCP-IPMI] pcap:pcap_receive failed: %s", result)
                        if (result == "TIMEOUT" and i < nprobes) then
                                -- try once more
                        else
                                return false, result
                        end
                end
        end

        -- so far so bad but this should not be reached!
        stdnse.print_debug("[RMCP-IPMI] ooops!")
        return false, ""
end

--
-- check for RMCP support
--

function check_rmcp(host, socket, pcap)
        local status, result

        -- send the RMCP probes
        status, result = send_probes(host, socket, pcap, nmap.registry.rmcp_probes)
        if (not status) then
                stdnse.print_debug("[RMCP-IPMI] RMCP probes failed")
                return false
        end

        -- we did receive something from this port so it must be open
        nmap.set_port_state(host, nmap.registry.rmcp_port, "open")

        -- check the reply
        if (string.sub(result, 1, 9) == nmap.registry.rmcp_data_1 or
            string.sub(result, 1, 9) == nmap.registry.rmcp_data_2) then
                -- the reply looks like a "Presence Pong" packet
                -- so the host must support RMCP
                stdnse.print_debug("[RMCP-IPMI] packet looks like a 'Presence Pong' reply")
                nmap.set_port_version(host, nmap.registry.rmcp_port, "hardmatched")
                return true
        end

        -- so far so bad
        return false
end

--
-- check for IPMI support
--

function check_ipmi(host, socket, pcap)
        local status, result

        -- send the IPMI probes
        status, result = send_probes(host, socket, pcap, nmap.registry.ipmi_probes)
        if (not status) then
                stdnse.print_debug("[RMCP-IPMI] IPMI probes failed")
                return false
        end

        -- check the reply
        if (string.sub(result, 1, 18) == nmap.registry.ipmi_data_1
            and string.sub(result, 20, 21) == nmap.registry.ipmi_data_2) then
                -- the reply looks like a "Get Channel Auth Capabilities" response packet
                -- so the host must support IPMI
                stdnse.print_debug("[RMCP-IPMI] packet looks like a 'Get Channel Auth Capabilities' reply")
                nmap.set_port_version(host, nmap.registry.ipmi_port, "hardmatched")
                return true
        end

        -- so far so bad
        return false
end

--
-- callback function for PCAP returning the pair of addresses and source port
-- (this assumes that the IP header does not contain any options)
--

pcap_callback = function(packetsz, layer2, layer3)
        return string.sub(layer3, 13, 22)
end

--
-- action
--

action = function(host)
        local socket, pcap, result

        -- create the needed sockets
        socket = nmap.new_socket()
        pcap = nmap.new_socket()

        -- configure PCAP to only see reply packets matching our probes
        result = string.format("%s and src port %d",
                nmap.registry.rmcp_port.protocol, nmap.registry.rmcp_port.number)
        pcap:pcap_open(host.interface, 96, 0, pcap_callback, result)

        -- set the PCAP reception timeout to one second
        pcap:set_timeout(1000)

        -- check for RMCP support
        result = check_rmcp(host, socket, pcap)

        -- then check for IPMI support if we have detected RMCP support
        if (result) then
                result = check_ipmi(host, socket, pcap)
        end

        -- cleanup and return nothing (no script output)
        socket:close()
        pcap:close()
        return
end

_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org

Current thread: