-- -*- mode: lua -*- -- vim: set filetype=lua : description = [[ This script checks whether the SSL certificate used by a host has a fingerprint that matches the ones in a database. The database checked is currently from LittleBlackBox 0.1, but any file of fingerprints will serve just as well. One suggestion is the list of the weak Debian OpenSSL keys. ]] --- -- @usage -- nmap --script ssl-known-key -p 443 -- -- @args ssl-known-key.fingerprintfile Specify a different file to read -- fingerprints from. -- -- @output -- PORT STATE SERVICE REASON -- 443/tcp open https syn-ack -- |_ssl-known-key: 00:28:E7:D4:9C:FA:4A:A5:98:4F:E4:97:EB:73:48:56:07:87:E4:96 is in the database with reason Little Black Box 0.1. author = "Mak Kolybabi" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"safe", "discovery", "vuln"} require("bin") require("nmap") require("shortport") require("stdnse") local FINGERPRINT_FILE = "ssl-fingerprints" local get_fingerprints = function(path) local pretty = function(key) local s = key:sub(1, 2) for i = 3, 40, 2 do s = s .. ":" .. key:sub(i, i + 1) end return s:upper() end -- Check registry for cached fingerprints. if nmap.registry.ssl_fingerprints then stdnse.print_debug(2, "Using cached SSL fingerprints.") return true, nmap.registry.ssl_fingerprints end -- Attempt to resolve path if it is relative. local full_path = nmap.fetchfile("nselib/data/" .. path) if not full_path then full_path = path end stdnse.print_debug(2, "Loading SSL fingerprints from %s.", full_path) -- Open database. local file = io.open(full_path, "r") if not file then return false, "Failed to open file " .. full_path end -- Parse database. local section = nil local fingerprints = {} for line in file:lines() do line = line:gsub("#.*", "") line = line:gsub("^%s*", "") line = line:gsub("%s*$", "") if line ~= "" then if line:sub(1,1) == "[" then -- Start a new section. line = line:sub(2, #line - 1) stdnse.print_debug(4, "Starting new section %s.", line) section = line elseif section ~= nil then -- Add fingerprint to section. line = pretty(line) stdnse.print_debug(4, "Added key %s to database.", line) fingerprints[line] = section else -- Key found outside of section. stdnse.print_debug(1, "Key %s is not in a section.", pretty(line)) end end end -- Close database. file:close() -- Cache fingerprints in registry for future runs. nmap.registry.ssl_fingerprints = fingerprints return true, fingerprints end portrule = shortport.ssl action = function(host, port) -- Get script arguments. local path = stdnse.get_script_args("ssl-known-key.fingerprintfile") or FINGERPRINT_FILE local status, result = get_fingerprints(path) if not status then stdnse.print_debug(1, result) return end local fingerprints = result -- Connect to host. local sock = nmap.new_socket() local status, err = sock:connect(host, port, "ssl") if not status then stdnse.print_debug(1, "Failed to connect: %s", err) return end -- Get SSL certificate. local cert = sock:get_ssl_certificate() sock:close() if not cert:digest("sha1") then stdnse.print_debug(2, "Certificate does not have a SHA-1 fingerprint.") return end -- Check SSL fingerprint against database. local fingerprint = stdnse.tohex(cert:digest("sha1"), {separator=":", group=2}):upper() local section = fingerprints[fingerprint] if not section then stdnse.print_debug(2, "%s was not in the database.", fingerprint) return end return "Found in " .. section .. " (certificate hash: " .. fingerprint .. ")" end