diff --git a/CHANGELOG b/CHANGELOG index eecc9b83d..0feded5d9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] [GH#194] Add support for reading fragmented TLS messages to + ssl-enum-ciphers. [Jacob Gajek] + o [Ncat] [GH#193] Fix Ncat listen mode over Unix sockets (named pipes) on OS X. This was crashing with the error: Ncat: getnameinfo failed: Undefined error: 0 QUITTING. diff --git a/nselib/tls.lua b/nselib/tls.lua index 17041c7cd..88aa72d81 100644 --- a/nselib/tls.lua +++ b/nselib/tls.lua @@ -1108,12 +1108,14 @@ end --- -- Read a SSL/TLS record --- @param buffer The read buffer --- @param i The position in the buffer to start reading +-- @param buffer The read buffer +-- @param i The position in the buffer to start reading +-- @param fragment Message fragment left over from previous record (nil if none) -- @return The current position in the buffer -- @return The record that was read, as a table -function record_read(buffer, i) +function record_read(buffer, i, fragment) local b, h, len + local add = 0 ------------ -- Header -- @@ -1148,6 +1150,14 @@ function record_read(buffer, i) return i, nil end + -- Adjust buffer and length to account for message fragment left over + -- from last record. + if fragment then + add = #fragment + len = len + add + buffer = buffer:sub(1, j - 1) .. fragment .. buffer:sub(j, -1) + end + -- Convert to human-readable form. ---------- @@ -1155,12 +1165,11 @@ function record_read(buffer, i) ---------- h["body"] = {} - while j < len do + + while j <= len do -- RFC 2246, 6.2.1 "multiple client messages of the same ContentType may -- be coalesced into a single TLSPlaintext record" - -- TODO: implement reading of fragmented records b = {} - table.insert(h["body"], b) if h["type"] == "alert" then -- Parse body. j, b["level"] = bin.unpack("C", buffer, j) @@ -1169,7 +1178,16 @@ function record_read(buffer, i) -- Convert to human-readable form. b["level"] = find_key(TLS_ALERT_LEVELS, b["level"]) b["description"] = find_key(TLS_ALERT_REGISTRY, b["description"]) + + table.insert(h["body"], b) elseif h["type"] == "handshake" then + + -- Check for message fragmentation. + if len - j < 3 then + h.fragment = buffer:sub(j, len) + return len + 1 - add, h + end + -- Parse body. j, b["type"] = bin.unpack("C", buffer, j) local msg_end @@ -1179,6 +1197,12 @@ function record_read(buffer, i) -- Convert to human-readable form. b["type"] = find_key(TLS_HANDSHAKETYPE_REGISTRY, b["type"]) + -- Check for message fragmentation. + if msg_end > len + 1 then + h.fragment = buffer:sub(j - 4, len) + return len + 1 - add, h + end + if b["type"] == "server_hello" then -- Parse body. j, b["protocol"] = bin.unpack(">S", buffer, j) @@ -1226,18 +1250,21 @@ function record_read(buffer, i) stdnse.debug2("Unknown handshake message type: %s", b["type"]) j, b["data"] = bin.unpack("A" .. msg_end - j, buffer, j) end + + table.insert(h["body"], b) elseif h["type"] == "heartbeat" then j, b["type"], b["payload_length"] = bin.unpack("C>S", buffer, j) j, b["payload"], b["padding"] = bin.unpack("PP", buffer, j) + table.insert(h["body"], b) else stdnse.debug1("Unknown message type: %s", h["type"]) end end -- Ignore unparsed bytes. - j = len+1 + j = len + 1 - return j, h + return j - add, h end --- diff --git a/scripts/ssl-enum-ciphers.nse b/scripts/ssl-enum-ciphers.nse index cf8044558..5b439c958 100644 --- a/scripts/ssl-enum-ciphers.nse +++ b/scripts/ssl-enum-ciphers.nse @@ -149,20 +149,22 @@ end local function get_record_iter(sock) local buffer = "" local i = 1 + local fragment return function () local record - i, record = tls.record_read(buffer, i) + i, record = tls.record_read(buffer, i, fragment) if record == nil then local status, err status, buffer, err = tls.record_buffer(sock, buffer, i) if not status then return nil, err end - i, record = tls.record_read(buffer, i) + i, record = tls.record_read(buffer, i, fragment) if record == nil then return nil, "done" end end + fragment = record.fragment return record end end