1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 12:41:29 +00:00

Improve robustness of PostgreSQL v3 response code. Fixes #2009

This commit is contained in:
nnposter
2020-06-27 21:51:45 +00:00
parent f31f56aa27
commit 832e8f41e2

View File

@@ -384,33 +384,30 @@ v3 =
-- @param pos number containing the offset into the data buffer -- @param pos number containing the offset into the data buffer
-- @return data string containing the initial and any additional data -- @return data string containing the initial and any additional data
readPacket = function(socket, data, pos) readPacket = function(socket, data, pos)
pos = pos or 1
data = data or ""
local pos = pos or 1 -- Make sure that we have enough data for the header
local data = data or "" local targetlen = pos - 1 + 5 -- header is 5 bytes (structure >BI4)
local status = true while #data < targetlen do
local tmp = "" local status, extra = socket:receive_bytes(targetlen - #data)
local ptype, len
local header
local catch = function() socket:close() stdnse.debug1("processResponse(): failed") end
local try = nmap.new_try(catch)
if ( data:len() - pos < 5 ) then
status, tmp = socket:receive_bytes( 5 - ( data:len() - pos ) )
end
if not status then if not status then
return nil, "Failed to read packet" stdnse.debug1("Not enough data for PGSQL response header")
return nil, extra
end
data = data .. extra
end end
if tmp:len() ~= 0 then local _, header = v3.decodeHeader(data, pos)
data = data .. tmp
targetlen = targetlen + header.len - 4 -- header.len is 4 bytes
while #data < targetlen do
local status, extra = socket:receive_bytes(targetlen - #data)
if not status then
stdnse.debug1("Not enough data for declared PGSQL response size")
return nil, extra
end end
data = data .. extra
pos, header = v3.decodeHeader(data,pos)
while data:len() < header.len do
data = data .. try(socket:receive_bytes( ( header.len + 1 ) - data:len() ))
end end
return data return data
end, end,
@@ -436,24 +433,27 @@ v3 =
-- @return response string containing decoded data -- @return response string containing decoded data
-- error message if pos is -1 -- error message if pos is -1
processResponse = function(data, pos) processResponse = function(data, pos)
local ptype, len, status, response pos = pos or 1
local pos = pos or 1
local header
pos, header = v3.decodeHeader( data, pos ) if not data then
return -1, "Not enough response data"
if v3.messageDecoder[header.type] then
pos, response = v3.messageDecoder[header.type](data, header.len, pos)
if pos ~= -1 then
response.type = header.type
return pos, response
end end
else
local header
pos, header = v3.decodeHeader( data, pos )
if not v3.messageDecoder[header.type] then
stdnse.debug1("Missing decoder for %d", header.type ) stdnse.debug1("Missing decoder for %d", header.type )
return -1, ("Missing decoder for %d"):format(header.type) return -1, ("Missing decoder for %d"):format(header.type)
end end
local response
pos, response = v3.messageDecoder[header.type](data, header.len, pos)
if pos == -1 then
return -1, "Decoding failed" return -1, "Decoding failed"
end
response.type = header.type
return pos, response
end, end,
--- Attempts to authenticate to the pgsql server --- Attempts to authenticate to the pgsql server
@@ -469,47 +469,44 @@ v3 =
-- result string containing an error message if login fails -- result string containing an error message if login fails
loginRequest = function ( socket, params, username, password, salt ) loginRequest = function ( socket, params, username, password, salt )
local catch = function() socket:close() stdnse.debug1("loginRequest(): failed") end local creds
local try = nmap.new_try(catch) if params.authtype == AuthenticationType.MD5 then
local response, header = {}, {} creds = createMD5LoginHash(username, password, salt)
local status, data, len, tmp, _ elseif params.authtype == AuthenticationType.Plain then
local pos = 1 creds = password
elseif params.authtype == AuthenticationType.Success then
if ( params.authtype == AuthenticationType.MD5 ) then
local hash = createMD5LoginHash(username, password, salt)
data = string.pack( ">BI4z", MessageType.PasswordMessage, 40, hash )
try( socket:send( data ) )
elseif ( params.authtype == AuthenticationType.Plain ) then
local data
data = string.pack(">BI4z", MessageType.PasswordMessage, password:len() + 4, password)
try( socket:send( data ) )
elseif ( params.authtype == AuthenticationType.Success ) then
return true, nil return true, nil
else
return false, "Unexpected authentication type 0x" .. stdnse.tohex(params.authtype)
end end
data, response.params = "", {} local status, err = socket:send(string.pack(">BI4z", MessageType.PasswordMessage, #creds + 5, creds))
if not status then
return false, "Unable to send AuthRequest: " .. err
end
data = v3.readPacket(socket, data, 1) local data = v3.readPacket(socket)
pos, tmp = v3.processResponse(data, 1) local pos, tmp = v3.processResponse(data, 1)
-- this should contain the AuthRequest packet -- this should contain the AuthRequest packet
if tmp.type ~= MessageType.AuthRequest then if tmp.type ~= MessageType.AuthRequest then
return false, "Expected AuthRequest got something else" return false, "Expected AuthRequest; got something else"
end end
if not tmp.success then if not tmp.success then
return false, "Login failure" return false, "Login failure"
end end
local params = {}
repeat repeat
data = v3.readPacket(socket, data, pos) data = v3.readPacket(socket, data, pos)
pos, tmp = v3.processResponse(data, pos) pos, tmp = v3.processResponse(data, pos)
if ( tmp.type == MessageType.ParameterStatus ) then if ( tmp.type == MessageType.ParameterStatus ) then
table.insert(response.params, {name=tmp.key, value=tmp.value}) table.insert(params, {name=tmp.key, value=tmp.value})
end end
until pos >= data:len() or pos == -1 until pos >= data:len() or pos == -1
return true, response return true, {params}
end, end,
--- Sends a startup message to the server containing the username and database to connect to --- Sends a startup message to the server containing the username and database to connect to