如何以编程方式检测可用的 ssh 身份验证类型?

发布于 2024-09-16 10:09:39 字数 232 浏览 5 评论 0 原文

我想编写一个监控插件来检查网络上的各个主机,以确保未启用密码或交互式 SSH 身份验证。也就是说,我需要编写以下代码:

  1. 连接到 SSH 端口。
  2. 枚举可用的身份验证方法。
  3. 验证是否只能进行基于密钥的身份验证。

使用 python 或 bourne sh 代码(使用 ssh)的方法对我来说是最有趣的,但其他语言、库或提示也很受欢迎。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

信愁 2024-09-23 10:09:39

我目前正在自己​​构建一个,但是,您可以使用 PreferredAuthentications 选项强制 ssh 输出(到 STDERR)支持的方法。这可以使用 grep/python/选择的语言轻松解析。

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).

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).
难以启齿的温柔 2024-09-23 10:09:39

RFC 4252 定义了 SSH 中的身份验证,内容如下:

身份验证方法通过其名称进行标识,如中所定义
[SSH-ARCH]。 “none”方法被保留,并且不得列为
支持。但是,它可能由客户端发送。服务器必须
始终拒绝此请求,除非要授予客户端访问权限
没有任何身份验证,在这种情况下,服务器必须接受
这个请求。发送此请求的主要目的是获取
来自服务器的支持方法列表。


因此,您可以发送身份验证请求来获取支持的列表。
然而,身份验证本身发生在某些较低级别的操作发生之后(密钥交换就是其中之一),因此您可能需要在 sh 脚本中编写 SSH 协议的一部分,这可能是一项艰巨的任务。

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.

枕梦 2024-09-23 10:09:39

您现在可以使用名为 nmap 内置 NSE 脚本>ssh-auth-methods 来做到这一点:

# 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

另外,有人做了一个类似的python3 脚本

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.

一袭水袖舞倾城 2024-09-23 10:09:39

这是基于 Eadwacer 答案的完整解决方案:

#!/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

重试是为了避免由于间歇性连接失败(例如超时等)而出现误报。

示例运行如下:

$ ./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: [email protected]: Permission denied (publickey,password).

调试信息发送到 stdout > 并且可以轻松静音以进行非交互式运行:

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

您必须将 1.1.1.12.2.2.2 替换为 SSH 服务器的 IP 地址或主机名。如果您想接受“publickey”之外的其他内容,还应该编辑 fgrep 中的字符串。

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: [email protected]: 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: [email protected]: 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".

魔法少女 2024-09-23 10:09:39

如果您需要 *nix 解决方案,您还可以连接到 OpenSSH 源。
如果 Windows 适合您 - 您还可以尝试一些 .NET 商业库,它们比 OpenSSH 源方便得多:)

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 :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文