[SCRIPT] DB2 Information gathering script

View: New views
10 Messages — Rating Filter:   Alert me  

[SCRIPT] DB2 Information gathering script

by Tom Sellers :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I have written a NSE script that enhances version detection for DB2.  It also
gathers platform (OS) and database instance information.  It functions in a similar
way to the MS SQL script.

The script sends a DB2 EXCSAT (exchange server attributes) command packet and
parses the response.  This is a legitimate DB2 command and, based on my reviews
of logs, causes no problems on the server.  DB2 does log the connection and the
source IP address though.


PORT      STATE SERVICE VERSION
523/tcp   open  ibm-db2 IBM DB2 Database Server 9.07.0
50000/tcp open  ibm-db2 IBM DB2 Database Server 9.07.0 (QDB2/LINUX)
|  db2-info: DB2 Version: 9.07.0
|  Server Platform: QDB2/LINUX
|  Instance Name:   db2inst1
|_ External Name:   db2inst1db2agent000051B3%FED%Y00

In the sample output above the version of the DB2 DAS service on port 523 was
detected using nmap-service-probes.  Historically, unlike the DAS port on 523,
we have been unable to detect the exact version number on the DB2 database
instances themselves.  There may be multiple DB2 database instances and they
typically cluster around port 50000 and 60000.

Port 50000 would normally be detected as service "ibm-db2" with a version string
"IBM DB2 Database Server".  The attached NSE script can now detect the exact
version number and platform as well as the instance name for each of the
databases.

Any testing or feedback with the functionality and structure of the script would
be greatly appreciated!


Here are my current concerns with the script:
1.  Is the default output too verbose?  Should I limit the output to the info
     on the port line by default and add the other lines with -v?

2.  The data from the server is encoded in EBCDIC.  I am decoding this will what
     amounts to a lookup table.  Is there a more appropriate/efficient way to
     handle this?

3.  I have built the EBCDIC table containing the ASCII chars that we should
     encounter in this context.  Should I go ahead and build out the full ASCII
     table? (And wow, I am glad we don't use EBCDIC for much.)

Thanks much,

Tom









description = [[
Attempts to extract information from IBM DB2 Server instances.  The script sends a
DB2 EXCSAT (exchange server attributes) command packet and parses the response.
]]

-- rev 1.0 (2010-11-08)
 
author = "Thomas Sellers <nmap@...>"

license = "Same as Nmap--See http://nmap.org/book/man-legal.html"

categories = {"safe", "discovery", "intrusive"}

require "stdnse"
require "shortport"

portrule = shortport.port_or_service({50000,60000},"ibm-db2", "tcp", {"open", "open|filtered"})

-- This function processes a section of the EXCSAT response packet

--@param response This is the data returned from the server as a result of the client query.
--@param position This is the position within the response that this function will start processing from.
--@param ebcdic2ascii This is a table containing a conversion chart for returning the ASCII value of EBCDIC encoded HEX value.
--@return section_length This is the length of the currect section. Will be used to move position for processing next section.
--@return data_string This string contains the data pulled from this section of the server response.
local function process_block(response, position, ebcdic2ascii)

        -- This fuction assumes that the current position is the start of a section within
        -- the DB2 EXCSAT response packet
               
        -- Get the length of this section of the response packet
        local section_length = string.format("%d",string.byte(response,position +1)) .. string.format("%d",string.byte(response,position + 2))
        position = position + 2
               
        -- locate the data string and convert it from EBCDIC to ASCII
        local i = 0
        local data_string = ""
        for i = (position + 3),(position + section_length -2 ),1 do
                -- stdnse.print_debug("%s","INFO: Current postion (i) = " .. i)
                -- stdnse.print_debug("%s","INFO: Hex value = " .. string.format("%x",string.byte(response,i)))
                -- stdnse.print_debug("%s","INFO: Current data_string = " .. data_string)
                if string.format("%x",string.byte(response,i)) == "0" then
                        break
                end
                data_string  = data_string .. ebcdic2ascii[string.format("%x",string.byte(response,i))]
        end
       
        return section_length, data_string
       
end -- fuction process_block



action = function(host, port)
       
        local ebcdic2ascii = {
                -- The following reference was used for this table:  http://www.simotime.com/asc2ebc1.htm
                ["00"] = string.format("%c", 00),
                ["40"] = " ",
                ["81"] = "a",
                ["82"] = "b",
                ["83"] = "c",
                ["84"] = "d",
                ["85"] = "e",
                ["86"] = "f",
                ["87"] = "g",
                ["88"] = "h",
                ["89"] = "i",
                ["91"] = "j",
                ["92"] = "k",
                ["93"] = "l",
                ["94"] = "m",
                ["95"] = "n",
                ["96"] = "o",
                ["97"] = "p",
                ["98"] = "q",
                ["99"] = "r",
                ["a2"] = "s",
                ["a3"] = "t",
                ["a4"] = "u",
                ["a5"] = "v",
                ["a6"] = "w",
                ["a7"] = "x",
                ["a8"] = "y",
                ["a9"] = "z",
                ["c1"] = "A",
                ["c2"] = "B",
                ["c3"] = "C",
                ["c4"] = "D",
                ["c5"] = "E",
                ["c6"] = "F",
                ["c7"] = "G",
                ["c8"] = "H",
                ["c9"] = "I",
                ["d1"] = "J",
                ["d2"] = "K",
                ["d3"] = "L",
                ["d4"] = "M",
                ["d5"] = "N",
                ["d6"] = "O",
                ["d7"] = "P",
                ["d8"] = "Q",
                ["d9"] = "R",
                ["e2"] = "S",
                ["e3"] = "T",
                ["e4"] = "U",
                ["e5"] = "V",
                ["e6"] = "W",
                ["e7"] = "X",
                ["e8"] = "Y",
                ["e9"] = "Z",
                ["f0"] = 0,
                ["f1"] = 1,
                ["f2"] = 2,
                ["f3"] = 3,
                ["f4"] = 4,
                ["f5"] = 5,
                ["f6"] = 6,
                ["f7"] = 7,
                ["f8"] = 8,
                ["f9"] = 9,
                ["4b"] = ".",
                ["4c"] = "<",
                ["4d"] = "(",
                ["4e"] = "+",
                ["4f"] = "|",
                ["5a"] = "!",
                ["5b"] = "$",
                ["5c"] = "*",
                ["5d"] = ")",
                ["5e"] = ";",
                ["60"] = "-",
                ["61"] = "/",
                ["6b"] = ",",
                ["6c"] = "%",
                ["6d"] = "_",
                ["6e"] = ">",
                ["6f"] = "?",
                ["79"] = "`",
                ["7a"] = ":",
                ["7b"] = "#",
                ["7c"] = "@",
                ["7d"] = "'",
                ["7e"] = "=",
                ["7f"] = "\"",
                ["a1"] = "~",
                ["ba"] = "[",
                ["bb"] = "]",
                ["c0"] = "{",
                ["d0"] = "}",
                ["e0"] = "\\"  -- escape the \ character
        }

        -- create the socket used for our connection
        local socket = nmap.new_socket()
       
        -- set a reasonable timeout value
        socket:set_timeout(10000)
       
        -- do some exception handling / cleanup
        local catch = function()
                stdnse.print_debug("%s", "ERROR communicating with " .. host.ip .. " on port " .. port.number)
                socket:close()
        end
       
        local try = nmap.new_try(catch)

        try(socket:connect(host.ip, port.number, "tcp"))

        -- Build DB2 EXCSAT (exchange server attributes) command packet
       
        local query = string.char(0x00, 0x98, 0xd0, 0x41, 0x00, 0x01, 0x00, 0x92, 0x10, 0x41)  -- Header
       
        --  NOTE:  The server's response packet is in the same format at the client query packet being built
        --         in the section below.
       
        --  External Name section: first two bytes (00,48) are section length in HEX, next bytes (11,5e) are section identifier for External Name
        --  In this packet the external name is 'db2jcc_application  JCC03570300' encoded in EBCDIC
        query = query .. string.char(0x00, 0x48, 0x11, 0x5e, 0x84, 0x82, 0xf2, 0x91, 0x83, 0x83, 0x6d, 0x81, 0x97, 0x97, 0x93, 0x89)
        query = query .. string.char(0x83, 0x81, 0xa3, 0x89, 0x96, 0x95, 0x40, 0x40, 0xd1, 0xc3, 0xc3, 0xf0, 0xf3, 0xf5, 0xf7, 0xf0)
        query = query .. string.char(0xf3, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
        query = query .. string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
        query = query .. string.char(0x00, 0x00, 0x00, 0x60, 0xf0, 0xf0, 0xf0, 0xf1)
       
         -- Client Name section: first two bytes (00,16) are section length in HEX, next two bytes (11,6d) are section identifier for Server Name
         -- In the request packet Server Name = client name.  The value here is all spaces, encoded in EBCDIC
        query = query .. string.char(0x00, 0x16, 0x11, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40)
        query = query .. string.char(0x40, 0x40, 0x40, 0x40, 0x40, 0x40)
       
        -- Product Release Level section:  This section is not as important in the client query as it is in the server response.
        -- The first two bytes (00,0c) are section length in HEX, next two bytes (11,5a) are section identifier for Product Release Level
        -- The value here is 'JCC03570' encoded in EBCDIC
        query = query .. string.char(0x00, 0x0c, 0x11, 0x5a, 0xd1, 0xc3, 0xc3, 0xf0, 0xf3, 0xf5, 0xf7, 0xf0)  
               
        -- Manager level section: first two bytes (00,18) are section length in HEX, next two bytes (14,04) are section identifier for Manager-Level List
        query = query .. string.char(0x00, 0x18, 0x14, 0x04, 0x14, 0x03, 0x00, 0x07, 0x24, 0x07, 0x00, 0x0a, 0x24, 0x0f, 0x00, 0x08)
        query = query .. string.char(0x14, 0x40, 0x00, 0x09, 0x14, 0x74, 0x00, 0x08)
       
        -- Server Class section: first two bytes (00,0c) are section length in HEX, next two bytes (11,47) are section identifier for Server Class Name
        -- This section is essentially platform software information.  The value here is 'QDB2/JBM' encoded in EBCDIC)
        query = query .. string.char(0x00, 0x0c, 0x11, 0x47, 0xd8, 0xc4, 0xc2, 0xf2, 0x61, 0xd1, 0xe5, 0xd4)
       
         -- Access Security section
        query = query .. string.char(0x00, 0x26, 0xd0, 0x01, 0x00, 0x02, 0x00, 0x20, 0x10, 0x6d, 0x00, 0x06, 0x11, 0xa2, 0x00, 0x03)
       

        -- Database name section:  This is the client's query for a specific database.  A DB2 default database name, 'db2insta1', was chosen.
        -- It is encoded below in EBCDIC.  The first two bytes (00,16) are section length in HEX, next two bytes (21,10) are section identifier
        -- for Relational Database Name
        query = query .. string.char(0x00, 0x16, 0x21, 0x10, 0x84, 0x82, 0xf2, 0x89, 0x95, 0xa2, 0xa3, 0xf1, 0x40, 0x40, 0x40, 0x40)
        query = query .. string.char(0x40, 0x40, 0x40, 0x40, 0x40, 0x40)
               
        try(socket:send(query))
       
        local status
        local response
       
        -- read in any response we might get
        status, response = socket:receive()

        socket:close()
       
        if (not status) or (response == "TIMEOUT") or (response == nil) then
                stdnse.print_debug("%s","ERROR: No data, ending communications with " .. host.ip .. ":" .. port.number)
                return
        end
       
        local position = 0
       
        -- Check to see if the data is actually a DB2 DDM EXCSAT response.
        -- 0d in the 3rd byte of the data section seems to be a reliable test.
        if string.format("%x",string.byte(response,position + 3)) ~= "d0" then
                return
        end
       
        local bytes = " "
        local len_response = string.len(response) - 2
       
        -- Parse response until the EXCSAT identifier is found.  From here we should
        -- be able to find everything else.
        while (bytes ~= "1443") and (position <= len_response) do
                bytes = string.format("%x",string.byte(response,position +1)) .. string.format("%x",string.byte(response,position + 2))
                if bytes == nil then
                        return
                end
                position = position + 2
        end
       
        if position >= len_response then
                -- If this section is true then this either not a valid response or
                -- it is in a format that we have not seen.  Exit cleanly.
                return
        end
               
        -- ****************************************************************************
        -- Process the Server class section of the response packet
        -- ****************************************************************************
        local len_external_name, external_name = process_block(response, position, ebcdic2ascii)

        -- ****************************************************************************
        -- Process the Manager Level section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_external_name
       
        -- Get the length of the next block, Wireshark calls this "Manager-Level list"
        -- We are going to skip over this section
        local len_manager_level = string.format("%d",string.byte(response, position +1)) .. string.format("%d",string.byte(response,position + 2))
       
       
        -- ****************************************************************************
        -- Process the Server class section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_manager_level
        local len_server_class, server_class = process_block(response, position, ebcdic2ascii)
               
       
        -- ****************************************************************************
        -- Process the Server name section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_server_class
        local len_server_name, server_name = process_block(response, position, ebcdic2ascii)
       
        -- ****************************************************************************
        -- Process the Server version section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_server_name
       
        local len_server_version, server_version = process_block(response, position, ebcdic2ascii)
       
        if string.sub(server_version,1,3) == "SQL" then
                local major_version = string.sub(server_version,4,5)

                -- strip the leading 0 from the major version, for consistency with
                -- nmap-service-probes results
                if string.sub(major_version,1,1) == "0" then
                        major_version = string.sub(major_version,2)
                end
                local minor_version = string.sub(server_version,6,7)
                local hotfix = string.sub(server_version,8)
                server_version = major_version .. "." .. minor_version .. "." .. hotfix
        end
               
        port.version.name = "ibm-db2"
        port.version.name_confidence = 100
        if server_version ~= nil then port.version.version = server_version end
        if server_class ~= nil then port.version.extrainfo = server_class   end
       
        nmap.set_port_version(host, port, "hardmatched")
       
        -- Generate results
            local results = "DB2 Version: " .. server_version .. "\n"
        results = results .. "Server Platform: " .. server_class .. "\n"
        results = results .. "Instance Name:   " .. server_name .. "\n"
        results = results .. "External Name:   " .. external_name
       
        return results
                       
end



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

Re: [SCRIPT] DB2 Information gathering script

by Matt Selsky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Nov 8, 2009, at 10:28 PM, Tom Sellers wrote:

> I have written a NSE script that enhances version detection for  
> DB2.  It also
> gathers platform (OS) and database instance information.  It  
> functions in a similar
> way to the MS SQL script.


Here's the output for my DB2 server on Solaris:

$ nmap -sV -p50000 spinach

Starting Nmap 5.05BETA1 ( http://nmap.org ) at 2009-11-09 09:44 EST
Interesting ports on spinach (192.168.1.233):
PORT      STATE SERVICE VERSION
50000/tcp open  ibm-db2 IBM DB2 Database Server 7.02.4
Service Info: OS: SUN

Service detection performed. Please report any incorrect results at http://nmap.org/submit/ 
  .
Nmap done: 1 IP address (1 host up) scanned in 11.30 seconds
$ NMAPDIR=. nmap -sV -p50000 --script=db2-info spinach

Starting Nmap 5.05BETA1 ( http://nmap.org ) at 2009-11-09 09:47 EST
NSE: Script Scanning completed.
Interesting ports on spinach (192.168.1.233):
PORT      STATE SERVICE VERSION
50000/tcp open  ibm-db2 IBM DB2 Database Server DB2 UDB 7.2 (QDB2/SUN)
|  db2-info: DB2 Version: DB2 UDB 7.2
|  Server Platform: QDB2/SUN
|  Instance Name:   db2inst1
|_ External Name:   db2inst1db2agent000052FF
Service Info: OS: SUN

Service detection performed. Please report any incorrect results at http://nmap.org/submit/ 
  .
Nmap done: 1 IP address (1 host up) scanned in 11.68 seconds


Why does the version with script scanning have a less precise version  
number? (7.02.4 vs 7.2)


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

Re: [SCRIPT] DB2 Information gathering script

by Ron (list) :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Tom,

I couldn't find any DB2 servers in our environment that listen on 50000
or 60000. Perhaps it has to do with our lockdown profile, or something?

I found one that I could connect to on 6789, though I didn't try that
port on all of them.

Ron

Tom Sellers wrote:

> I have written a NSE script that enhances version detection for DB2.  It
> also
> gathers platform (OS) and database instance information.  It functions
> in a similar
> way to the MS SQL script.
>
> The script sends a DB2 EXCSAT (exchange server attributes) command
> packet and
> parses the response.  This is a legitimate DB2 command and, based on my
> reviews
> of logs, causes no problems on the server.  DB2 does log the connection
> and the
> source IP address though.
>
>
> PORT      STATE SERVICE VERSION
> 523/tcp   open  ibm-db2 IBM DB2 Database Server 9.07.0
> 50000/tcp open  ibm-db2 IBM DB2 Database Server 9.07.0 (QDB2/LINUX)
> |  db2-info: DB2 Version: 9.07.0
> |  Server Platform: QDB2/LINUX
> |  Instance Name:   db2inst1
> |_ External Name:   db2inst1db2agent000051B3%FED%Y00
>
> In the sample output above the version of the DB2 DAS service on port
> 523 was
> detected using nmap-service-probes.  Historically, unlike the DAS port
> on 523,
> we have been unable to detect the exact version number on the DB2 database
> instances themselves.  There may be multiple DB2 database instances and
> they
> typically cluster around port 50000 and 60000.
>
> Port 50000 would normally be detected as service "ibm-db2" with a
> version string
> "IBM DB2 Database Server".  The attached NSE script can now detect the
> exact
> version number and platform as well as the instance name for each of the
> databases.
>
> Any testing or feedback with the functionality and structure of the
> script would
> be greatly appreciated!
>
>
> Here are my current concerns with the script:
> 1.  Is the default output too verbose?  Should I limit the output to the
> info
>     on the port line by default and add the other lines with -v?
>
> 2.  The data from the server is encoded in EBCDIC.  I am decoding this
> will what
>     amounts to a lookup table.  Is there a more appropriate/efficient
> way to
>     handle this?
>
> 3.  I have built the EBCDIC table containing the ASCII chars that we should
>     encounter in this context.  Should I go ahead and build out the full
> ASCII
>     table? (And wow, I am glad we don't use EBCDIC for much.)
>
> Thanks much,
>
> Tom
>
>
>
>
>
>
>
>
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> Sent through the nmap-dev mailing list
> http://cgi.insecure.org/mailman/listinfo/nmap-dev
> Archived at http://seclists.org/nmap-dev/
_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://seclists.org/nmap-dev/

Re: [SCRIPT] DB2 Information gathering script

by doug-12 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Works good for me:

PORT      STATE SERVICE
50000/tcp open  ibm-db2
|  db2-info: DB2 Version: 8.02.9
|  Server Platform: QDB2/SUN
|  Instance Name:   db2inst1
|_ External Name:   db2inst1db2agent00002B430


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

attachment0 (203 bytes) Download Attachment

Re: [SCRIPT] DB2 Information gathering script

by Tom Sellers :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Matt Selsky wrote:

> Why does the version with script scanning have a less precise version
> number? (7.02.4 vs 7.2)
>
>

Thanks for testing the script!  Based on my testing it looks like the service,
at least with the 7.x family, is actually returning less precise information.

With other families I see EBCDIC encoded "SQL090204" which I convert to 9.02.04.
With 7 it returns EBCDIC encoded "DB2 UDB 7.2".  I will add code to the script to
see if the normal probe has returned a version number, and if so, determine if
it is more precise than what the script has returned.

Thanks again!

Tom

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

Re: [SCRIPT] DB2 Information gathering script

by Fyodor :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Sun, Nov 08, 2009 at 09:28:55PM -0600, Tom Sellers wrote:
> I have written a NSE script that enhances version detection for DB2.
> It also gathers platform (OS) and database instance information.  It
> functions in a similar way to the MS SQL script.

>From the feedback and functionality so far, it sounds like a winner!
If you commit it in the next day or two, it should be in time for the
new release.

Cheers,
-F
_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://seclists.org/nmap-dev/

Re: [SCRIPT] DB2 Information gathering script - New version of the script

by Tom Sellers :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Matt Selsky wrote:

>
> On Nov 8, 2009, at 10:28 PM, Tom Sellers wrote:
>
>> I have written a NSE script that enhances version detection for DB2.  
>> It also
>> gathers platform (OS) and database instance information.  It functions
>> in a similar
>> way to the MS SQL script.
>
>
> Here's the output for my DB2 server on Solaris:
>
> $ nmap -sV -p50000 spinach

<SNIP>

>
> Why does the version with script scanning have a less precise version
> number? (7.02.4 vs 7.2)

Thanks everyone for the feedback!

I have attached an updated version of the db2-info.nse script that should
keep the probed version string if it is more precise.  Either way the additional
data is generated when the verbosity is high enough.

Also, the categories have been updated to be more appropriate:

        categories = {"safe", "discovery", "version"}

If no one objects, I will also tweak the nmap-service-probes entry for ibm-db2 from
ports 523,50000  to
ports 523,50000-50025,60000-60025

This should improve the likelihood that DB2 is detected without having to use
--version-all.  There aren't any other probes in that range and, I think, mainstream
products using this range are limited so there should be almost no performance impact.


Tom


description = [[
Attempts to extract information from IBM DB2 Server instances.  The script sends a
DB2 EXCSAT (exchange server attributes) command packet and parses the response.
]]

-- rev 1.1 (2010-11-10)
 
author = "Tom Sellers <nmap@...>"

license = "Same as Nmap--See http://nmap.org/book/man-legal.html"

categories = {"safe", "discovery", "version"}

require "stdnse"
require "shortport"

portrule = shortport.port_or_service({50000,60000},"ibm-db2", "tcp", {"open", "open|filtered"})

-- This function processes a section of the EXCSAT response packet

--@param response This is the data returned from the server as a result of the client query.
--@param position This is the position within the response that this function will start processing from.
--@param ebcdic2ascii This is a table containing a conversion chart for returning the ASCII value of EBCDIC encoded HEX value.
--@return section_length This is the length of the currect section. Will be used to move position for processing next section.
--@return data_string This string contains the data pulled from this section of the server response.
local function process_block(response, position, ebcdic2ascii)

        -- This fuction assumes that the current position is the start of a section within
        -- the DB2 EXCSAT response packet
               
        -- Get the length of this section of the response packet
        local section_length = string.format("%d",string.byte(response,position +1)) .. string.format("%d",string.byte(response,position + 2))
        position = position + 2
               
        -- locate the data string and convert it from EBCDIC to ASCII
        local i = 0
        local data_string = ""
        for i = (position + 3),(position + section_length -2 ),1 do
                -- stdnse.print_debug("%s","INFO: Current postion (i) = " .. i)
                -- stdnse.print_debug("%s","INFO: Hex value = " .. string.format("%x",string.byte(response,i)))
                -- stdnse.print_debug("%s","INFO: Current data_string = " .. data_string)
                if string.format("%x",string.byte(response,i)) == "0" then
                        break
                end
                data_string  = data_string .. ebcdic2ascii[string.format("%x",string.byte(response,i))]
        end
       
        return section_length, data_string
       
end -- fuction process_block



action = function(host, port)
       
        local ebcdic2ascii = {
                -- The following reference was used for this table:  http://www.simotime.com/asc2ebc1.htm
                ["00"] = string.format("%c", 00),
                ["40"] = " ",
                ["81"] = "a",
                ["82"] = "b",
                ["83"] = "c",
                ["84"] = "d",
                ["85"] = "e",
                ["86"] = "f",
                ["87"] = "g",
                ["88"] = "h",
                ["89"] = "i",
                ["91"] = "j",
                ["92"] = "k",
                ["93"] = "l",
                ["94"] = "m",
                ["95"] = "n",
                ["96"] = "o",
                ["97"] = "p",
                ["98"] = "q",
                ["99"] = "r",
                ["a2"] = "s",
                ["a3"] = "t",
                ["a4"] = "u",
                ["a5"] = "v",
                ["a6"] = "w",
                ["a7"] = "x",
                ["a8"] = "y",
                ["a9"] = "z",
                ["c1"] = "A",
                ["c2"] = "B",
                ["c3"] = "C",
                ["c4"] = "D",
                ["c5"] = "E",
                ["c6"] = "F",
                ["c7"] = "G",
                ["c8"] = "H",
                ["c9"] = "I",
                ["d1"] = "J",
                ["d2"] = "K",
                ["d3"] = "L",
                ["d4"] = "M",
                ["d5"] = "N",
                ["d6"] = "O",
                ["d7"] = "P",
                ["d8"] = "Q",
                ["d9"] = "R",
                ["e2"] = "S",
                ["e3"] = "T",
                ["e4"] = "U",
                ["e5"] = "V",
                ["e6"] = "W",
                ["e7"] = "X",
                ["e8"] = "Y",
                ["e9"] = "Z",
                ["f0"] = 0,
                ["f1"] = 1,
                ["f2"] = 2,
                ["f3"] = 3,
                ["f4"] = 4,
                ["f5"] = 5,
                ["f6"] = 6,
                ["f7"] = 7,
                ["f8"] = 8,
                ["f9"] = 9,
                ["4b"] = ".",
                ["4c"] = "<",
                ["4d"] = "(",
                ["4e"] = "+",
                ["4f"] = "|",
                ["5a"] = "!",
                ["5b"] = "$",
                ["5c"] = "*",
                ["5d"] = ")",
                ["5e"] = ";",
                ["60"] = "-",
                ["61"] = "/",
                ["6b"] = ",",
                ["6c"] = "%",
                ["6d"] = "_",
                ["6e"] = ">",
                ["6f"] = "?",
                ["79"] = "`",
                ["7a"] = ":",
                ["7b"] = "#",
                ["7c"] = "@",
                ["7d"] = "'",
                ["7e"] = "=",
                ["7f"] = "\"",
                ["a1"] = "~",
                ["ba"] = "[",
                ["bb"] = "]",
                ["c0"] = "{",
                ["d0"] = "}",
                ["e0"] = "\\"  -- escape the \ character
        }

        -- create the socket used for our connection
        local socket = nmap.new_socket()
       
        -- set a reasonable timeout value
        socket:set_timeout(10000)
       
        -- do some exception handling / cleanup
        local catch = function()
                stdnse.print_debug("%s", "ERROR communicating with " .. host.ip .. " on port " .. port.number)
                socket:close()
        end
       
        local try = nmap.new_try(catch)

        try(socket:connect(host.ip, port.number, "tcp"))

        -- Build DB2 EXCSAT (exchange server attributes) command packet
       
        local query = string.char(0x00, 0x98, 0xd0, 0x41, 0x00, 0x01, 0x00, 0x92, 0x10, 0x41)  -- Header
       
        --  NOTE:  The server's response packet is in the same format at the client query packet being built
        --         in the section below.
       
        --  External Name section: first two bytes (00,48) are section length in HEX, next bytes (11,5e) are section identifier for External Name
        --  In this packet the external name is 'db2jcc_application  JCC03570300' encoded in EBCDIC
        query = query .. string.char(0x00, 0x48, 0x11, 0x5e, 0x84, 0x82, 0xf2, 0x91, 0x83, 0x83, 0x6d, 0x81, 0x97, 0x97, 0x93, 0x89)
        query = query .. string.char(0x83, 0x81, 0xa3, 0x89, 0x96, 0x95, 0x40, 0x40, 0xd1, 0xc3, 0xc3, 0xf0, 0xf3, 0xf5, 0xf7, 0xf0)
        query = query .. string.char(0xf3, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
        query = query .. string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
        query = query .. string.char(0x00, 0x00, 0x00, 0x60, 0xf0, 0xf0, 0xf0, 0xf1)
       
         -- Client Name section: first two bytes (00,16) are section length in HEX, next two bytes (11,6d) are section identifier for Server Name
         -- In the request packet Server Name = client name.  The value here is all spaces, encoded in EBCDIC
        query = query .. string.char(0x00, 0x16, 0x11, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40)
        query = query .. string.char(0x40, 0x40, 0x40, 0x40, 0x40, 0x40)
       
        -- Product Release Level section:  This section is not as important in the client query as it is in the server response.
        -- The first two bytes (00,0c) are section length in HEX, next two bytes (11,5a) are section identifier for Product Release Level
        -- The value here is 'JCC03570' encoded in EBCDIC
        query = query .. string.char(0x00, 0x0c, 0x11, 0x5a, 0xd1, 0xc3, 0xc3, 0xf0, 0xf3, 0xf5, 0xf7, 0xf0)  
               
        -- Manager level section: first two bytes (00,18) are section length in HEX, next two bytes (14,04) are section identifier for Manager-Level List
        query = query .. string.char(0x00, 0x18, 0x14, 0x04, 0x14, 0x03, 0x00, 0x07, 0x24, 0x07, 0x00, 0x0a, 0x24, 0x0f, 0x00, 0x08)
        query = query .. string.char(0x14, 0x40, 0x00, 0x09, 0x14, 0x74, 0x00, 0x08)
       
        -- Server Class section: first two bytes (00,0c) are section length in HEX, next two bytes (11,47) are section identifier for Server Class Name
        -- This section is essentially platform software information.  The value here is 'QDB2/JBM' encoded in EBCDIC)
        query = query .. string.char(0x00, 0x0c, 0x11, 0x47, 0xd8, 0xc4, 0xc2, 0xf2, 0x61, 0xd1, 0xe5, 0xd4)
       
         -- Access Security section
        query = query .. string.char(0x00, 0x26, 0xd0, 0x01, 0x00, 0x02, 0x00, 0x20, 0x10, 0x6d, 0x00, 0x06, 0x11, 0xa2, 0x00, 0x03)
       

        -- Database name section:  This is the client's query for a specific database.  A DB2 default database name, 'db2insta1', was chosen.
        -- It is encoded below in EBCDIC.  The first two bytes (00,16) are section length in HEX, next two bytes (21,10) are section identifier
        -- for Relational Database Name
        query = query .. string.char(0x00, 0x16, 0x21, 0x10, 0x84, 0x82, 0xf2, 0x89, 0x95, 0xa2, 0xa3, 0xf1, 0x40, 0x40, 0x40, 0x40)
        query = query .. string.char(0x40, 0x40, 0x40, 0x40, 0x40, 0x40)
               
        try(socket:send(query))
       
        local status
        local response
       
        -- read in any response we might get
        status, response = socket:receive()

        socket:close()
       
        if (not status) or (response == "TIMEOUT") or (response == nil) then
                stdnse.print_debug("%s","ERROR: No data, ending communications with " .. host.ip .. ":" .. port.number)
                return
        end
       
        local position = 0
       
        -- Check to see if the data is actually a DB2 DDM EXCSAT response.
        -- 0d in the 3rd byte of the data section seems to be a reliable test.
        if string.format("%x",string.byte(response,position + 3)) ~= "d0" then
                return
        end
       
        local bytes = " "
        local len_response = string.len(response) - 2
       
        -- Parse response until the EXCSAT identifier is found.  From here we should
        -- be able to find everything else.
        while (bytes ~= "1443") and (position <= len_response) do
                bytes = string.format("%x",string.byte(response,position +1)) .. string.format("%x",string.byte(response,position + 2))
                if bytes == nil then
                        return
                end
                position = position + 2
        end
       
        if position >= len_response then
                -- If this section is true then this either not a valid response or
                -- it is in a format that we have not seen.  Exit cleanly.
                return
        end
               
        -- ****************************************************************************
        -- Process the Server class section of the response packet
        -- ****************************************************************************
        local len_external_name, external_name = process_block(response, position, ebcdic2ascii)

        -- ****************************************************************************
        -- Process the Manager Level section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_external_name
       
        -- Get the length of the next block, Wireshark calls this "Manager-Level list"
        -- We are going to skip over this section
        local len_manager_level = string.format("%d",string.byte(response, position +1)) .. string.format("%d",string.byte(response,position + 2))
       
       
        -- ****************************************************************************
        -- Process the Server class section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_manager_level
        local len_server_class, server_class = process_block(response, position, ebcdic2ascii)
               
       
        -- ****************************************************************************
        -- Process the Server name section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_server_class
        local len_server_name, server_name = process_block(response, position, ebcdic2ascii)
       
        -- ****************************************************************************
        -- Process the Server version section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_server_name
       
        local len_server_version, server_version = process_block(response, position, ebcdic2ascii)
       
        if string.sub(server_version,1,3) == "SQL" then
                local major_version = string.sub(server_version,4,5)

                -- strip the leading 0 from the major version, for consistency with
                -- nmap-service-probes results
                if string.sub(major_version,1,1) == "0" then
                        major_version = string.sub(major_version,2)
                end
                local minor_version = string.sub(server_version,6,7)
                local hotfix = string.sub(server_version,8)
                server_version = major_version .. "." .. minor_version .. "." .. hotfix
        end
               
        -- Try to determine which of the two values (probe version vs script) has more
        -- precision.  A couple DB2 versions send DB2 UDB 7.1 vs SQL090204 (9.02.04)
        local _
        local current_count = 0
        if port.version.version ~= nil then
                _, current_count = string.gsub(port.version.version, "%.", "%.")
        end

        local new_count = 0
        if server_version ~= nil then
                _, new_count = string.gsub(server_version, "%.", "%.")
        end
       
        if current_count < new_count then
                port.version.version = server_version
        end
               
        -- Set port information
        port.version.name = "ibm-db2"
        port.version.name_confidence = 100
        if server_class ~= nil then port.version.extrainfo = server_class   end
       
        nmap.set_port_version(host, port, "hardmatched")
       
        -- Generate results
            local results = "DB2 Version: " .. server_version .. "\n"
        results = results .. "Server Platform: " .. server_class .. "\n"
        results = results .. "Instance Name:   " .. server_name .. "\n"
        results = results .. "External Name:   " .. external_name
       
        return results
                       
end



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

Re: [SCRIPT] DB2 Information gathering script - New version of the script

by David Fifield :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Tue, Nov 10, 2009 at 11:10:41PM -0600, Tom Sellers wrote:

> Thanks everyone for the feedback!
>
> I have attached an updated version of the db2-info.nse script that
> should keep the probed version string if it is more precise.  Either
> way the additional data is generated when the verbosity is high
> enough.
>
> Also, the categories have been updated to be more appropriate:
>
> categories = {"safe", "discovery", "version"}

The only thing I can see to change is the ebcdic2ascii table. It should
be able to handle any byte value, or unless I'm mistaken, the script can
crash at this line:

> data_string  = data_string .. ebcdic2ascii[string.format("%x",string.byte(response,i))]

You can do it by setting a default value on the table as is described
here: http://www.lua.org/pil/13.4.3.html. So in this case it might be

setmetatable(ebcdic2ascii, { __index = function() return "." end })

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

Re: [SCRIPT] DB2 Information gathering script - New version of the script

by Tom Sellers :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

David Fifield wrote:
> On Tue, Nov 10, 2009 at 11:10:41PM -0600, Tom Sellers wrote:

> The only thing I can see to change is the ebcdic2ascii table. It should
> be able to handle any byte value, or unless I'm mistaken, the script can
> crash at this line:
>
>> data_string  = data_string .. ebcdic2ascii[string.format("%x",string.byte(response,i))]
>
> You can do it by setting a default value on the table as is described
> here: http://www.lua.org/pil/13.4.3.html. So in this case it might be
>
> setmetatable(ebcdic2ascii, { __index = function() return "." end })
>
Thanks David, changed.  I feel much better about that table now.
I set the default character as a space as the script does some testing
based on number of "." in the version string.

Tom

description = [[
Attempts to extract information from IBM DB2 Server instances.  The script sends a
DB2 EXCSAT (exchange server attributes) command packet and parses the response.
]]

-- rev 1.2 (2010-11-11)
 
author = "Tom Sellers <nmap@...>"

license = "Same as Nmap--See http://nmap.org/book/man-legal.html"

categories = {"safe", "discovery", "version"}

require "stdnse"
require "shortport"

portrule = shortport.port_or_service({50000,60000},"ibm-db2", "tcp", {"open", "open|filtered"})

-- This function processes a section of the EXCSAT response packet

--@param response This is the data returned from the server as a result of the client query.
--@param position This is the position within the response that this function will start processing from.
--@param ebcdic2ascii This is a table containing a conversion chart for returning the ASCII value of EBCDIC encoded HEX value.
--@return section_length This is the length of the currect section. Will be used to move position for processing next section.
--@return data_string This string contains the data pulled from this section of the server response.
local function process_block(response, position, ebcdic2ascii)

        -- This fuction assumes that the current position is the start of a section within
        -- the DB2 EXCSAT response packet
               
        -- Get the length of this section of the response packet
        local section_length = string.format("%d",string.byte(response,position +1)) .. string.format("%d",string.byte(response,position + 2))
        position = position + 2
               
        -- locate the data string and convert it from EBCDIC to ASCII
        local i = 0
        local data_string = ""
        for i = (position + 3),(position + section_length -2 ),1 do
                -- stdnse.print_debug("%s","INFO: Current postion (i) = " .. i)
                -- stdnse.print_debug("%s","INFO: Hex value = " .. string.format("%x",string.byte(response,i)))
                -- stdnse.print_debug("%s","INFO: Current data_string = " .. data_string)
                if string.format("%x",string.byte(response,i)) == "0" then
                        break
                end
                data_string  = data_string .. ebcdic2ascii[string.format("%x",string.byte(response,i))]
        end
       
        return section_length, data_string
       
end -- fuction process_block



action = function(host, port)
       
        local ebcdic2ascii = {
                -- The following reference was used for this table:  http://www.simotime.com/asc2ebc1.htm
                ["00"] = string.format("%c", 00),
                ["40"] = " ",
                ["81"] = "a",
                ["82"] = "b",
                ["83"] = "c",
                ["84"] = "d",
                ["85"] = "e",
                ["86"] = "f",
                ["87"] = "g",
                ["88"] = "h",
                ["89"] = "i",
                ["91"] = "j",
                ["92"] = "k",
                ["93"] = "l",
                ["94"] = "m",
                ["95"] = "n",
                ["96"] = "o",
                ["97"] = "p",
                ["98"] = "q",
                ["99"] = "r",
                ["a2"] = "s",
                ["a3"] = "t",
                ["a4"] = "u",
                ["a5"] = "v",
                ["a6"] = "w",
                ["a7"] = "x",
                ["a8"] = "y",
                ["a9"] = "z",
                ["c1"] = "A",
                ["c2"] = "B",
                ["c3"] = "C",
                ["c4"] = "D",
                ["c5"] = "E",
                ["c6"] = "F",
                ["c7"] = "G",
                ["c8"] = "H",
                ["c9"] = "I",
                ["d1"] = "J",
                ["d2"] = "K",
                ["d3"] = "L",
                ["d4"] = "M",
                ["d5"] = "N",
                ["d6"] = "O",
                ["d7"] = "P",
                ["d8"] = "Q",
                ["d9"] = "R",
                ["e2"] = "S",
                ["e3"] = "T",
                ["e4"] = "U",
                ["e5"] = "V",
                ["e6"] = "W",
                ["e7"] = "X",
                ["e8"] = "Y",
                ["e9"] = "Z",
                ["f0"] = 0,
                ["f1"] = 1,
                ["f2"] = 2,
                ["f3"] = 3,
                ["f4"] = 4,
                ["f5"] = 5,
                ["f6"] = 6,
                ["f7"] = 7,
                ["f8"] = 8,
                ["f9"] = 9,
                ["4b"] = ".",
                ["4c"] = "<",
                ["4d"] = "(",
                ["4e"] = "+",
                ["4f"] = "|",
                ["5a"] = "!",
                ["5b"] = "$",
                ["5c"] = "*",
                ["5d"] = ")",
                ["5e"] = ";",
                ["60"] = "-",
                ["61"] = "/",
                ["6b"] = ",",
                ["6c"] = "%",
                ["6d"] = "_",
                ["6e"] = ">",
                ["6f"] = "?",
                ["79"] = "`",
                ["7a"] = ":",
                ["7b"] = "#",
                ["7c"] = "@",
                ["7d"] = "'",
                ["7e"] = "=",
                ["7f"] = "\"",
                ["a1"] = "~",
                ["ba"] = "[",
                ["bb"] = "]",
                ["c0"] = "{",
                ["d0"] = "}",
                ["e0"] = "\\"  -- escape the \ character
        }

        -- ebcdic2ascii does not contain all value, set a default value
        -- to improve stability.
        setmetatable(ebcdic2ascii, { __index = function() return " " end })
       
        -- create the socket used for our connection
        local socket = nmap.new_socket()
       
        -- set a reasonable timeout value
        socket:set_timeout(10000)
       
        -- do some exception handling / cleanup
        local catch = function()
                stdnse.print_debug("%s", "ERROR communicating with " .. host.ip .. " on port " .. port.number)
                socket:close()
        end
       
        local try = nmap.new_try(catch)

        try(socket:connect(host.ip, port.number, "tcp"))

        -- Build DB2 EXCSAT (exchange server attributes) command packet
       
        local query = string.char(0x00, 0x98, 0xd0, 0x41, 0x00, 0x01, 0x00, 0x92, 0x10, 0x41)  -- Header
       
        --  NOTE:  The server's response packet is in the same format at the client query packet being built
        --         in the section below.
       
        --  External Name section: first two bytes (00,48) are section length in HEX, next bytes (11,5e) are section identifier for External Name
        --  In this packet the external name is 'db2jcc_application  JCC03570300' encoded in EBCDIC
        query = query .. string.char(0x00, 0x48, 0x11, 0x5e, 0x84, 0x82, 0xf2, 0x91, 0x83, 0x83, 0x6d, 0x81, 0x97, 0x97, 0x93, 0x89)
        query = query .. string.char(0x83, 0x81, 0xa3, 0x89, 0x96, 0x95, 0x40, 0x40, 0xd1, 0xc3, 0xc3, 0xf0, 0xf3, 0xf5, 0xf7, 0xf0)
        query = query .. string.char(0xf3, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
        query = query .. string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
        query = query .. string.char(0x00, 0x00, 0x00, 0x60, 0xf0, 0xf0, 0xf0, 0xf1)
       
         -- Client Name section: first two bytes (00,16) are section length in HEX, next two bytes (11,6d) are section identifier for Server Name
         -- In the request packet Server Name = client name.  The value here is all spaces, encoded in EBCDIC
        query = query .. string.char(0x00, 0x16, 0x11, 0x6d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40)
        query = query .. string.char(0x40, 0x40, 0x40, 0x40, 0x40, 0x40)
       
        -- Product Release Level section:  This section is not as important in the client query as it is in the server response.
        -- The first two bytes (00,0c) are section length in HEX, next two bytes (11,5a) are section identifier for Product Release Level
        -- The value here is 'JCC03570' encoded in EBCDIC
        query = query .. string.char(0x00, 0x0c, 0x11, 0x5a, 0xd1, 0xc3, 0xc3, 0xf0, 0xf3, 0xf5, 0xf7, 0xf0)  
               
        -- Manager level section: first two bytes (00,18) are section length in HEX, next two bytes (14,04) are section identifier for Manager-Level List
        query = query .. string.char(0x00, 0x18, 0x14, 0x04, 0x14, 0x03, 0x00, 0x07, 0x24, 0x07, 0x00, 0x0a, 0x24, 0x0f, 0x00, 0x08)
        query = query .. string.char(0x14, 0x40, 0x00, 0x09, 0x14, 0x74, 0x00, 0x08)
       
        -- Server Class section: first two bytes (00,0c) are section length in HEX, next two bytes (11,47) are section identifier for Server Class Name
        -- This section is essentially platform software information.  The value here is 'QDB2/JBM' encoded in EBCDIC)
        query = query .. string.char(0x00, 0x0c, 0x11, 0x47, 0xd8, 0xc4, 0xc2, 0xf2, 0x61, 0xd1, 0xe5, 0xd4)
       
         -- Access Security section
        query = query .. string.char(0x00, 0x26, 0xd0, 0x01, 0x00, 0x02, 0x00, 0x20, 0x10, 0x6d, 0x00, 0x06, 0x11, 0xa2, 0x00, 0x03)
       

        -- Database name section:  This is the client's query for a specific database.  A DB2 default database name, 'db2insta1', was chosen.
        -- It is encoded below in EBCDIC.  The first two bytes (00,16) are section length in HEX, next two bytes (21,10) are section identifier
        -- for Relational Database Name
        query = query .. string.char(0x00, 0x16, 0x21, 0x10, 0x84, 0x82, 0xf2, 0x89, 0x95, 0xa2, 0xa3, 0xf1, 0x40, 0x40, 0x40, 0x40)
        query = query .. string.char(0x40, 0x40, 0x40, 0x40, 0x40, 0x40)
               
        try(socket:send(query))
       
        local status
        local response
       
        -- read in any response we might get
        status, response = socket:receive()

        socket:close()
       
        if (not status) or (response == "TIMEOUT") or (response == nil) then
                stdnse.print_debug("%s","ERROR: No data, ending communications with " .. host.ip .. ":" .. port.number)
                return
        end
       
        local position = 0
       
        -- Check to see if the data is actually a DB2 DDM EXCSAT response.
        -- 0d in the 3rd byte of the data section seems to be a reliable test.
        if string.format("%x",string.byte(response,position + 3)) ~= "d0" then
                return
        end
       
        local bytes = " "
        local len_response = string.len(response) - 2
       
        -- Parse response until the EXCSAT identifier is found.  From here we should
        -- be able to find everything else.
        while (bytes ~= "1443") and (position <= len_response) do
                bytes = string.format("%x",string.byte(response,position +1)) .. string.format("%x",string.byte(response,position + 2))
                if bytes == nil then
                        return
                end
                position = position + 2
        end
       
        if position >= len_response then
                -- If this section is true then this either not a valid response or
                -- it is in a format that we have not seen.  Exit cleanly.
                return
        end
               
        -- ****************************************************************************
        -- Process the Server class section of the response packet
        -- ****************************************************************************
        local len_external_name, external_name = process_block(response, position, ebcdic2ascii)

        -- ****************************************************************************
        -- Process the Manager Level section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_external_name
       
        -- Get the length of the next block, Wireshark calls this "Manager-Level list"
        -- We are going to skip over this section
        local len_manager_level = string.format("%d",string.byte(response, position +1)) .. string.format("%d",string.byte(response,position + 2))
       
       
        -- ****************************************************************************
        -- Process the Server class section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_manager_level
        local len_server_class, server_class = process_block(response, position, ebcdic2ascii)
               
       
        -- ****************************************************************************
        -- Process the Server name section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_server_class
        local len_server_name, server_name = process_block(response, position, ebcdic2ascii)
       
        -- ****************************************************************************
        -- Process the Server version section of the response packet
        -- ****************************************************************************
        -- Move the position to the beginning of the current section
        position = position + len_server_name
       
        local len_server_version, server_version = process_block(response, position, ebcdic2ascii)
       
        if string.sub(server_version,1,3) == "SQL" then
                local major_version = string.sub(server_version,4,5)

                -- strip the leading 0 from the major version, for consistency with
                -- nmap-service-probes results
                if string.sub(major_version,1,1) == "0" then
                        major_version = string.sub(major_version,2)
                end
                local minor_version = string.sub(server_version,6,7)
                local hotfix = string.sub(server_version,8)
                server_version = major_version .. "." .. minor_version .. "." .. hotfix
        end
               
        -- Try to determine which of the two values (probe version vs script) has more
        -- precision.  A couple DB2 versions send DB2 UDB 7.1 vs SQL090204 (9.02.04)
        local _
        local current_count = 0
        if port.version.version ~= nil then
                _, current_count = string.gsub(port.version.version, "%.", "%.")
        end

        local new_count = 0
        if server_version ~= nil then
                _, new_count = string.gsub(server_version, "%.", "%.")
        end
       
        if current_count < new_count then
                port.version.version = server_version
        end
               
        -- Set port information
        port.version.name = "ibm-db2"
        port.version.name_confidence = 100
        if server_class ~= nil then port.version.extrainfo = server_class   end
       
        nmap.set_port_version(host, port, "hardmatched")
       
        -- Generate results
            local results = "DB2 Version: " .. server_version .. "\n"
        results = results .. "Server Platform: " .. server_class .. "\n"
        results = results .. "Instance Name:   " .. server_name .. "\n"
        results = results .. "External Name:   " .. external_name
       
        return results
                       
end



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

Re: [SCRIPT] DB2 Information gathering script - New version of the script

by Matt Selsky :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Nov 11, 2009, at 12:10 AM, Tom Sellers wrote:

> I have attached an updated version of the db2-info.nse script that should
> keep the probed version string if it is more precise.  Either way the additional
> data is generated when the verbosity is high enough.

I just tested this and the output maintains the precision.  Thanks!


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