OpenSSH CVE-2024-6387 対策ホストスキャンスクリプト (Azure)

## 概要

OpenSSH の緊急度の高い脆弱性 CVE-2024-6387 (regreSSHion) が公開されました。リモートコード実行の可能性があるため、影響を受けるサーバーでは早期の OpenSSH のアップデートが推奨されており、対応に追われている方も多いと思います。

https://blog.qualys.com/vulnerabilities-threat-research/2024/07/01/regresshion-remote-unauthenticated-code-execution-vulnerability-in-openssh-server

取り急ぎ対処が必要なのは、脆弱性があるバージョンのOpenSSHを実行している外部に公開されたホストです。OpenSSH に含まれる [ssh-keyscan] コマンドを使えば、ホストで実行中の OpenSSH サーバーのバージョンをホストの外部からでも調べることができます。

今回は、AuzreのパブリックIPアドレスのリソースをリストアップして、それらのホストに実際にアクセスしてOpenSSHのバージョンを調べるBashスクリプトを作ってみましたので紹介します。

## スクリプト

`list.sh` と `scan.sh` の2つのスクリプトにより構成されます。実行には Azure CLI (az コマンド) が必要です。ホストのスキャンには [ssh-keyscan] コマンドを使用します。

[ssh-keyscan]: https://man.openbsd.org/ssh-keyscan.1
 
```bash:list.sh
#!/bin/bash
tab=$'\t'
az account list -o tsv --query "[].[id,name]" |
while read id name; do
    az network public-ip list --subscription $id -o tsv --query "[].[ipAddress,id]" |
    while read ip id; do
        IFS=/ read a b c d e f g h i j <<< $id
        echo "$ip$tab$name($c)$tab$e$tab$i"
    done
done
```

```bash:scan.sh
#!/bin/bash
tab=$'\t'
while read ip rem; do
    scan=$(test "$ip" != None && ssh-keyscan $ip 2>&1 | head -1)
    echo "$ip$tab${rem%$tab#*}$tab# ${scan#\# }"
done
```

## 使用法

`az login` でAzureの認証を済ませたあと、 `list.sh` でパブリックIPアドレスリソースのリストを生成し、次に  `scan.sh` でそのIPアドレスのホストをスキャンします。OpenSSHサーバーに接続できたホストについてそのバージョンを出力します。

```console
$ az login
$ ./list.sh >hosts.txt
$ ./scan.sh <hosts.txt >result.txt
```

2つのスクリプトをパイプでつなげて実行することも可能です。
```console
$ az login
$ ./list.sh | ./scan.sh >result.txt
```

出力ファイル `result.txt` は次回の `scan.sh` 実行の入力にすることもできます。なお入力と出力をうっかり同じファイル名にすると中身が消えてしまいますので注意してください。

```console
$ ./scan.sh <result.txt >result.txt # result.txt が空になる
```

## スクリプト出力の説明

`list.sh` を実行すると、次のような各行ごとにタブ文字区切りのテキストを出力します。
```
<IPアドレス> <サブスクリプション名(サブスクリプションID)> <リソースグループ名> <リソース名>
```

以下は出力例です。
```text
20.37.101.104   Azure Sub A(aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)       VM_almalinux9   almalinux9-ip
172.207.241.131 Azure Sub A(aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)       VM_freebsd141   freebsd141-ip
52.156.56.199   Azure Sub A(aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)       VM_rhel94       rhel94-ip
20.37.97.17     Azure Sub A(aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)       VM_ubuntu2204   ubuntu2204-ip
104.41.185.6    Azure Sub A(aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)       VM_ubuntu2404   ubuntu2404-ip
None    Azure Sub B(bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb)       VM_dxdt1        dxdt1
20.37.106.122   Azure Sub B(bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb)       VM_coder1       coder1
```

IPアドレスが `None` となることがありますが、これは動的なパブリックIPアドレス(Basic)のリソースで仮想マシンが停止状態であるためにIPアドレスが割り当てられていない場合などが該当します。

`scan.sh` は入力ファイルの各行の最初のフィールドをIPアドレスとして [ssh-keyscan] コマンドを実行し、その結果で各行の末尾の `#` 文字以降を置き換えて出力します。`#` 文字がなければ追加します。

以下は出力例です。
```text
20.37.101.104   Azure Sub A(aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)       VM_almalinux9   almalinux9-ip   # 20.37.101.104:22 SSH-2.0-OpenSSH_8.7
172.207.241.131 Azure Sub A(aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)       VM_freebsd141   freebsd141-ip   # 172.207.241.131:22 SSH-2.0-OpenSSH_9.7 FreeBSD-20240701
52.156.56.199   Azure Sub A(aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)       VM_rhel94       rhel94-ip       # 52.156.56.199:22 SSH-2.0-OpenSSH_8.7
20.37.97.17     Azure Sub A(aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)       VM_ubuntu2204   ubuntu2204-ip   # 20.37.97.17:22 SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.10
104.41.185.6    Azure Sub A(aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)       VM_ubuntu2404   ubuntu2404-ip   # 104.41.185.6:22 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.3
None    Azure Sub B(bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb)       VM_dxdt1        dxdt1   # 
20.37.106.122   Azure Sub B(bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb)       VM_coder1       coder1  # 
```

スキャンに成功したホストでは `# SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.11` のような結果文字列となりOpenSSHのバージョンやディストリビューション情報がわかります。

Linuxの場合はDebian系であれば対処済みのパッケージバージョンかどうかもわかりますが、RHEL系ではその情報は得られないようです。

結果文字列が空の場合は次のような理由が考えられます。

- 仮想マシンが起動していない
- スクリプトを実行したホストから標準SSHポートに到達できない

空であることは対策が完了したといえるわけではないことに注意してください。 host.sh の出力のアドレスリストは保存しておき、`scan.sh` を時間や場所を変えて実行することをおすすめします。

## ssh-keyscan バージョン文字列リファレンス

Azure Marketplaceで利用可能なOS・ディストリビューションについて、[ssh-keyscan] によって得られるOpenSSHバージョン・ディストリビューション情報文字列の情報をまとめます。

|ディストリビューション|リリース|OpenSSHバージョン|ディストリビューション情報<br>(脆弱性対処済みバージョン)|
|-|-|-|-|
|Ubuntu|22.04 LTS|`SSH-2.0-OpenSSH_8.9p1`|`Ubuntu-3ubuntu0.10`|
|Ubuntu|24.04 LTS|`SSH-2.0-OpenSSH_9.6p1`|`Ubuntu-3ubuntu13.3`|
|Debian|12|`SSH-2.0-OpenSSH_9.2p1`|`Debian-2+deb12u3`|
|RHEL|9.4|`SSH-2.0-OpenSSH_8.7`|情報は得られない|
|FreeBSD|14.1|`SSH-2.0-OpenSSH_9.7`|`FreeBSD-20240701`|