1
0
mirror of https://github.com/nmap/nmap.git synced 2026-01-04 13:49:03 +00:00

Fix parsing of Set-Cookie headers. Closes #229

This commit is contained in:
dmiller
2015-11-02 16:02:50 +00:00
parent 20cb909e9b
commit 5e2bb7ad86
2 changed files with 64 additions and 89 deletions

View File

@@ -1,5 +1,8 @@
# Nmap Changelog ($Id$); -*-text-*-
o [NSE] [GH#229] Improve parsing in http.lua for multiple Set-Cookie headers in
a single response. [nnposter]
o [NSE] [GH#194] Add support for reading fragmented TLS messages to
ssl-enum-ciphers. [Jacob Gajek]

View File

@@ -651,6 +651,8 @@ local function parse_status_line(status_line, response)
return true
end
local parse_set_cookie -- defined farther down
-- Sets response.header and response.rawheader.
local function parse_header(header, response)
local pos
@@ -684,10 +686,22 @@ local function parse_header(header, response)
-- Set it in our table.
name = string.lower(name)
local value = table.concat(words, " ")
if response.header[name] then
response.header[name] = response.header[name] .. ", " .. table.concat(words, " ")
-- TODO: delay concatenation until return to avoid resource exhaustion
response.header[name] = response.header[name] .. ", " .. value
else
response.header[name] = table.concat(words, " ")
response.header[name] = value
end
-- Update the cookie table if this is a Set-Cookie header
if name == "set-cookie" then
local cookie, err = parse_set_cookie(value)
if cookie then
response.cookies[#response.cookies + 1] = cookie
else
-- Ignore any cookie parsing error
end
end
-- Next field, or end of string. (If not it's an error.)
@@ -701,8 +715,8 @@ local function parse_header(header, response)
return true
end
-- Parse the contents of a Set-Cookie header field. The result is an array
-- containing tables of the form
-- Parse the contents of a Set-Cookie header field.
-- The result is a table of the form
--
-- { name = "NAME", value = "VALUE", Comment = "...", Domain = "...", ... }
--
@@ -713,96 +727,64 @@ end
-- "HISTORICAL". Values need not be quoted, but if they start with a quote they
-- will be interpreted as a quoted string.
local function parse_set_cookie(s)
local cookies
local name, value
local _, pos
local _
cookies = {}
local cookie = {}
pos = 1
while true do
local cookie = {}
-- Get the NAME=VALUE part.
local pos = skip_space(s, 1)
pos, cookie.name = get_token(s, pos)
if not cookie.name then
return nil, "Can't get cookie name."
end
pos = skip_space(s, pos)
if s:sub(pos, pos) ~= "=" then
return nil, string.format("Expected '=' after cookie name \"%s\".", cookie.name)
end
pos = pos + 1
pos = skip_space(s, pos)
if s:sub(pos, pos) == "\"" then
pos, cookie.value = get_quoted_string(s, pos)
else
_, pos, cookie.value = s:find("([^; \t]*)", pos)
pos = pos + 1
end
if not cookie.value then
return nil, string.format("Can't get value of cookie named \"%s\".", cookie.name)
end
pos = skip_space(s, pos)
-- Get the NAME=VALUE part.
pos = skip_space(s, pos)
pos, cookie.name = get_token(s, pos)
if not cookie.name then
return nil, "Can't get cookie name."
end
pos = skip_space(s, pos)
if pos > #s or string.sub(s, pos, pos) ~= "=" then
return nil, string.format("Expected '=' after cookie name \"%s\".", cookie.name)
end
-- Loop over the attributes.
while s:sub(pos, pos) == ";" do
pos = pos + 1
pos = skip_space(s, pos)
if string.sub(s, pos, pos) == "\"" then
pos, cookie.value = get_quoted_string(s, pos)
else
_, pos, cookie.value = string.find(s, "([^;]*)[ \t]*", pos)
pos = pos + 1
end
if not cookie.value then
return nil, string.format("Can't get value of cookie named \"%s\".", cookie.name)
pos, name = get_token(s, pos)
if not name then
return nil, string.format("Can't get attribute name of cookie \"%s\".", cookie.name)
end
pos = skip_space(s, pos)
-- Loop over the attributes.
while pos <= #s and string.sub(s, pos, pos) == ";" do
if s:sub(pos, pos) == "=" then
pos = pos + 1
pos = skip_space(s, pos)
pos, name = get_token(s, pos)
if not name then
return nil, string.format("Can't get attribute name of cookie \"%s\".", cookie.name)
end
pos = skip_space(s, pos)
if pos <= #s and string.sub(s, pos, pos) == "=" then
pos = pos + 1
pos = skip_space(s, pos)
if string.sub(s, pos, pos) == "\"" then
pos, value = get_quoted_string(s, pos)
else
-- account for the possibility of the expires attribute being empty or improperly formatted
local last_pos = pos
if string.lower(name) == "expires" then
-- For version 0 cookies we must allow one comma for "expires".
_, pos, value = string.find(s, "([^,]*,[^;,]*)[ \t]*", pos)
else
_, pos, value = string.find(s, "([^;,]*)[ \t]*", pos)
end
-- account for the possibility of the expires attribute being empty or improperly formatted
if ( not(pos) ) then
_, pos, value = s:find("([^;]*)", last_pos)
end
pos = pos + 1
end
if not value then
return nil, string.format("Can't get value of cookie attribute \"%s\".", name)
end
if s:sub(pos, pos) == "\"" then
pos, value = get_quoted_string(s, pos)
else
value = true
_, pos, value = s:find("([^;]*)", pos)
value = value:match("(.-)[ \t]*$")
pos = pos + 1
end
cookie[name:lower()] = value
pos = skip_space(s, pos)
if not value then
return nil, string.format("Can't get value of cookie attribute \"%s\".", name)
end
else
value = true
end
cookies[#cookies + 1] = cookie
if pos > #s then
break
end
if string.sub(s, pos, pos) ~= "," then
return nil, string.format("Syntax error after cookie named \"%s\".", cookie.name)
end
pos = pos + 1
cookie[name:lower()] = value
pos = skip_space(s, pos)
end
return cookies
return cookie
end
-- Read one response from the socket <code>s</code> and return it after
@@ -818,6 +800,7 @@ local function next_response(s, method, partial)
["status-line"]=nil,
header={},
rawheader={},
cookies={},
body=""
}
@@ -845,17 +828,6 @@ local function next_response(s, method, partial)
end
response.body = body
-- We have the Status-Line, header, and body; now do any postprocessing.
response.cookies = {}
if response.header["set-cookie"] then
response.cookies, err = parse_set_cookie(response.header["set-cookie"])
if not response.cookies then
-- Ignore a cookie parsing error.
response.cookies = {}
end
end
return response, partial
end