遥かへのスピードランナー

シリコンバレーでAndroidアプリの開発してます。コンピュータービジョン・3D・アルゴリズム界隈にもたまに出現します。

Wiresharkで暗号化されたSQL Serverパスワードが解析できてしまう件

SQL Serverに対し、SQL Server認証を行う場合ネットワーク上を流れるパスワードは暗号化されています。といっても、これは非常に簡単な単換字方式の暗号化で、ビット演算をかじったことがある人なら簡単に暗号化・複合を行うことができます。

まず暗号化のロジックですが、1バイトずつ以下の処理を施していくことによって暗号化されたバイト列を得ることができます。

  1. Asciiコード(1byte)に、0x00を付与して2byteに拡張する。
    例) 0x70 => 0x70 0x00
  2. 2byteそれぞれの上位4ビットと下位4ビットを入れ替える。
    例) 0x70 0x00 => 0x07 0x00
  3. 2byteそれぞれの0xA5とのXORをとる
    例) 0x07 0x00 => 0xA2 0xA5

複合はその逆で、2バイトずつ以下の処理を行うことによって可能です。

  1. 2byteそれぞれの0xA5とのXORをとる
    例) 0xA2 0xA5 => 0x07 0x00
  2. 2byteのうち下位1byteは0x00になるはずなので、上位1バイトのみにする
    例) 0x07 0x00 => 0x07
  3. 上位1バイトに対して、上位4ビットと下位4ビットを入れ替える
    例) 0x07 => 0x70

以上それぞれをJavaScriptにするとこんな感じです。

//平文のSQL Serverパスワードを暗号化する
function crypt(plainText){
	var r = [];
	for(var i = 0; i < plainText.length; i++){
		var byte  = String.charCodeAt(plainText[i]);
		r[2*i]    = ((byte & 0x0F ^ 0xA) << 4) + (byte >> 4 ^ 0x5);
		r[2*i+1]  = ((0x00  ^ 0xA) << 4) + (0x00  ^ 0x5);
	}
	return r;
}

//暗号化されたSQL Serverパスワードを複合する
function decrypt(cryptedBytes){
	var r = [];
	for(var i = 0; i < cryptedBytes.length; i+=2){
		var byte = cryptedBytes[i];
		var rByte = ((byte & 0x0F ^ 0x5) << 4) + (byte >> 4 ^ 0xA);
		r[i] = String.fromCharCode(rByte);
	}
	return r;
}

とまあ大した暗号ではないのですが、ここで言いたかったのはWireshark*1では、ネットワークを流れるパケット上で暗号化されたSQL Serverパスワードの複合まで自動で行ってくれる(!)という話。
以下がその証拠画像。

上記はport 1433でフィルタリングしているんですが、Info欄に「TDS7/8 Login Packet」と表示されたパケットの詳細を追っていくと、赤丸をつけた箇所(下部)に平文のパスワードが表示されていることが確認できます。
この方法を使うと、バックグランドでSQL Server接続をしているツールのパスワードや、忘れてしまったパスワードの解析などが簡単にできます。

Wiresharkは他にも非常に高機能で、様々なプロトコルのパケットを構造化して見ることができます。
他にパスワード複合まで行えるプロトコルを発見できなかったのですが(平文で流れるFTPや、秘密鍵方式の通信は例外として)、パスワード関連で困ったことがあるときは、Wiresharkを使うといいことがあるかもしれません。

*1:言わずもがな、マルチプラットフォームで動作するパケットキャプチャの代表的ツール:http://www.wireshark.org/