はじめに
当社で運用しているハニーポットの1つとして、リモートデスクトップ環境のVNCを公開しています。本来の観測目的はVNCでログインした先にあるシステムに対する攻撃活動ですが、今回はVNCに対して攻撃者が入力するパスワードを調査しました。
※本記事の内容は自社運営のハニーポット環境で実施したものです。他者のシステムに対する不正アクセスやパスワード解読は決して行わないでください。
VNCとは
VNCはリモートデスクトップ環境の1つです。リモートデスクトップ環境の代表例としてWindowsのリモートデスクトップがありますが、それに対し、VNCはクロスプラットフォームである点やソフトウェアの選択肢が多い点がメリットとして挙げられます。そのため、リモートメンテナンスなどで広く使用されています。
設定によってはインターネット経由でのアクセスも容易で、実際、Shodanの検索結果(下図)によると、インターネット上に公開されているVNCサーバーは近年急激に増加し、現在では200万台程度に上ります。これほどの台数が公開されていることから、攻撃者の標的になることも少なくありません。2024年にはVNCサーバーを狙うKurtLar_SCADAというマルウェアが発見されています[1]。

出典:Shodan (https://www.shodan.io)
Shodan ® - All rights reserved
VNCの通信プロトコルはRFB (Remote Framebuffer) プロトコルと呼ばれ、RFC 6143として公開されています。RFC 6143では「認証なし(None)」と「VNC Authentication」が基本的な認証方式として定義されており、クライアントとサーバーは接続時にこれらから認証方式を選択します。ただし、VNC Authenticationは暗号学的に弱いため、実際のVNCソフトウェアではより強固な認証方式が実装されています。
今回、我々はハニーポットでVNCサーバーを公開している立場なので、あえてこの脆弱なVNC Authenticationを採用し、それに対して攻撃者が入力してくるパスワードを解読してみます。
VNCのパスワード認証の仕組み
ここで説明するパスワード認証とは、RFC 6143で定義されているVNC Authenticationのことです。このパスワード認証は、暗号アルゴリズムDESを用いたチャレンジ・レスポンス認証です。
サーバーはランダムな16バイトのチャレンジを生成し、クライアントに送付します。クライアントは、そのチャレンジをDESで暗号化します。その際、DESの鍵としてユーザーのパスワードを使用しますが、パスワードは8バイトになるように切り詰めるか、もしくは0埋めされます。
クライアントは暗号化した結果をレスポンスとしてサーバーに送り返します。サーバーは同じ処理(保存されたパスワードでチャレンジを暗号化)を行い、クライアントからのレスポンスと比較することで認証を行います。
DES暗号化処理を整理します:
- パスワードを8バイトに調整(不足分は0埋め、超過分は切り詰め)
- 各バイトのビット順を逆転(VNC独自の処理)
- 2で作成したデータをDESの鍵として、チャレンジデータを暗号化
ステップ2が注意点です。VNC AuthenticationではパスワードをDESの鍵として使用する際に各バイトのビット順を逆転させる処理が必要で、この処理を忘れると認証が失敗します。たとえば、パスワード「ABC」の場合、元々のバイト列は16進で「41 42 43」ですが、逆転後のバイト列は「82 42 C2」となります。
| 文字 | バイト(16進) | ビット列 | 逆転後のビット列 | 逆転後のバイト(16進) |
|---|---|---|---|---|
| A | 41 | 01000001 | 10000010 | 82 |
| B | 42 | 01000010 | 01000010 | 42 |
| C | 43 | 01000011 | 11000010 | C2 |
以下にPythonでの実装例を示します:
import binascii
from Cryptodome.Cipher import DES
def reverse_bits(byte):
"""各バイトのビット順を逆にする"""
return int('{:08b}'.format(byte)[::-1], 2)
def vnc_auth_encrypt(password, challenge):
"""VNC認証の暗号化処理"""
# 1. パスワードを8バイトに調整
padded = password.ljust(8, '\x00')[:8]
# 2. VNC Authenticationの仕様により各バイトのビット順を逆転
key = bytes(reverse_bits(b) for b in padded.encode('latin-1'))
# 3. DES暗号化
cipher = DES.new(key, DES.MODE_ECB)
return cipher.encrypt(challenge)
攻撃者のパスワードを解読する
ハニーポットで観測したVNCに対する攻撃から、実際にチャレンジ・レスポンスのペアを取得しました。攻撃者が使用しているパスワードリストの特定や、現在最も狙われやすいパスワードの傾向を把握するため、前章で説明した仕組みを使って、実際にパスワード解読を試みました。
理論上は総当たりでパスワードを特定できますが、現実的ではありません。攻撃者自身も効率を重視してパスワードリストを使用していると考えられるため、我々も同様にパスワードリストを活用します。
今回はGitHub上で公開されているパスワードリスト[2]を使用します。この中でも「rockyou.txt」は2009年のRockYou社のデータ漏洩で流出した1400万超のパスワードリストで、実際のユーザーが使用していたパスワードが収録されています。攻撃者が辞書攻撃で最もよく使用するリストの一つです。
今回は解読手法の有効性を確認するため、52件のサンプルを対象に解読を試みました。その結果、32件(62%)のパスワードを解読することができました。
解読例:
チャレンジ:1201cc14ef370421ee076aab5cdec922
レスポンス:3d139ef0e1c5bdcc5230caa11e8e4f03
パスワード:151793
解読できたパスワードには以下のような傾向が見られました:
数字のみ:001821、002109、032604、050492、081201 など
英字のみ:Apple、penny、cresta、sweet1 など
英数混合:5tgbvfr4、xxsnowxx など
短いパスワード:ah(2文字)から長いもの(mauraders:9文字)まで様々
解読結果から大きく3つのことが分かりました。
-
不規則なパスワード
「5tgbvfr4」のような一般的な単語ではない不規則なパスワードを解読できました。このような不規則なパスワードを見ると、特定の機器でデフォルト設定されているパスワードである可能性なども考えられます。しかし、rockyou.txtに含まれていることから、実際のユーザーが使用していたパスワードであり、攻撃者もrockyou.txtやそれに基づくパスワードリストを使用していることが推測されます。 -
8文字制限の影響
「mauraders」のように8文字を超えるパスワードも見られますが、VNC Authenticationでは8文字制限があるため、実際には「maurader」として処理されています。そのため、解読に用いたrockyou.txtに含まれる「mauraders」の先頭8文字にヒットしたことは分かりますが、実際に攻撃者が入力したパスワードの9文字目以降が何なのかは確定しません。mauraders = maurader = mauraderXYZ = mauraderHello (VNC Authenticationでは全て同じパスワードとして扱われる)とはいえ、「mauraders」である可能性は比較的高いと思われます。「mauraders」はおそらく英単語「marauders」のミススペリングであり一般的な単語ではないため、rockyou.txtから選ばれた可能性が高いです。
-
解読できなかったパスワード
rockyou.txtで解読できなかった20件については、攻撃者がrockyou.txt以外のパスワードリストに含まれるパスワードや、独自に選択したパスワードを使用していたと考えられますが、62%という解読率は多くの攻撃者が一般的なパスワードリストに含まれる脆弱なパスワードを使用して攻撃を試みていることを示しています。
なお、今回はサンプル数が限られているため、攻撃者全体の傾向を断定するには至りませんでした。より多くのサンプルを収集することで、攻撃者が使用するパスワードリストの特徴や時期による傾向の変化などを分析できると考えています。
おわりに
ハニーポットで観測したVNCに対する攻撃から攻撃者が入力したパスワードを解読し、その傾向を分析しました。攻撃者がパスワードリストに含まれる脆弱なパスワードを入力していることが分かり、適切なパスワード管理の重要性を改めて確認しました。
参考文献
[1] https://www.sans.org/webcasts/story-kurtlar-scada-malware-discovery-victim-disclosure
[2] https://github.com/danielmiessler/SecLists/tree/master/Passwords/Leaked-Databases
