先日、私は、あらかじめパッケージ化されたクエリのセットから選択し、選択したクエリの答えを取得するためにサーバーと通信することができるプログラムに出くわしました。 そのプログラムがサーバとの認証のためのパスワードを要求しないことに気づいたとき、私は不思議に思い始めました。それは、パスワードがプログラムに埋め込まれていたことが判明したのです。著者は、1)それはバイナリファイルだったので、誰もそれを読むことができないし、2)彼らがそれを読むことができた場合、彼らはパスワードの文字列を見つけることができないだろうから、それは大丈夫だと説明しました。
これらの理由はどちらも有効ではないので、この投稿を使ってそのことを証明したいと思います。私の例のプログラムは端末に「パスワード」を書き込むだけですが、実際の本番プログラムとの違いは、悪意のあるユーザが検索しなければならない文字列の数だけです。
#include <stdio.h>
#include <string.h>
main ()
{
char password [9] = {"secret"};
printf ("This is the password: %sn", password);
}
|
図1 - パスワードを埋め込んだ基本プログラム |
実行すると以下のように表示されます。
x1 これはパスワードです。 用意ができました 14:12:16 |
図 2 - 基本プログラムの実行 |
プログラムモジュール内の文字列を表示するには、悪意のあるユーザは >system>gnu_library>bin ディレクトリの strings コマンドを使用します。n5は5文字以上の文字列に出力を制限します。表示を短くするために出力(. . . ...)を切り詰めましたが、パスワードが最初に表示される文字列の一つであることがわかります。パスワードを"login"や"password"のようなキーワードや"admin"、"root"、"sql"のような識別可能なユーザIDの近くに置くことで、検索が容易になります。
システム >system>gnu_library>bin>strings -n5 x1.pm
bind, Release 17.1.beta.be
phx_vos#
Noah_Davids.CAC
プレリリース
これはパスワードです: %s
ひみつ
s$start_c_program
プリエンプションクリーンアップ
撤退
.. . . .
|
図3 - GNU stringsコマンドを使ってプログラムモジュール内の4文字より長い文字列をすべて見つける |
もしstringコマンドが利用できなければ、悪意のあるユーザはプログラムモジュールを表示させることができます。私は、私たち全員が誤ってこれを行ってしまったのではないかと疑っていますが、それはきれいではありませんが、パスワードを表示します。もう一度出力を切り詰めてみたところ、パスワードが表示されました。
ディーエックスワンピーエム
%phx_vos#m16_mas>SysAdmin>Noah_Davids>x1.pm 11-01-13 14:13:59 mst
`00`01`00`1Abind, Release 17.1.beta.be`00`00`00`00`00`00`00`04ctp
`00`00`00`00`0
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`0
0phx_vos
+#`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`
00`00ノア
.. . .
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`C
D'`8B`C0
+00`00`00`00`00これはパスワードです: %s
`00`00`00`00`00`00`00secret`00`00`00`00`00`00`00`04`B0`00`00`00`00`01`00
`00`00,`
+`00`00`04`00`00`00`03@`00`FF`F8`FF`FE`00`04main`00`00`83`EC`2C`C7D$$`00
.. . .
|
図 4 - パスワードを見つけるためのプログラムモジュールの表示 |
プログラムにパスワードを埋め込むには、いくつかの方法があります。
パスワードをファイルに入れておくと、プログラムがファイルを読むことができます。実行する必要がある」人だけがファイルへの読み込みアクセス権を与えられ、それ以外の人はヌルアクセスになります。もちろん、「実行する必要がある」ということは、「パスワードを知る必要がある」ということとは一致しないかもしれません。また、誰かが誤ってアクセスを変更する危険性が常にあります。
別の方法としては、プログラムにパスワード文字列を残しておき、プログラムへのアクセスを制限するという方法があります。この解決策もまた、「アクセスリストを変更するのは簡単だ」という問題に悩まされます。プログラムが誰でも実行可能な他のプログラムと同じディレクトリに置かれている場合、アクセスリストが変更される確率が高くなります。
最後に、パスワードはプログラムモジュールに残しておきますが、何らかの方法で文字列を難読化することができます。次のプログラムはその簡単な方法を示しています。
#include <stdio.h>
#include <string.h>
main ()
{
char obfuscate [9] = {0x0c, 0x81, 0xe2, 0x94, 0xe6, 0x9c};
char key [9] = {127, 228, 129, 230, 131, 232, 133, 234, 135};
char password [9];
int i;
for (i = 0; i < strlen (obfuscate); i++)
password [i] = obfuscate [i] ^ key [i];
password [i] = 0x0;
printf ("This is the password: %sn", password);
}
|
図 5 - パスワード文字列を難読化したプログラム |
ご覧のように、パスワードを作成するために難読化された文字を別のキー文字と組み合わせてXORしています。このプログラムを実行すると、最初のプログラムと同じ出力が得られます。
x2 これはパスワードです。 用意ができました 14:12:51 |
図 6 - 難読化されたプログラムの実行 |
しかし、文字列を検索してもパスワードのようなものは表示されません。
システム >system>gnu_library>bin>strings -n5 x1.pm bind, Release 17.1.beta.be phx_vos# Noah_Davids.CAC プレリリース これはパスワードです: %s s$start_c_program プリエンプションクリーンアップ 撤退 .. . . |
図 7 - strings コマンドでパスワードが表示されなくなった |
そして、ファイルが表示されている間に難読化されたパスワードの文字とキー文字が表示されますが、どこでそれらを見つけるのか、また、それらがパスワードを作成するためにどのように結合されているかを正確に知っている必要があります。
ディーエックスツーピーエム
%phx_vos#m16_mas>SysAdmin>Noah_Davids>x2.pm 11-01-13 14:15:27 mst
.. . .
+`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`00`CD'`8B`
C0`00`0
+0`00`00これはパスワードです: %s
`00`00`00`00`00`00`00`0C`81`E2`94`E6`9C`00`00`00`00`00`00`00`00`00`00`7F
`E4`81`
+E6`83`E8`85`EA`87`00`00`00`00`04`B0`00`00`00`00`01`00`00`00`00`00`00`0
4`01`B0
+`00`03@`00`FF`F0`00`08`FF`FE`00`04main`00`00`83`EC`89$X`89t$T`89|$P`C
7D$L`00
|
図 8 - プログラムモジュールの表示に認識可能なパスワードが表示されなくなった |
プログラムモジュールを分解してパスワードがどのように生成されるかを把握することができるので、これはまだ完全な解決策ではありません。それでもプログラムを分解するには、単にプログラムモジュールを表示するだけではなく、より多くの洗練された技術が必要です。
難読化されたパスワード文字列をファイルに入れて、アクセスリストを使ってファイルを保護することについて、私は二つの考えを持っています。一方では、パスワードを変更する必要がある場合には、難読化されたパスワード文字列をプログラムに埋め込むよりもはるかに良い解決策です。一方で、ファイルに他の設定オプションがあったとしても、ファイルに文字列を入れることで、悪意のあるユーザが調査すべき潜在的なパスワード文字列が少なくなるのではないかと疑っています。 もちろん、彼はファイルにアクセスして難読化技術と鍵を見つけ出さなければなりません。
最後に、どのようにして難読化されたパスワード文字を生成したのでしょうか?XOR演算の良いところは、((A XOR B) XOR B)はAに等しいということですので、元のパスワードの文字を私のキーでXORして結果を印刷する短いプログラムを書きました。
#include <stdio.h>
#include <string.h>
main ()
{
char password [9];
char key [9] = {127, 228, 129, 230, 131, 232, 133, 234, 135};
char newPW [9];
int i;
strcpy (password, "secret");
for (i = 0; i < strlen (password); i++)
{
newPW [i] = password [i] ^ key [i];
printf ("%xn", newPW [i]);
}
}
|
図 9 - 難読化されたパスワードを生成するプログラム |