Intuit.Ruサイトのローカルバージョンがハッキングされた方法

画像



接続の確立...



INTUIT-eでの次の試験の結果(かなり残念)を見て、100回目の質問をしました。「なぜこの質問は再び真実ではないのですか?! 結局、私は答えに100%確信していました...” co病の一部を示したので、私はテストへの正しい答えを探すことにしました、そして、彼らのオープンアクセスの欠如に非常に驚きました。 確かに何かが起こりましたが、それは基本的に「無料で10個のランダムな回答があり、残りは親切に送金してください」という形の誘惑でした。 そこにあるお金はそれほど大きくありませんが、私は答えにお金を払いたくなかったので、私は反対に行くことにしました。



最初のクレイジーな考えは、サイト上のバグを探すことでしたが、次のバグが到着したのですぐに拒否しました。 昔、INTUITはローカルバージョンのWebサイトを備えたディスクを販売していたため、インターネットに接続せずにトレーニングを受けることができました。 試験を受ける。 その後、接続時に結果を同期できます。 さらに、論理的な連鎖が脳に現れました:「テストをオフラインにすることができます=>チェックはオフラインで行われます=>回答のあるファイルがある場所です」。



約4.5GBのおかげで、私は自分の幸福とすべてのジレンマの解決策から離れました! このディスクは、あるトラッカーで非常に迅速に検出され、ダウンロードされました。 退屈から、私はコメントを読み始めました。 感謝の散乱の中で、誰かが尋ねました:「そこに答えが見えますか?」。 彼らは彼に答えなかったが、それから私に全く警告しなかった。 これで、イメージがダウンロードおよびインストールされました(かなり長い間インストールされています)。 ここにあるものを見てみましょう。



浸漬



ルートディレクトリの内部構造は次のとおりです。

html lib local_web_server intu32.ico INTUIT.exe intuit.ini trayicon.ico uninstall.exe
      
      





どういうわけか、私はすぐにlibフォルダーに引き付けられました。 そして、正しく引っ張った。 多数の* .pmファイルと、「 course 」および「 test 」という興味深い名前のいくつかのディレクトリ。 テストフォルダーを調べてその内容を調べたところ、何も理解していないことに気付きました。 これには3つの.pmファイルが含まれていましたが、ご想像のとおり、これはperlパッケージでした。 私は真珠の第一人者ではない、と言わなければなりません。 実験のために一度書いたことがありましたが、その後はなくなっていました。 私の手に入ったシステムは完全に真珠で書かれていました。 しかし、これはあきらめる理由ではなく、発掘は続けられました。



コースディレクトリには、 コースの略称に非常によく似た名前のファイルが多数ありました。 必要なものを見つける方法は? 明らかに、デスクトップからショートカットを起動し、そこで提供されているものを確認するときです。 起動後しばらくして、デフォルトのブラウザが起動し、 localhost :3232でページをロードしました。 面白い。 このウィグワムのどこかには、まだ本格的なApacheまたは最悪の場合デンバーが住んでいます。 しかし、それについては後で。



管理者としてログイン:管理者、私はシステム全体がどのように機能するかを見るために正しいコースで試験に合格しようとすることにしました。 質問への回答を書き留め、送信ボタンをクリックすると、次のテキストが表示されました。 「{n}%ポイントを獲得しました。 {k}の{M}の問題は正しく解決されました。」そして、私の目は、コースへのパスが示されているブラウザー行に注目しましたが、 libディレクトリではなく、 htmlディレクトリにありました。 コースの略称がありました。 私はすでに同様のファイルを見たようです... lib / coursesディレクトリに移動し、oopbase.pmを開いて、いくつかの手順、%講義配列のハッシュを確認しますが、下の写真はもっと面白いです! ここで彼はハンサムです! ハッシュは、すべての質問と回答オプションを含む%テスト配列です。 「問題はすべて解決しました!」その瞬間、私は考えましたが、このハッシュを調べてみると、美しいピンク色のオブロミンゴの鳥がどんどん窓をノックしました...答えはありませんでした。 ハッシュは非常に大きいため、興味深いのは次のとおりです。



 830 => ['830','50','5',"16ab48786a5d520fe6eeea7f1a6e140b", [['5708','830','1','10',"7ce21606425e2b20e566f422696b92de", [['16692','5708','1','1',"  –  ","e9b0b5d2f98bf71489891a48f80cb868", "387b3b67d9c1248131c136eae3e6cab9", [['349301','16692','1',"",], ['349302','16692','2',"  ,     ",], ['349303','16692','3',"    ",], ['349304','16692','4',"     ",],],],
      
      





キー830は、レクチャーハッシュ%からキーに割り当てられた値です。 同時に、その一部を引用します。



 my %lecture = ( 11 => 830, 7 => 826, 17 => 836, 2 => 821, 1 => 820, … );
      
      





ここで、キー830に対応する質問が11回目の講義に関連していることが明らかになります。 そして、これはその時に明らかになった唯一のものです。 私はこのファイルについてこれ以上賢い考えを持っていなかったので、すべての普通のヒーローがするように、それは迂回することに決めました。 私の道は、ファイル構造をさらに研究することでした。おそらく答えの表があります。 私は彼女がどのように見えるかを考え出しました。 テーブルは見つかりませんでしたが、 libディレクトリを注意深く調べた結果、チェーンtest.pm => etest.pmを経由して、手順が含まれるかなり忙しいext.pmファイルが見つかりました。



 sub check_answer_exam sub check_answer_exam_extern sub check_answer
      
      





次に、パッケージ「er」から同じプロシージャを呼び出しました。 パッケージは非常に迅速に発見され、その内容の分析により、 XSLoaderを介して、近くにあるer.dllライブラリがロードされることが示されました(そして、近くはまったく同じでしたが、拡張子.soの* nixの下)。 写真が形になり始め、私はこのライブラリのエクスポートテーブルを見に行きました。



もっと深くする必要があります!







輸出テーブルは私にすべての美意識を殺しました。 boot_er関数が1つ含まれていました。 しかし、 check_answerと他のグッズについてはどうでしょうか...灯油のような臭いがし始め、対策が必要です:おいしいお茶とIDA Pro!



ライブラリは見た目ほど単純ではなく、すべての種類の手順が含まれていることがわかりました。さらに、興味深いインポートテーブルがあります。 KERNEL32とmsvcrtからの非常に馴染みのあるインポートに加えて、perl58.dllライブラリから多数の関数を見つけましたが、これらの関数がGoogleで簡単に見つかると、すべてがそれほど面白くないでしょう。 しかし、そうではありませんでした。







多分これはパールバーの経験が不足しているからかもしれませんし、そのような機能を探す方法がわかりませんが、完全な説明はどこにもありません。 フォーラムの情報のみ。 どうぞ Shift + F12を大胆に押すと、潜在的に興味深いことがたくさんありました。







特に、Usage:er :: check_answer(a、local_user_id、t、answer)という行は、私自身のperlスクリプトからこのライブラリーを呼び出すように促しました。 唯一のキャッチは、入力データ形式でした。 テストが行​​われたスクリプトをいじってみると、興味のある印刷物の呼び出しがいくつか見られました。 値は、ある種のログファイルではなく、コンソールに直接表示されました。 これがWebサーバーのコンソールであると仮定することは論理的であり、このコンソールを見つけるために小さなことは残っています。



local_web_serverディレクトリを調べたところ、Apacheやデンバーの臭いもまったくなかったことがわかりました。 サーバーはperlで作成され、2つのバージョン「server_unix.pl」と「server_win32.pl」、および上記のディレクトリにあるメインのスタートアップファイル「intuit.exe」で提供され、このすべてのエコノミーを起動しました。このファイルからIDAを使用する







すばらしいので、すべてを手動で開始し、STDOUTに何が書かれているかを確認できます。 必要なコースの最初のテストを選択し、正解をマークすると、送信ボタンをクリックすると、次のコンソール出力が表示されました。







そして、これが最初のクッキーです! 実際に重要な値を分析しましょう:



回答配列の数字は、%テスト配列で以前に考慮された回答の識別子です。



関数呼び出しer :: check_answer()のパラメーターを取得するために残ります。 print関数の呼び出しがetest.pmファイルに追加され、Data :: Dumperを使用して、 param配列の内容がファイルに書き込まれ、関数に渡されました。 コードは次のとおりです。



 if($type eq 'lecture') { open(F, ">>D:\param.txt"); print F Dumper(@param); close(F); ($mtime,$csa,$tasks,$points,$mark) = test::ext::check_answer(@param); }
      
      





さらに、受け取ったデータに基づいて、次の内容のスクリプトをスケッチしました。



 #!/usr/bin/perl -w require test::ext; @var1 = [ 1388075531, '8647ee8932669c9e0a00827bb82957d2', [ [ 5658, 16542 ] #   4     ,       . ]; $var2 = '57d0c0e0304de48376b064b86cd36bc1'; @var3 = [ … ]; #         $var4 = ['348973', '348975']; my @param = (@var1, $var2, @var3, $var4); my ($mtime,$csa,$tasks,$points,$mark); ($mtime,$csa,$tasks,$points,$mark) = test::ext::check_answer(@param); print "Tasks: " . $tasks;
      
      





ここで何が起こっているのか分析しましょう。 @ Var1には、現在のテストで尋ねられた質問が含まれています; [intセクションid、質問のint id]の形式の5つの配列があります。 変数$ var2には、テストに合格したユーザーのハッシュ識別子が含まれています。 テスト結果を保存する可能性が最も高い。 @ var3ハッシュには、%テスト配列からの値の正確なコピーが含まれています。これは、レクチャー識別子キーによって取得できます。 最後に、 $ var4配列には、選択した回答オプションが含まれています。 上記のスクリプトでは、テストしやすいように、最初の質問に対する回答のみを残しました。



スクリプトの実行は「タスク:1」という行に満足しており、これは私が正しい軌道に乗っていることを意味しました。 すぐに、$ var4配列のさまざまなバリアントを形成し、$タスクが一致しているかどうかをチェックする単純なブルートフォースを記述するというアイデアが生まれました。 私の圧倒的な関心とウサギの穴がどれほど深いかを知りたいという願望がなかったら、この物語は終わっただろう。



目を覚まし、ネオ...マトリックスはあなたを持っています...



IDAのインポートテーブルを再度調べた後、 strncmp関数から開始することが決定されました。 デバッガ(上記のコードを参照)でテストスクリプトを実行し、er.dllライブラリを読み込む前にプログラムをトレースすることで、使用する関数のリストを開き、strncmpインポートのブレークポイントを設定しました。 スクリプトを再起動した後、デバッガーは「 msvcrt.strncmpでのブレークポイント 」を喜んで報告し、比較された行はスタックにありました:







@ var1配列からのハッシュであり、この関数の呼び出し元を確認することにしました。 Ctrl + F9を押してからF8を押すと、プログラムをさらにトレースし始めました。 トレースの過程で、strncmp内に再び現れ、フォームの2行「57f260a2606af344753ffc00ad834581」を送信しました。 このハッシュは漠然と馴染みがあり、スクリプトコードを見て、これが質問の1つに関連するハッシュであると確信しました。 しかし、すでに暖かかった! F9を押して、再び比較関数に入ると、スタックで見たパラメーターに少し困惑しました。







行 "e8c178abd4f1114837d00771871b6379"は、テストの別の質問に関連するハッシュであり、2番目は私には馴染みがありませんでした。 起動を続けて、比較関数への6つの呼び出しのリストをコンパイルしました。 ここにあります:

挑戦する ハッシュ#1 ハッシュ#2
1 8647ee8932669c9e0a00827bb82957d2 8647ee8932669c9e0a00827bb82957d2
2 57f260a2606af344753ffc00ad834581 57f260a2606af344753ffc00ad834581
3 e8c178abd4f1114837d00771871b6379 0f4632529bff4a4ebab04b5794c1518a
4 9ffcabf80ad0aea2ec7b8c7b89051c29 46916f5f972e01efa665f6cf2245f071
5 3f2fd6dc285372ee847ee9837718f0df a95478bfb5af215aa268d21b498b493c
6 7f29edf5e4fad4e5782ffd36512cc6b7 a2401e9ad6086aa57ea59b61ec0d55d2


2番目から6番目までの呼び出しには、最初の行としてハッシュが含まれていました。そのとき、「質問識別子」と呼びました。 同時に、私が正解した質問では、ハッシュが一致しました。 ある種の中毒がありました。 スクリプトで$ var4配列を変更すると、「57f260a2606af344753ffc00ad834581」の代わりに完全に異なるハッシュ「859c6288692d7037035a011ba54597aa」が表示され、ようやくこれに納得しました。 今、これらのハッシュがどこから来たのかを理解する必要がありました。



関数呼び出しに移動して、パラメーター転送の開始点にブレークポイントを設定し、他のすべてのポイントを削除して、スクリプトを再起動しました。







コードからわかるように、ハッシュがあるアドレスはEAXに格納されており、メモリダンプが確認されました。







ポイントは小さいです-彼をそこに置いた人を見つけてください。



アルゴリズムのさらなる研究により、各質問について、次の形式の行が示されました。

asdc * a * <question id> * a * <response_1_id_id >> * a * <answer_number_N> 、md5アルゴリズムによってハッシュされ、以前に「質問識別子」と誤って呼んだハッシュに対してチェックされます。 実際、正解のハッシュ識別子であることが判明しました。 確認してください!



そして、仲間!



ブルートフォースの回答を書くという考えは、最初に思ったほど悪くないことが判明しました。 そして、私はまだこれに再び来ましたが、このブルートフォースはまったく異なる品質のものです。 完全なコードを提供するのではなく、その主要な点のみを検討します。



perlの信者の気持ちを傷つけないように(私の奇跡のスクリプトで十分だと思います)、C#でブルートフォースを実装します。これは、ご存知のように、すべてに耐えることができます。 コースを含むファイルの分析では、正規表現を使用することが決定されました。 このアプローチの批評家に、私はすぐに言いたいです:あなたはもっと良くできますか? やれ そして、それは私にとってより便利です。



コースファイルでは、次のデータブロックが重要です。



最初のハッシュ配列は、かなり単純な正規表現で取得できます。



 (?<lnum>\d+)\s=>\s(?<lid>\d+)
      
      





次に、コースからの質問の受信を調べます。 これを行うには、次の正規表現を適用しました。



 \['(?<tid>\d+)','\d+'(?:,'\d'){2},"(?<ttext>.*?)","(?<ahash>[a-f0-9]*)","[a-f0-9]*",(?<avars>\[{2}.*?(?:,\]){2})
      
      





一見複雑に見えるかもしれませんが、実際には超自然的なものも何もありません。 名前付きグループには次が含まれます。



未処理の回答オプションの解析は、次の式を使用して行われます。



 '(?<aid>\d+)'.*?"(?<atext>.*?)"
      
      





名前付きグループには次が含まれます。



この場合、回答の総当たりは、特定のセットのすべてのサブセットを見つけるという組み合わせの問題です。 サブセットの生成は、次のアルゴリズムを示すバイナリコードを使用して実行されました。



 int SetPower = (int)Math.Pow(2, answers.Count); for (int i = 1; i < SetPower; i++) { string aStr = ""; answers.ForEach(x => x.Valid = false); for (int j = 0; j < answers.Count; j++) { if ((i & (1 << j)) != 0) { aStr += "*a*" + answers[j].AID; answers[j].Valid = true; } } string answerString = "asdc*a*" + task.TID + aStr; if (GetMD5Hash(answerString) == task.TrueAnswerHash) { return answers.Where(w => w.Valid == true).ToList(); } }
      
      





アルゴリズムの各パスで、回答オプションのサブセットが形成され、そこからハッシュ用の文字列が収集されます。 ハッシュを受信した後、参照ハッシュと比較され、一致する場合、多くの正解が返されます。



接続が終了しました...



それで知識の闘争についてのこの話は終わりました。 知識とは、労働によってのみ得られるものであり、無料で得られた知識(広義では)は何もかかりません。 INTUITのノウハウに感謝します。彼らのリソース全般と、マジックライブラリーer.dllを使って私に送ってくれた楽しい瞬間に感謝します。



記事の追加資料


私がデバッグに使用するPerlスクリプト

コンソールブルートフォースアンサー



All Articles