Nmap Development mailing list archives

[NSE] script to check for weak SSH hostkeys


From: Sven Klemm <sven () c3d2 de>
Date: Sat, 18 Oct 2008 15:48:27 +0200

Hi everyone,

attached is a script to check for weak SSH hostkeys. I am not sure
about merging this into nmap trunk because it depends on data files
not part of nmap. You can get those files for example from here:
http://packages.debian.org/source/testing/openssh-blacklist
http://packages.debian.org/source/testing/openssh-blacklist-extra

It expects to find the blacklists in NMAPDATADIR with the following
name: openssh.blacklist.$algorithm-$bits

If no weak key lists are found the scripts prints a message if
verbosity >= 3: "No weak SSH key datafile found for RSA-2048."

This script depends on the SSH-hostkey script to put the SSH keys in
the nmap registry.

Here is an example:

:!./nmap --script SSH-hostkey,SSH-weak_key localhost -p22

Starting Nmap 4.76 ( http://nmap.org ) at 2008-10-18 15:29 CEST
Interesting ports on localhost (127.0.0.1):
PORT   STATE SERVICE
22/tcp open  ssh
|_ SSH Hostkey: 2048 f0:58:ce:f4:aa:a4:59:1c:8e:dd:4d:07:44:c8:25:11 (RSA)

Host script results:
|_ SSH weak key: 2048 f0:58:ce:f4:aa:a4:59:1c:8e:dd:4d:07:44:c8:25:11
(RSA)

Nmap done: 1 IP address (1 host up) scanned in 0.47 seconds

Any opinions about including this and whether it should be in the
default category?

Cheers,
Sven

-- 
Sven Klemm
http://cthulhu.c3d2.de/~sven/
--- checks SSH hostkeys for weak keys. May use blacklists from e.g.
-- http://packages.debian.org/source/testing/openssh-blacklist
-- http://packages.debian.org/source/testing/openssh-blacklist-extra
--
-- The blacklist filename is openssh.blacklist.$algorithm-$bits
--
--@output
--|_ SSH weak key: 2048 f0:58:ce:f4:aa:a4:59:1c:8e:dd:4d:07:44:c8:25:11 (RSA)

require("stdnse")
require("shortport")
require("datafiles")
require("ssh1")

id = "SSH weak key"
author = "Sven Klemm <sven () c3d2 de>"
description = "Show weak SSH hostkeys"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html";
categories = {"default","safe","vuln"}
runlevel = 2.0

hostrule = function( host ) return true end

local weak_keys = {}
local mutex = nmap.mutex(id)

--- read list of weak ssh key fingerprints
local read_datafile = function( db )
  mutex "lock"
  if not weak_keys[db] then
    local file = "openssh.blacklist." .. db

    local status, parsed = datafiles.parse_file( file, {["^[a-f0-9]+"]= "" })
    if status then
      weak_keys[db] = parsed
    else
      weak_keys[db] = {}
      stdnse.print_debug( 3, "No weak SSH key datafile found for %s.", db )
    end
  end
  mutex "done"
end

--- get value of the first entry of a table
local table_first_key = function( t )
  for key in pairs(t) do
    return key
  end
end

--- Derive the fingerprint length of a datafile from the first entry of the
-- table of this algorithm. This allows different fingerprint lengths for 
-- each key algorithm - key length pair.
--@param t table with fingerprints
local fingerprint_length = function( t )
  if table_first_key( t ) then return #table_first_key( t ) end
end

--- check whether this is a known weak key for a specific algorithm
local check_key = function( db, fp )
  if not weak_keys[db] then read_datafile( db ) end
  datalength = fingerprint_length( weak_keys[db] )
  if datalength then
    fp = fp:sub( #fp - datalength + 1, #fp )
    if weak_keys[db][fp] then
      return true
    end
  end
end

local supported_algorithms = {RSA=true,DSA=true}

--- lookup whether key is a known weak key
local known_weak_key = function( key )
  if supported_algorithms[key.algorithm] then
    local db = ("%s-%d"):format( key.algorithm, key.bits )
    return check_key( db, stdnse.tohex( key.fingerprint ) )
  else
    stdnse.print_debug( "Unsupported key algorithm: %s", key.algorithm )
    return false
  end
end

action = function( host )
  if not nmap.registry['SSH Hostkey'] then return end
  local output = {}
  local keys = nmap.registry['SSH Hostkey'][host.ip]

  for _, key in ipairs( keys ) do
    if known_weak_key( key ) then
      table.insert(output, ssh1.fingerprint_hex(key.fingerprint,key.algorithm,key.bits))
    end
  end

  if #output > 0 then
    return table.concat( output, '\n' )
  end
end


_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org

Current thread: