
任意のポートをスキャンし、SSHバナーを検証し、認証方式、脆弱な暗号化、Terrapin脆弱性、およびホスト間・CIDR範囲にわたるホストキーの再利用を監査する、並列SSHサービス検出・セキュリティ監査ツール。
ネットワーク上のすべてのSSHサービスを検出し、基準を満たしているかを判定し、変更があった場合に通知します。
sshfinder は、依存関係のない単一のPythonファイルです。CIDRレンジを指定すると、ポート22だけでなく、実際にリッスンしている場所のSSHを検出し、それぞれが本当にSSHを話していることを確認し、暗号化の状態を評価し、ポリシーに違反するものがある場合は非ゼロの終了コードを返します。
ほとんどのチームは、自社のSSH資産に関する3つの質問に答えられません。
既存のツールはそれぞれ一部に答え、そこで止まります。
| ツール | SSHを検出 | 評価 | フリート全体 |
|---|---|---|---|
nmap | はい | 浅い(NSEスクリプト経由) | はい |
ssh-audit | いいえ — 1台のホストを指定する | 深い | いいえ |
masscan / zmap | インターネット規模 | いいえ | はい |
sshfinder | はい | はい | はい |
そのギャップ — 検出と評価と判定を1つの成果物で実現すること — が、このツールが存在する目的です。すでに知っている1台のホストだけを監査する必要がある場合は、ssh-audit を使用してください。単一のサービスに対しては、これよりも深く調査します。
ペネトレーションテスターは監査とSOCKSピボットが有用だと感じるでしょうが、このツールは一度きりのエンゲージメントではなく、所有する資産に対して同じスキャンを繰り返し実行することを目的としています。
git clone https://github.com/kabiri-labs/sshfinder.git
cd sshfinder
python sshfinder.py 10.0.0.0/24 -p 22,2222
インストール不要、依存関係なし。Python 3.9+ が必要です。
3つのコマンドで実行できる3つの機能:
# 1. インベントリ — どのSSHが存在するか?
python sshfinder.py 10.0.0.0/24 --audit --format csv -o ssh-inventory.csv
# 2. 判定 — 基準を満たしているか? (満たしていない場合は終了コード3)
python sshfinder.py 10.0.0.0/24 -p 22,2222 --policy baseline
# 3. ドリフト — 昨夜から何が変わったか?
python sshfinder.py 10.0.0.0/24 -p 22,2222 --baseline yesterday.json \
--fail-on-drift
全65535ポートのスキャンがデフォルトです。非標準ポートのSSHサービスこそ、誰も記録していないものだからです。すべてのオープンポートにラベルが付けられるため、オープンポートが暗黙的にSSHとしてカウントされることはありません。
=== 10.0.0.5 ===
open: 10.0.0.5:22 [SSH], 10.0.0.5:8080 [not ssh]
SSH 10.0.0.5:22 (SSH-2.0-OpenSSH_7.4)
確認は、ワイヤ上の最初のバイトを見るのではなく、実際のRFC 4253識別交換です。最初に法的なバナーを出力するサーバー、クライアントが自己識別するのを待つサーバー、またはバナーがTCPセグメントに分割されて到着するサーバーは、すべて正しく認識されます — これらはそれぞれ、単純な実装では偽陰性になります。
各サービスの全体像を把握するには --audit を追加します:
SSH 10.0.0.5:22 (SSH-2.0-OpenSSH_7.4)
host key: ssh-ed25519 SHA256:T/ZM4jOL4amTsO5K3AaCdg2...
auth: publickey, password [!] password auth enabled
[!] Terrapin (CVE-2023-48795): VULNERABLE
[!] weak ciphers: aes128-cbc
aes128-cbc [weak]: CBC mode is vulnerable to the SSH plaintext-recovery attack (CVE-2008-5161) and, …
共有SSHホストキー (共有/クローンされたホストの可能性):
SHA256:T/ZM4jOL4amTsO5K3AaCdg2...
-> 10.0.0.5:22, 10.0.0.9:22
最後のブロックは知っておく価値があります。マシン間で再利用されるホストキーは、通常、クローンされたVMまたは共有イメージを意味し、1つのホストが侵害されると、すべてのホストのIDが侵害されることを意味します。
OpenSSH 10.0 は mlkem768x25519-sha256 をデフォルトの鍵交換にしました。10.1 は、従来のセッションが store now, decrypt later キャプチャに対して開かれていると警告しています。--pq-report は、KEXINITのみを使用してフリートレベルの質問に直接答えます — サードパーティのライブラリは不要です:
python sshfinder.py 10.0.0.0/24 -p 22,2222 --pq-report
Post-quantum readiness:
1/3 service(s) negotiate post-quantum key exchange with a current client
[!] no PQ key exchange offered (1):
10.0.0.2:22
[!] pre-standard PQ only (1) - looks post-quantum but is not:
10.0.0.3:22
2 service(s) exposed to store-now-decrypt-later capture; upgrade to OpenSSH 9.0+
pre-standard カテゴリは、人々を驚かせるものです。[email protected] またはKyberドラフトをアドバタイズするサーバーは、アルゴリズムダンプではポスト量子に見えますが、OpenSSHは2020年にその撤回されたパラメータセットを削除しました — そのため、現在のクライアントは共通のメソッドを見つけられず、従来の暗号にフォールバックします。準備完了としてカウントされると、まったく見ないよりも悪いことになります。
レポートは問題を説明します。ポリシーは問題を主張し、ビルドを失敗させることができます:
python sshfinder.py 10.0.0.0/24 -p 22,2222 --policy baseline; echo "exit $?"
Policy 'baseline':
No password login, no Terrapin exposure, no weak algorithms.
1/3 service(s) pass
[FAIL] 1 service(s):
10.0.0.3:22
- password_auth: password login accepted: publickey, password
- terrapin: vulnerable to Terrapin (CVE-2023-48795)
- post_quantum (warn): post-quantum readiness is absent, ready required
[warn] 1 service(s):
10.0.0.2:22
- post_quantum (warn): post-quantum readiness is absent, ready required
exit 3
3つのポリシーが組み込まれています — baseline、strict、pq — ディストリビューションではなく、強制する結果にちなんで名付けられています。ルールには fail または warn の重大度があり、--fail-on がどのゲートを決定するかを決めるため、チームはまず警告としてより厳しい基準を採用し、後で何も編集せずに昇格させることができます。
独自のポリシーをJSONで記述します:
{
"name": "house-rules",
"description": "What we expect of every SSH service.",
"rules": [
{"check": "password_auth", "severity": "fail"},
{"check": "terrapin", "severity": "fail"},
{"check": "post_quantum", "require": "ready", "severity": "warn"},
{"check": "forbid", "field": "ciphers",
"algorithms": ["3des-cbc", "arcfour"], "severity": "fail"},
{"check": "require", "field": "kex_algorithms",
"algorithms": ["curve25519-sha256"], "severity": "fail"}
]
}
チェック: password_auth、terrapin、weak_algorithms、post_quantum(require: ready、legacy、absent 付き)、および kex_algorithms、host_key_algorithms、ciphers、macs の field に対する forbid / require。
その他はすべて、スキャン開始前にポリシーがロードされるときにハードエラーになります。 理解できないルールを静かにスキップするゲートは、ゲートがないよりも悪いです。実行はグリーンになり、誰もチェックが実行されなかったことを知りません。
$ sshfinder 10.0.0.0/24 --policy house.json
sshfinder: error: rule 1: unknown check 'pasword_auth'
(known: forbid, password_auth, post_quantum, require, terrapin, weak_algorithms)
昨夜のレポートに対して毎晩実行し、移動したものだけを確認します:
# Nightly, in cron:
python sshfinder.py 10.0.0.0/24 -p 22,2222 --audit --json -o today.json
python sshfinder.py 10.0.0.0/24 -p 22,2222 --baseline yesterday.json \
--fail-on-drift
Baseline drift (vs yesterday.json):
[alert] 2 change(s):
10.0.0.5:22 SHA256:T/ZM4jO... -> SHA256:9aKm2Qx...; expected only after a rebuild or key rotation
10.0.0.3:22 password login is now accepted
[added] 1 change(s):
10.0.0.9:2222 new SSH service (SSH-2.0-OpenSSH_9.6)
[improved] 1 change(s):
10.0.0.7:22 post-quantum readiness rose from absent to ready
変更されたホストキー は、ここで最も重要なシグナルです — 再構築またはキーローテーション後にのみ期待され、それ以外の場合は調査する価値があります。
--fail-on-drift をゲートするのは alert のみです。廃止されたホストは通常のチャーンであり、それで毎晩のジョブを失敗させると、誰もが結果を無視するように訓練されてしまいます。
比較は、変更を発明しないように注意しています。どちらのスキャンも測定しなかったフィールドが変更されたとして報告されることはなく、両方のスキャンに存在するホストのみが比較され、ホストキーフィンガープリントを保持するベースラインにより、このスキャンもディーププローブを実行します — そのため、浅い再スキャンがすべてのキーが消えたと読まれることはありません。
--format text|json|sarif|csv、オプションで -o を使用してファイルに書き込めます。
csv — 確認されたSSHサービスごとに1行。資産インベントリが実際にソートおよびフィルタリングされる形式です。json — ネイティブレポートであり、--baseline の入力形式です。sarif — OASISスキーマに対して検証されたSARIF 2.1.0。検出結果は host:port 論理ロケーションに固定され、安定したフィンガープリントを保持するため、コンシューマーは毎晩の実行で同じ検出結果を追跡し、毎回新しいアラートを開くことはありません。--stream — 各ポートが開き、各サービスが確認されるたびにフラッシュされる改行区切りのJSONイベント。スキャンがまだ実行されている間にパイプラインが最初の結果に基づいて行動できます:python sshfinder.py 10.0.0.0/24 --stream -q | jq -c 'select(.event=="ssh")'
{"event":"ssh","elapsed":0.164,"host":"10.0.0.5","port":22,"banner":"SSH-2.0-OpenSSH_9.6"}
{"event":"ssh","elapsed":0.881,"host":"10.0.0.9","port":2222,"banner":"SSH-2.0-dropbear"}
SARIFとGitHubコードスキャニングについて。 SARIF結果には空でないアーティファクトロケーションが必要です。そうしないと
upload-sarifがファイルを拒否するため、論理ロケーションとともに合成ssh://host:portURIが出力されます。これはリポジトリ内のファイルに解決されないため、アラートはコードアンカーなしで表示されます。これは一般的なセキュリティツール向けのSARIFとして扱ってください — VS Code SARIFビューアー、Azure DevOps、アーカイブ — 差分に注釈を付ける方法としては扱わないでください。
ポリシーとドリフト機能の要点なので、正確に述べる価値があります: