--- Shows SSL certificate -- --@output --| SSL cert: benkstein.net (*.benkstein.net, toidinamai.de, *.toidinamai.de, wiki.c3d2.de, www.c3d2.de)\n --| Issuer: CAcert Inc., Expires: Oct 5 08:37:00 2008 GMT\n --|_ SHA1: d5:a9:b0:0b:d4:e6:a3:c0:46:83:c6:2a:4b:2b:bc:08:2f:21:9c:fe\n id = "SSL cert" author = "Sven Klemm " description = "Show SSL certificate" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"safe","default"} require("shortport") require("stdnse") require("bin") require("snmp") -- openssl is required for this script if pcall(require,"openssl") then else action = function() stdnse.print_debug( 3, "Skipping %s script because OpenSSL is missing.", id ) end end local rule_helper = shortport.service({'https','imaps','pop3s'}) portrule = function( host, port ) return rule_helper(host,port) or port.version.service_tunnel == "ssl" end local CIPHER local SSLv3 = {} SSLv3.parse_record_layer = function( data ) local offset, content_type, major, minor, length = bin.unpack(">CCCS", data ) local payload = data:sub(offset,offset+length-1) local remainder = data:sub(offset+length,#data) local record = {content_type=content_type,major=major,minor=minor,length=length,payload=payload} return record, remainder end SSLv3.parse_handshake = function( data ) local offset, content_type, hi_len, length = bin.unpack(">CCS", data ) length = length + 65536 * hi_len local payload = data:sub(offset,offset+length-1) local remainder = data:sub(offset+length,#data) local handshake = {type=content_type,length=length,payload=payload} return handshake, remainder end --- returns table of strings containing the certificates SSLv3.parse_certificate = function( data ) local offset, hi_len, length = bin.unpack(">CS", data ) local packet_length = length + 65536 * hi_len offset, hi_len, length = bin.unpack(">CS", data, offset ) local cert_length = length + 65536 * hi_len local cert = data:sub(offset,offset+cert_length-1) return cert end SSLv3.unpack_layer = function( data ) local _,hi_len,length _, hi_len, length = bin.unpack( ">CS", data ) length = length + 65536 * hi_len return data:sub( 4, 3+length ) end --- put certificate in the nmap registry for usage by other scripts --@param host nmap host table --@param cert host key table local add_cert_to_registry = function( host, cert ) nmap.registry[id] = nmap.registry[id] or {} nmap.registry[id][host.ip] = nmap.registry[id][host.ip] or {} table.insert( nmap.registry[id][host.ip], cert ) end action = action or function(host, port) local output = {} local client_hello = bin.pack("H", "16 03 00 00 52 01 00 00 4e 03 00 48 ef 88 79 02 8d 5f 2a 25 c6 cb 67 eb 3f 13 57 fa 0a 4e 1e 08 83 00 6e 8b 17 e9 4e d7 e0 11 2f 00 00 26 00 39 00 38 00 35 00 16 00 13 00 0a 00 33 00 32 00 2f 00 05 00 04 00 15 00 12 00 09 00 14 00 11 00 08 00 06 00 03 02 01 00") local sock = nmap.new_socket() local catch = function() socket:close() end local try = nmap.new_try(catch) try(sock:connect( host.ip, port.number )) try(sock:send( client_hello )) data = try(sock:receive_bytes(1)) local server_hello, remainder = SSLv3.parse_record_layer( data ) if SSLv3.parse_handshake( server_hello.payload ).type == 2 then local certificate = SSLv3.parse_record_layer( remainder ) while certificate.length > #certificate.payload do data = try(sock:receive_bytes(1)) remainder = remainder .. data certificate = SSLv3.parse_record_layer( remainder ) end local handshake = SSLv3.parse_handshake( certificate.payload ) if handshake.type == 11 then local raw_cert = SSLv3.parse_certificate( handshake.payload ) local cert = openssl.X509_parse_certificate( raw_cert ) add_cert_to_registry( host, cert ) local altnames = {} if cert.extensions.subjectAltName then -- extract altnames local _, altname = snmp.decode( cert.extensions.subjectAltName ) for _,value in pairs( altname ) do if type( value ) == "string" and value ~= cert.subject.commonName then table.insert( altnames, value ) end end end local domains = cert.subject.commonName if #altnames > 0 then domains = ("%s (%s)"):format( domains, table.concat(altnames,', ')) end table.insert( output, domains ) table.insert( output, (" Issuer: %s, Expires: %s"):format(cert.issuer.organizationName,cert.validity.notAfter)) table.insert( output, (" SHA1: %s"):format(stdnse.tohex(cert.sha1_hash,{separator=":"}))) end end sock:close() if #output > 0 then return table.concat( output, "\n" ) end end CIPHER = { TLS_RSA_WITH_AES_256_CBC_SHA = string.byte(0x00,0x35), }