Nmap Development mailing list archives

Re: [NSE Script] MySQL Server Information


From: sawall <sawall () gmail com>
Date: Tue, 18 Dec 2007 11:32:22 -0600

Great work guys.  This script is awesome.  The one thing I am trying to test
for is a blank (or easy) password for the sa account.  How hard do you think
that would be to build in?

I'm at a slight disadvantage as I just started trying to learn about Lua
today, so it might take me a bit before I'm off and running.

Thanks,
chris



 From: jah 
<jah_at_zadkiel.plus.com<jah_at_zadkiel.plus.com?Subject=Re:%20%5BNSE%20Script%5D%20MySQL%20Server%20Information>>

Date: Tue, 18 Dec 2007 02:26:14 +0000

On 17/12/2007 00:07, Kris Katterjohn wrote:
*>> Initiating SCRIPT ENGINE at 22:20 *
*>> *
*>> SCRIPT ENGINE: TCP 192.168.1.1:12439 > 85.234.xxx.xxx:3306 | CONNECT *
*>> SCRIPT ENGINE: TCP 192.168.1.1:12439 < 85.234.xxx.xxx:3306 | *
*>> n....i.Host 'me!' is blocked because of many connection errors; *
*>> unblock with 'mysqladmin flush-hosts' *
*>> *
*>> and it seemed to hang right after: *
*>> *
*>> SCRIPT ENGINE: TCP 192.168.1.1:12439 > 85.234.xxx.xxx:3306 | CLOSE *
*>> *
*>> It may, or may not, be worth testing for this. *
*>> *
*> Great script by the way. I found another thing while I was playing. *
*> I found a webpage that reveals the php mysql_connect command yesterday *
*> and, as if by magic, your post to nmap-dev followed shortly after so I *
*> took the opportunity to see if I could mod the script and connect to *
*> it with the supplied credentials. After playing for a while, running *
*> your script was causing nmap to hammer my cpu and I found that I'd *
*> been blocked by the mysql server and got the following response (the *
*> names have been changed to protect the stupid): *
*> *
*> Did this error happen with my script verbatim, or only with your *
*> modded version? *
*> *
*> I've just been reading on this, and one possibility is just the actual *
*> network connection between you and the host. Another thing could be *
*> my script causing this. I'm certainly no expert on the MySQL protocol *
*> (or Lua as you can tell :D). *
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...

jah

[1] many connection errors -
http://dev.mysql.com/doc/refman/5.0/en/blocked-host.html
[2] MySql Protocol -
http://www.redferni.uklinux.net/mysql/MySQL-Protocol.html

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

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 = shortport.port_or_service(3306, "mysql")

--[[ ** If version.extrainfo contains "unauthorized" then don't run the
script **

portrule = function(host, port)

        if
                (port.number == 3306
                or port.service == "mysql")
                and port.protocol == "tcp"
                and port.state == "open"
                and not (port.version.extrainfo ~= nil and string.match(
port.version.extrainfo, "unauthorized"))
                -- should we also test for lang en-gb "unauthoriSed"??

        then
                return true
        else
                return false
        end
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

        else

                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"

        end

        return output

end

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


Current thread: