diff --git a/nselib/mysql.lua b/nselib/mysql.lua index 148b77cb4..d9af71471 100644 --- a/nselib/mysql.lua +++ b/nselib/mysql.lua @@ -9,10 +9,13 @@ module(... or "mysql", package.seeall) --- Version 0.2 +-- Version 0.3 -- -- Created 01/15/2010 - v0.1 - created by Patrik Karlsson -- Revised 01/23/2010 - v0.2 - added query support, cleanup, documentation +-- Revised 08/24/2010 - v0.3 - added error handling for recieveGreeting +-- fixed a number of incorrect receives and changed +-- them to receive_bytes instead. local HAVE_SSL = false @@ -101,13 +104,25 @@ function receiveGreeting( socket ) local catch = function() socket:close() stdnse.print_debug("receiveGreeting(): failed") end local try = nmap.new_try(catch) - local data = try( socket:receive(4) ) + local data = try( socket:receive_bytes(HEADER_SIZE) ) local pos, response, tmp, _ pos, response = decodeHeader( data, 1 ) - - if response.len > data:len() then - stdnse.print_debug( "Missing %d bytes of data, receiving ... ", response.len - data:len() ) + + -- do we need to read the remainder + if ( #data - HEADER_SIZE < response.len ) then + local tmp = try( socket:receive_bytes( response.len - #data + HEADER_SIZE ) ) + data = data .. tmp + end + + local is_error + pos, is_error = bin.unpack( "C", data, pos ) + + if ( is_error == 0xff ) then + pos, response.errorcode = bin.unpack( "S", data, pos ) + pos, response.errormsg = bin.unpack("A" .. (#data - pos + 1), data, pos ) + + return false, response.errormsg end pos, response.proto = bin.unpack( "C", data, pos ) @@ -121,6 +136,7 @@ function receiveGreeting( socket ) pos, tmp = bin.unpack( "A12", data, pos ) response.salt = response.salt .. tmp + response.errorcode = 0 return true, response @@ -217,9 +233,15 @@ function loginRequest( socket, params, username, password, salt ) packet = bin.pack( "I", tmp ) .. packet try( socket:send(packet) ) - packet = try( socket:receive(packet) ) - + packet = try( socket:receive_bytes(HEADER_SIZE) ) local pos, response = decodeHeader( packet ) + + -- do we need to read the remainder + if ( #packet - HEADER_SIZE < response.len ) then + local tmp = try( socket:receive_bytes( response.len - #packet + HEADER_SIZE ) ) + packet = packet .. tmp + end + local is_error pos, is_error = bin.unpack( "C", packet, pos ) @@ -311,14 +333,14 @@ function decodeQueryResponse( socket ) local block_start, block_end local EOF_MARKER = 254 - data = try( socket:receive(HEADER_SIZE) ) + data = try( socket:receive_bytes(HEADER_SIZE) ) pos, header = decodeHeader( data, pos ) -- -- First, Let's attempt to read the "Result Set Header Packet" -- if data:len() < header.len then - data = data .. try( socket:receive( header.len - data:len() ) ) + data = data .. try( socket:receive_bytes( header.len - #data + HEADER_SIZE ) ) end rs.header = data:sub( 1, HEADER_SIZE + header.len ) @@ -345,13 +367,13 @@ function decodeQueryResponse( socket ) while true do if data:len() - pos < HEADER_SIZE then - data = data .. try( socket:receive( HEADER_SIZE - ( data:len() - pos ) ) ) + data = data .. try( socket:receive_bytes( HEADER_SIZE - ( data:len() - pos ) ) ) end pos, header = decodeHeader( data, pos ) if data:len() - pos < header.len - 1 then - data = data .. try( socket:receive( header.len - ( data:len() - pos ) ) ) + data = data .. try( socket:receive_bytes( header.len - ( data:len() - pos ) ) ) end if header.len > 0 then diff --git a/scripts/mysql-empty-password.nse b/scripts/mysql-empty-password.nse index 4d33aaece..016d36b6c 100644 --- a/scripts/mysql-empty-password.nse +++ b/scripts/mysql-empty-password.nse @@ -28,17 +28,23 @@ portrule = shortport.port_or_service(3306, "mysql") action = function( host, port ) local socket = nmap.new_socket() - local catch = function() socket:close() end - local try = nmap.new_try(catch) - local result, response = {}, nil + local result = {} local users = {"", "root"} - + -- set a reasonable timeout value socket:set_timeout(5000) for _, v in ipairs( users ) do - try( socket:connect(host, port) ) - response = try( mysql.receiveGreeting( socket ) ) + local status, response = socket:connect(host, port) + if( not(status) ) then return " \n ERROR: Failed to connect to mysql server" end + + status, response = mysql.receiveGreeting( socket ) + if ( not(status) ) then + stdnse.print_debug(3, SCRIPT_NAME) + socket:close() + return response + end + status, response = mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, v, nil, response.salt ) if response.errorcode == 0 then table.insert(result, string.format("%s account has empty password", ( v=="" and "anonymous" or v ) ) )