11

I'd like to write a monitoring plugin that checks various hosts on my network to make sure that password or interactive SSH authentication is not enabled. That is, I need to write code that:

  1. Connects to an SSH port.
  2. Enumerates available authentication methods.
  3. Verifies that only key based authentication is possible.

Methods using either python or bourne sh code (using ssh) is most interesting to me, but other languages, libraries or hints are appreciated too.

Stef
  • 6,729
  • 4
  • 34
  • 26

5 Answers5

16

I'm currently building one myself, however, you can force ssh to output (to STDERR) the supported methods by using the PreferredAuthentications option. This can easily be parsed with grep/python/language of choice.

HostA$ ssh -o PreferredAuthentications=none HostB
Permission denied (publickey,gssapi-with-mic).
HostA$ ssh -o PreferredAuthentications=none HostC
Permission denied (publickey,gssapi-with-mic,password,keyboard-interactive,hostbased).
Eadwacer
  • 1,138
  • 7
  • 10
  • Nice and simple. Thanks. Would be interested in your code if it ends up being open source... – Stef Aug 31 '10 at 23:08
  • Unfortunately, this particular block of code won't be able to be open-sourced. Sorry. That's why I wanted to share the initial thoughts before I need to stop talking about it. – Eadwacer Sep 01 '10 at 00:33
7

RFC 4252, which defines authentication in SSH, says the following:

Authentication methods are identified by their name, as defined in [SSH-ARCH]. The "none" method is reserved, and MUST NOT be listed as supported. However, it MAY be sent by the client. The server MUST always reject this request, unless the client is to be granted access without any authentication, in which case, the server MUST accept this request. The main purpose of sending this request is to get the list of supported methods from the server.

So you can send a request for none authentication to get the list of supported ones. However, authentication itself occurs after certain lower-level actions take place (key exchange is one of them) so you might need to write a part of SSH protocol in sh script, which is probably a non-trivial task.

Eugene Mayevski 'Callback
  • 45,135
  • 8
  • 71
  • 121
1

You can now use the nmap built-in NSE script called ssh-auth-methods to do this:

# nmap -p 22 --script ssh-auth-methods 192.168.1.2

Starting Nmap 7.60 ( https://nmap.org ) at 2017-12-26 00:56 GMT
Nmap scan report for 192.168.1.2
Host is up (0.027s latency).

PORT   STATE SERVICE
22/tcp open  ssh
| ssh-auth-methods:
|   Supported authentication methods:
|     publickey
|_    keyboard-interactive
MAC Address: AA:BB:CC:DD:EE:FF (Apple)

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

In addition, someone has made a similar python3 script.

not2qubit
  • 14,531
  • 8
  • 95
  • 135
0

Here is a complete solution based on Eadwacer's answer:

#!/bin/bash
set -u

for ip in 1.1.1.1 2.2.2.2; do
        ok=0
        for try in {1..3}; do
                echo "$ip: try #$try"
                OUT="$(timeout -s KILL 30s ssh -o PreferredAuthentications=none "root@$ip" 2>&1)"
                RES="$(echo "$OUT" | fgrep ': Permission denied (publickey).')"
                if [ "$RES" != "" ]; then
                        ok=1
                        echo '  Auth is OK.'
                        break
                else
                        echo '  Auth is BAD.'
                fi
                sleep 5
        done
        if [ "$ok" -eq 1 ]; then
                echo "$ip: final result: OK"
        else
                echo "$ip: final result: FAIL" >&2
                echo "  ssh output: $OUT" >&2
        fi
done

The retries are there to avoid false-positives due to intermittent connection failures like timeouts, etc.

Here is how a sample run looks like:

$ ./ssh-assert-key-auth-only.sh
1.1.1.1: try #1
  Auth is OK.
1.1.1.1: final result: OK
2.2.2.2: try #1
  Auth is BAD.
2.2.2.2: try #2
  Auth is BAD.
2.2.2.2: try #3
  Auth is BAD.
2.2.2.2: final result: FAIL
  ssh output: root@2.2.2.2: Permission denied (publickey,password).

Debug info is sent to stdout and can easily be muted for non-interactive runs:

$ ./ssh-assert-key-auth-only.sh >/dev/null
2.2.2.2: final result: FAIL
  ssh output: root@2.2.2.2: Permission denied (publickey,password).

You have to replace 1.1.1.1 and 2.2.2.2 with the IP addresses or hostnames of your SSH servers. The string in fgrep should also be edited if you want to accept something else than just "publickey".

famzah
  • 1,462
  • 18
  • 21
-1

If you need *nix solution, you can also hook into OpenSSH sources. If Windows suitable for you - you can also try some .NET commercial libraries, they are much handier than OpenSSH sources :)

Nickolay Olshevsky
  • 13,706
  • 1
  • 34
  • 48