PHDays IIIのベストリバースコンテスト:開発者の視点

コンペティションのタスクの準備を開始したとき、面白くて難しいが、同時に解決できるようにしたかったのです。



私たちの観点からすると、優れたリバーサーはマシンコードを読み取り、それを理解可能なアルゴリズムに変換し、このアルゴリズムのエラーや弱点を見つけ、可能であればそれらを悪用できるはずです。 同時に、分析用に提案されたコードは、実際のプログラムコードに似ている必要があります。



プラットフォームとして64ビットバージョンのWindowsが選択されました。 64ビット-x86用のHex-Rays Decompilerを使用するとタスクが大幅に簡素化されるため、x64用のデコンパイラーはまだありません。 とにかく、64ビットアプリケーションはすでに一般的になっています。



そのため、Qt(および静的ライブラリ)を使用して小さなプログラムが構築されました。 この場合、実行可能ファイルのサイズはほぼ10 MBでした。 しかし、これは本当のリバーサーにとってはたくさんですか? レビューによると、一部の参加者はファイルサイズに怖がっていましたが。 一方、Qtは有用な情報を大量に残しており、リバーサーは穀物をch殻から分離できるはずです...



プログラムを開始するには、システムに2つの動的ライブラリ( msvcp110.dll



およびmsvcr110.dll



)が必要でした。 競技者の中には、彼のプログラムがまったく開始されなかったと不満を漏らしたが、例外はあった。 しかし、参加者または設定の残りの部分が適切に設定されているか、モチベーションが強かった;)



最初のステップは、ユーザー名とキーを入力することでした。 プログラムはコンプライアンスをチェックし、成功または失敗を報告しました。 Base64デコード(アルファベットの文字列によって基本的に決定された)に加えて、メインチェックはOpenSSLを使用して記述されました。 ライブラリは、数分でソースを調べてほとんどすべての関数の名前を決定する機会があるという点で、リバーサーにとって便利です。

ソースコードでは、検証関数は次のようになりました。



phdInt checkDSAsig (BIGNUM *h, BIGNUM *r, BIGNUM*s) {





BN_CTX *ctx = BN_CTX_new();





BIGNUM *g = BN_bin2bn(El_g, sizeof(El_g), NULL);





BIGNUM *p = BN_bin2bn(El_p, sizeof(El_p), NULL);





BIGNUM *y = BN_bin2bn(El_y, sizeof(El_y), NULL);





BIGNUM *v1 = BN_new();





BIGNUM *v2 = BN_new();





BIGNUM *t1 = BN_new();





BIGNUM *t2 = BN_new();





phdInt rc = 0;







if (BN_mod_exp(v1, g, h, p, ctx) && BN_mod_exp(t1, y, r, p, ctx) && BN_mod_exp(t2, r, s, p, ctx) && BN_mod_mul(v2, t1, t2, p, ctx) && 0 == BN_cmp (v1, v2)) rc = 1;







BN_clear_free(t2);





BN_clear_free(t1);





BN_clear_free(v2);





BN_clear_free(v1);





BN_clear_free(y);





BN_clear_free(p);





BN_clear_free(g);





BN_CTX_free(ctx);





return rc;







暗号化の知識があると、これがEl-Gamalアルゴリズムを使用したデジタル署名検証であることをすぐに理解できます。 操作が実行されるEl_p



モジュールのサイズは500ビットであり、このような署名は安定していると見なされます。 したがって、キーを額で計算することはできません。



別のコードブランチは、入力されたキーの長さが6文字であることを確認し、SHA1をカウントして、最初の8バイトをシーケンス{0xEE,0xD1,0xAC,0xA8,0xD0,0xCC,0xA3,0x3F}



と比較しました。 Base64アルファベットで構成される6文字の文字列は、わずか2 36個のオプションです。 それらを整理する場合(必要ではありません-移行条件を修正するだけです)、イースターエッグがあります-「PHDays」という言葉です。



イースターエッグがアクティブになると、プログラムは何か難しいことを始めますが、結果を待つことはほとんどありません。 各反復で、 El_p



の値を超えずにEl-Gamalの公開鍵El_yをEl_p



で乗算した大きな数値が生成され、結果はEl_p



になります。これが発生した場合、生成された数値はRC4アルゴリズムの暗号化鍵として使用され、この鍵El-Gamalの正しい署名を含むコードの行が復号化されます。 理論的には、乱数ジェネレーターはいつか正しいRC4キーとなる一連のバイトを生成しますが、これは太陽が消える前に発生することはほとんどありません。 したがって、競技者は正しいRC4キーを受け取るべきであると想定されました-拡張ユークリッドアルゴリズムを使用して代数補数を計算するだけです。



有効なAl-Gamal署名を取得することは、署名が生成された名前にゼロバイトが含まれているため、最初のステージの決定ではありません: " |<33p y0ur pr1v473 $3cr37\0\0\0



"。 そして、そのような行は名前として入力できません:ゼロバイトは破棄されます。



注意深い暗号作成者は、署名検証コードにアルゴリズムで説明されて0 < r < El_p



検証0 < r < El_p



ことにすぐに気付くはずです( 0 < r < El_p



)。 この場合、Applied Cryptographyのハンドブック(セクション11.66.iv)には、有効な署名が1つしかない場合に任意のメッセージの署名を計算できる攻撃が含まれています。 この攻撃により、プログラムが認識する名前の署名が取得されます。



タスクの2番目の部分では、入力されたキーはユーザー名に関連付けられていませんでした。 キーはBase64によってデコードされた後、いくつかのトリッキーなアクションが実行され、結果として、サブストリング「 PHDays III validator ;)\0



」がPHDays III validator ;)\0



れたバイトのセットを取得する必要があります。 最初は、サブストリングをどこでも検索することを計画していましたが、コーディングエラー(開発者も人であるため)により、出力バイトセットの先頭でのみ一致がチェックされました。



タスクの複雑さは、公開鍵を使用した暗号化の要素も使用することでしたが、それらは独立して完全に明白ではない方法で実装されました。 実際、入力されたキーは、2つの素数の積である大きな(1000ビット)数を法として累乗されました。 そして、これはまさにRSA暗号システムの根底にある数学です。 累乗は、モンゴメリー乗算によって実現されました。入力数は、モンゴメリーが事前に指定する必要があります。



公開指数の値は5で、検証を正しく実装すると、入力シークレットの計算には1000ビット数の因数分解が必要になりますが、これは今まで誰にも不可能でした。 しかし、24バイトの部分文字列の存在のみがチェックされたため、目的の結果の5番目のルート(モジュロではない!)を計算し、それをモンゴメリーに沿って持ち込み、Base64をエンコードし、2番目の部分のキーを取得することができました。



3番目の部分は、問題を数学的に解決するために提案された通常のcrackmeタスクの観点からはあまり一般的ではありませんでした。 しかし、まず最初に。 キー検証アルゴリズムは、ユーザーが入力したデータをBase64アルゴリズムに従ってサイズsizeof(El_p)*2+1024



バッファーにデコードしました。 さらに、デコードされたデータがsizeof(El_r)



超えて占有した場合、そのようなキーは有効であると認識されませんでした。 デコードされたデータの後、SHA-3ハッシュが取得され、文字列「 ESETESETESETESETESETESETESETESET



」と比較されました。 明らかに、課題の作成者でさえ正しい入力値を知らなかったため、参加者は代替ソリューションを検索する必要がありました。



注意深い読者は、最初の部分の脆弱性により、デコードされたデータがコピーされたバッファをオーバーフローさせる可能性がある長さのEl_r



を選択できることにすでに気付いています。 さらに、このバッファーはスタック上にあります。 このプログラムは、スタック保護なしで固定ベースでコンパイルされたため、ROP手法を使用して脆弱性を悪用し、タスク全体の解決策を検証する機能をバイパスすることができました。



タスク全体の解決策の確認は、次のように構成されました。タスクの各部分の各ビットについて、グローバル変数の3ビットを確認し、すべてのビットが設定されている場合、タスクの完了を祝うウィンドウを表示しました。 そのため、タスクを解決するために、グローバル変数のアドレスに数値7を書き込み、3番目の部分をチェックする機能を正しく完了することができるROPガジェットを見つけることが残っています。 次に、おめでとうとウィンドウを見ることができました。



競争の結果によると、表彰台は次のようになります。



配置: ビクター・アリューシン



II位: ミハイル・ボロノフ 、デニス・リトヴィノフ



III位: アントン・チェレパノフ



受賞者にはブランドのバックパックPHDaysとAR Drone 2.0が贈られ 、他の受賞者にも記念すべき贈り物が贈られました。 おめでとうございます!







PS Victor Alyushinは、コンテスト専用の優れた文章を書きました。



All Articles