Nmap Security Scanner
*Intro
*Ref Guide
*Install Guide
*Download
*Changelog
*Book
*Docs
Security Lists
*Nmap Hackers
*Nmap Dev
*Bugtraq
*Full Disclosure
*Pen Test
*Basics
*More
Security Tools
*Pass crackers
*Sniffers
*Vuln Scanners
*Web scanners
*Wireless
*Exploitation
*Packet crafters
*More
Site News
Site Search:
Exploit World
Advertising
About/Contact
Credits
Sponsors:
edgeos



Nmap Development: Re: [NSE Script] MySQL Server Information

Re: [NSE Script] MySQL Server Information

From: Kris Katterjohn <katterjohn_at_gmail.com>
Date: Tue, 18 Dec 2007 00:22:46 -0600

jah wrote:
> No, it seems your script was not the cause, my messing around caused
> it. I think "many connection errors" refers to the application layer
> [1] and after much testing, I found that your script is closing sockets
> without issue and that the issue was further into the script.
> Then after a bit of reading, I found that the script was breaking when
> the mysql server responded with an error which it does by serving 0xff
> instead of a protocol byte, followed by a two byte error code and then
> the error string [2]. It was evident that your code wasn't handling
> this even though I couldn't tell exactly where it broke. Doesn't really
> matter though...
>
> I've attached your script, slightly modified to return any mysql error
> details and which shouldn't cause the above described issue in these
> circumstances. Hopefully.
>
> While I was testing it with many many random hosts I found that, using
> version detection, the version always contains "unauthorized" when I get
> an error code 0x046A (1130 ~ "Host not allowed to connect") from the
> script output - quite useful cross check I thought - and, given that
> nmap will have already matched the mysql error, it's maybe worth testing
> for this straight away (if used with version detection) to avoid running
> the script. I've included this modded portrule too, but it's not
> implemented so you can have a play - I imagine the script needs a bit
> more testing anyway. Next step, supply the script with args for
> user:pwd and try to return database names...
>

Great job, man :)

I've attached yet another modified script which I'd like for you to try
out if you can.

The changes are pretty obvious I think, but here's the rundown:

Added some comments to the top.

I used a little more object-oriented approach in the portrule involving
the extrainfo field, especially since I added another match against it.

I grepped the nmap-service-probes and saw "unauthorized" with a
lowercase and uppercase U, so I edited that part. I saw no occurrences
of your suggested "unauthorised" in there, so I didn't add that.

I added a check for too many connections, which I saw in the probes
file. It should match the different little versions of it from there,
though I didn't find a server with that error to test with.

Another change was just for personal preference :) Instead of indenting
the rest of the script after the protocol vs. error check, I just return
from that if() and the rest was as before. I just think it's too long
to warrant the extra indentation.

> jah
>

Thanks a lot,
Kris Katterjohn

-- Connect to MySQL server and print information such as the protocol and
-- version numbers, thread id, status, capabilities and the password salt

-- If, after service detection, the MySQL server appears to be blocking
-- our host or is blocked from too many connections, then we don't bother
-- running this script.

-- Many thanks to jah (jah_at_zadkiel.plus.com) for testing and enhancements

id = "MySQL Server Information"

description = "Connects to a MySQL server and prints information"

author = "Kris Katterjohn <katterjohn_at_gmail.com>"

license = "Look at Nmap's COPYING"

categories = { "discovery", "safe" }

require 'shortport'
require 'bit'

-- Grabs NUL-terminated string
local getstring = function(orig)
        local str = ""
        local index = 1

        while orig:byte(index) ~= 0 do
                str = str .. string.char(orig:byte(index))

                index = index + 1
        end

        return str
end

-- Convert two bytes into a number
ntohs = function(num)
        local b1 = bit.band(num:byte(1), 255)
        local b2 = bit.band(num:byte(2), 255)

        return bit.bor(b1, bit.lshift(b2, 8))
end

-- Convert three bytes into a number
ntoh3 = function(num)
        local b1 = bit.band(num:byte(1), 255)
        local b2 = bit.band(num:byte(2), 255)
        local b3 = bit.band(num:byte(3), 255)

        return bit.bor(b1, bit.lshift(b2, 8), bit.lshift(b3, 16))
end

-- Convert four bytes into a number
ntohl = function(num)
        local b1 = bit.band(num:byte(1), 255)
        local b2 = bit.band(num:byte(2), 255)
        local b3 = bit.band(num:byte(3), 255)
        local b4 = bit.band(num:byte(4), 255)

        return bit.bor(b1, bit.lshift(b2, 8), bit.lshift(b3, 16), bit.lshift(b4, 24))
end

-- Convert number to a list of capabilities for printing
capabilities = function(num)
        local caps = ""

        if bit.band(num, 1) > 0 then
                caps = caps .. "Long Passwords, "
        end

        if bit.band(num, 8) > 0 then
                caps = caps .. "Connect with DB, "
        end

        if bit.band(num, 32) > 0 then
                caps = caps .. "Compress, "
        end

        if bit.band(num, 64) > 0 then
                caps = caps .. "ODBC, "
        end

        if bit.band(num, 2048) > 0 then
                caps = caps .. "SSL, "
        end

        if bit.band(num, 8192) > 0 then
                caps = caps .. "Transactions, "
        end

        if bit.band(num, 32768) > 0 then
                caps = caps .. "Secure Connection, "
        end

        return caps:gsub(", $", "")
end

portrule = function(host, port)
        local extra = port.version.extrainfo

        if
                (port.number == 3306
                or port.service == "mysql")
                and port.protocol == "tcp"
                and port.state == "open"
                and not (extra ~= nil
                        and (extra:match("[Uu]nauthorized")
                                or extra:match("[Tt]oo many connection")))
        then
                return true
        end

        return false
end

action = function(host, port)
        local sock
        local response = ""
        local output = ""

        sock = nmap.new_socket()

        sock:set_timeout(5000)

        sock:connect(host.ip, port.number)

        while true do
                local status, line = sock:receive_lines(1)

                if not status then
                        break
                end

                response = response .. line
        end

        sock:close()

        local length = ntoh3(response:sub(1, 3))

        if length ~= response:len() - 4 then
                return "Invalid greeting (Not MySQL?)"
        end

        -- Keeps track of where we are in the binary data
        local offset = 1 + 4

        local protocol = response:byte(offset)

        if (protocol == 255) then
                output = "MySQL Error detected!" -- 0xff found where protocol should be
                
                offset = offset + 1
                
                local sqlerrno = ntohs(response:sub(offset, offset + 2))
                
                offset = offset + 2
                
                local sqlerrstr = response:sub(offset)
                
                output = output .. "\n" .. "Error Code was: " .. sqlerrno .. "\n"
                
                output = output .. "\n" .. sqlerrstr

                return output
        end

        offset = offset + 1

        local version = getstring(response:sub(offset))

        offset = offset + version:len() + 1

        local threadid = ntohl(response:sub(offset, offset + 4))

        offset = offset + 4

        local salt = getstring(response:sub(offset))

        offset = offset + salt:len() + 1

        local caps = capabilities(ntohs(response:sub(offset, offset + 2)))

        offset = offset + 2

        offset = offset + 1

        local status = ""

        if ntohs(response:sub(offset, offset + 2)) == 2 then
                status = "Autocommit"
        end

        offset = offset + 2

        offset = offset + 13 -- unused

        if response:len() - offset + 1 == 13 then
                salt = salt .. getstring(response:sub(offset))
        end

        output = output .. "Protocol: " .. protocol .. "\n"
        output = output .. "Version: " .. version .. "\n"
        output = output .. "Thread ID: " .. threadid .. "\n"

        if caps:len() > 0 then
                output = output .. "Some Capabilities: " .. caps .. "\n"
        end

        if status:len() > 0 then
                output = output .. "Status: " .. status .. "\n"
        end

        output = output .. "Salt: " .. salt .. "\n"

        return output
end

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

[ Nmap | Sec Tools | Mailing Lists | Site News | About/Contact | Advertising | Privacy ]
edgeos