diff --git a/nselib/tls.lua b/nselib/tls.lua index 273064117..bd073368b 100644 --- a/nselib/tls.lua +++ b/nselib/tls.lua @@ -568,6 +568,18 @@ function record_read(buffer, i) j, b["session_id"] = bin.unpack("A" .. b["session_id_length"], buffer, j) j, b["cipher"] = bin.unpack(">S", buffer, j) j, b["compressor"] = bin.unpack("C", buffer, j) + -- Optional extensions + if j < len then + local num_exts + b["extensions"] = {} + j, num_exts = bin.unpack(">S", buffer, j) + for e = 0, num_exts do + local extcode, datalen + j, extcode = bin.unpack(">S", buffer, j) + extcode = find_key(EXTENSIONS, extcode) or extcode + j, b["extensions"][extcode] = bin.unpack(">P", buffer, j) + end + end -- Convert to human-readable form. b["protocol"] = find_key(PROTOCOLS, b["protocol"]) @@ -667,8 +679,9 @@ function client_hello(t) local extensions = {} if t["extensions"] ~= nil then -- Add specified extensions. - for _, extension in pairs(t["extensions"]) do + for extension, data in pairs(t["extensions"]) do table.insert(extensions, bin.pack(">S", EXTENSIONS[extension])) + table.insert(extensions, bin.pack(">P", data)) end end -- Extensions are optional diff --git a/scripts/tls-nextprotoneg.nse b/scripts/tls-nextprotoneg.nse index 3b5e0ee98..27e71face 100644 --- a/scripts/tls-nextprotoneg.nse +++ b/scripts/tls-nextprotoneg.nse @@ -5,6 +5,7 @@ local stdnse = require "stdnse" local table = require "table" local bin = require "bin" local os = require "os" +local tls = require "tls" description = [[ Enumerates a TLS server's supported protocols by using the next protocol negotiation extension. @@ -52,40 +53,18 @@ portrule = shortport.ssl local client_hello = function(host, port) local sock, status, response, err, cli_h - -- Craft Client Hello - -- Content Type: Client Handshake - cli_h = bin.pack(">C", 0x16) - -- Version: TLS 1.0 - cli_h = cli_h .. bin.pack(">S", 0x0301) - -- Length, fixed - cli_h = cli_h .. bin.pack(">S", 0x0037) - -- Handshake protocol - -- Handshake Type: Client Hello - cli_h = cli_h .. bin.pack(">C", 0x01) - -- Length, fixed - cli_h = cli_h .. bin.pack(">CS", 0x00, 0x0033) - -- Version: TLS 1.0 - cli_h = cli_h .. bin.pack(">S", 0x0301) - -- Random: epoch time - cli_h = cli_h .. bin.pack(">I", os.time()) - -- Random: random 28 bytes - cli_h = cli_h .. stdnse.generate_random_string(28) - -- Session ID length - cli_h = cli_h .. bin.pack(">C", 0x00) - -- Cipher Suites length - cli_h = cli_h .. bin.pack(">S", 0x0006) - -- Ciphers - cli_h = cli_h .. bin.pack(">S", 0xc011) - cli_h = cli_h .. bin.pack(">S", 0x0039) - cli_h = cli_h .. bin.pack(">S", 0x0004) - -- Compression Methods length - cli_h = cli_h .. bin.pack(">C", 0x01) - -- Compression Methods: null - cli_h = cli_h .. bin.pack(">C", 0x00) - -- Extensions length - cli_h = cli_h .. bin.pack(">S", 0x0004) - -- TLS NPN Extension - cli_h = cli_h .. bin.pack(">I", 0x33740000) + cli_h = tls.client_hello({ + ["protocol"] = "TLSv1.0", + ["ciphers"] = { + "TLS_ECDHE_RSA_WITH_RC4_128_SHA", + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_RC4_128_MD5", + }, + ["compressors"] = {"NULL"}, + ["extensions"] = { + ["next_protocol_negotiation"] = "", + }, + }) -- Connect to the target server sock = nmap.new_socket() @@ -120,53 +99,36 @@ end --@args response Response to parse. --@return results List of found protocols. local check_npn = function(response) - local results = {} - local shlength, npndata, protocol - - if not response then - stdnse.print_debug(SCRIPT_NAME .. ": Didn't get response.") - return results - end - -- If content type not handshake - if string.sub(response,1,1) ~= string.char(22) then - stdnse.print_debug(SCRIPT_NAME .. ": Response type not handshake.") - return results - end - -- If handshake protocol not server hello - if string.sub(response, 6, 6) ~= string.char(02) then - stdnse.print_debug(SCRIPT_NAME .. ": Handshake response not server hello.") - return results + local i, record = tls.record_read(response, 0) + if record == nil then + stdnse.print_debug("%s: Unknown response from server", SCRIPT_NAME) + return nil end - -- Get the server hello length - local _ - _, shlength = bin.unpack(">S", response, 4) - local serverhello = string.sub(response, 6, 6 + shlength) + if record.type == "handshake" and record.body.type == "server_hello" then + if record.body.extensions == nil then + stdnse.print_debug("%s: Server does not support TLS NPN extension.", SCRIPT_NAME) + return nil + end + local results = {} + npndata = record.body.extensions["next_protocol_negotiation"] + if npndata == nil then + stdnse.print_debug("%s: Server does not support TLS NPN extension.", SCRIPT_NAME) + return nil + end + -- Parse data + i = 0 + local protocol + while i < #npndata do + i, protocol = bin.unpack(">p", npndata, i) + table.insert(results, protocol) + end - -- If server didn't return TLS NPN extension - local npnextension, _ = string.find(serverhello, string.char(0x33) .. string.char(0x74)) - if not npnextension then - stdnse.print_debug(SCRIPT_NAME .. ": Server doesn't support TLS NPN extension.") - return results + return results + else + stdnse.print_debug("%s: Server response was not server_hello", SCRIPT_NAME) + return nil end - - -- Get NPN data length - local _, npnlen = bin.unpack(">S", serverhello:sub(npnextension + 2, npnextension + 3)) - if not npnlen then - return results - end - - npndata = serverhello:sub(npnextension + 4, npnextension + 4 + npnlen) - -- Parse data - local i, len = 1 - while i < #npndata do - len = npndata:byte(i) - protocol = npndata:sub(i+1, i+len) - table.insert(results, protocol) - i = i + len + 1 - end - - return results end action = function(host, port)