
Easy Grade Pro 4.1のファイル解析バグを教育用の例として使用し、初心者がリバースエンジニアリングを通じて脆弱性研究を始める方法を示す。
リバースエンジニアリングによる脆弱性調査が最初の一歩から可能であることを初心者に示すために作られた教育用の例です。
このリポジトリは、脆弱性調査を始めたばかりの初心者、特にリバースエンジニアリングに興味がある人を教える際に使えるシンプルな例が必要だという思いから生まれました。
バイナリエクスプロイトやバイナリ解析に関連するコース、トレーニング、修士課程を修了した後、多くの学生は実際の脆弱性調査は自分のレベルをはるかに超えたものだと感じています。彼らは通常、リバースエンジニアリングをカーネルの脆弱性、ブラウザのエクスプロイト、ファームウェア調査、複雑な最新ターゲットといった高度なトピックと結び付けて考えているため、まだ準備ができていないと思っています。実際には、問題は知識の不足ではなく、現実的な出発点の不足です。
基礎コースで学んだスキルだけで、実際のプログラムを対象に、その動作を理解し、クラッシュを引き起こし、実際のバグを特定することがすでに可能であることを示せる例が欲しいと思いました。
このリポジトリはまさにそのような例です。複雑な脆弱性を見つけることが目的ではありません。初心者が小さく、再現可能で、理解しやすいものから始めても、実際の脆弱性調査を行えることを示すことが目的です。
このリポジトリは、私の講演 "The Path That Leads to Your First CVE" に直接関連しています。この講演では、脆弱性調査の世界に入る方法は複数あり、通常は各自の興味に応じて異なる道をたどることになると説明しています。
ソースコード監査から始める人もいれば、リバースエンジニアリング、Webセキュリティ、技術調査から始める人もいます。どの道も有効ですが、重要なのは、すべての分野に初心者向けの入り口があることを理解することです。
初心者向けの道の例は次のとおりです:
このようなターゲットは基本的に見えるかもしれませんが、複雑なシステムを扱う際に後で必要となるのと同じ核となるスキルを教えてくれます。
このリポジトリは、リバースエンジニアリング分野におけるそうした初心者向けの道の1つを表しています。
ここで文書化されている脆弱性は、古いアプリケーションを分析し、そのファイル形式がどのように解析されるかを理解し、クラッシュにつながるプログラミング上の誤りを特定することで発見されました。影響自体は複雑ではありませんが、そのプロセスは現実的で再現可能であり、脆弱性調査が実際にどのように機能するかを学ぶのに役立ちます。
これは「最初のCVEに至る道」を説明する際に私が使うタイプの例です。リバースエンジニアリングが最初から有効な道であり、単純なターゲットから始めることは許容されるだけでなく、多くの場合最も良い学び方であることを示しています。
始めたばかりの頃は、最新のアプリケーションはあまりにも複雑であることがよくあります。それらは、多くの経験なしには理解が難しい保護、緩和策、コードベースを使用しています。古いソフトウェアは異なります。
レガシーアプリケーションは、現代のセキュリティ慣行を考慮して書かれたものではありません。それらには、基本的なリバースエンジニアリングのスキルで理解できる単純な解析バグ、安全でないメモリ操作、ロジックエラーがしばしば含まれています。そのため、学習に最適です。
古いプログラムを使えば、バイナリのリバースエンジニアリング、ファイル形式の理解、クラッシュの誘発、クラッシュの分析、バグの特定、問題の文書化、脆弱性の報告ができます。言い換えれば、すべての脆弱性研究者に必要な基礎スキルを学べるのです。
この例はまさにそのことを示しています。
この例が重要なのは、非常に単純なことを示しているからです:
リバースエンジニアリングが好きなら、最初からその道を進むことができます。習得には何年もかかるかもしれませんが、実際の仕事を始めるために何年も待つ必要はありません。
この脆弱性(CVE-2025-70330))は、Easy Grade Pro 4.1 が独自の .EGP 成績簿ファイルを読み込む際のファイル解析ロジックに影響します。
このアプリケーションは、ファイルから固定位置のフィールドを読み取り、その値を読み込まれたファイルバッファ内のオフセットとして使用して、内部の成績簿構造を再構築します。これらのオフセットは後でメモリサイズの計算や、動的に割り当てられたバッファへのデータコピーに使用されます。
通常の条件下では、ファイルは解析が続行される前にいくつかの構造チェックを通過します。しかし、これらのチェックが成功すると、パーサーはファイル内に格納されたオフセット値が読み込まれたバッファの境界内に収まっていることを検証せずに信頼します。
それ以外は有効な .EGP ファイル内の特定のバイトを変更することで、これらの内部オフセット計算を破壊することが可能です。パーサーが後でこれらの値を使用すると、有効なファイル領域の外側のメモリを読み取ろうとし、その結果アクセス違反とアプリケーションのクラッシュが発生します。
この状態は領域外読み取り(CWE-125)に該当し、細工されたファイルを開くとローカルなサービス拒否(DoS)につながります。
.EGP ファイル形式は、オフセットベースのアプローチで解析されます。パーサーはファイルを順次処理する代わりに、ファイル内の特定のデータブロックが配置されるべき位置を示す開始オフセットと終了オフセットを含む内部構造を読み取ります。
これらのオフセットは、メモリ領域のサイズを計算し、読み込まれたファイルバッファから新しく割り当てられたメモリへデータをコピーするために使用されます。
脆弱なロジックは次のように要約できます:
size = offset_end - offset_start + 1
buffer = calloc(1, size)
memcpy(buffer, file_buffer[offset_start - base_offset], size)
ファイルが初期検証チェックを通過すると、パーサーはファイル内に格納されたオフセットが有効であると想定します。計算されたソースポインタが読み込まれたファイルバッファ内に収まっていることを確認する検証は実行されません。
オフセットが制御された方法で操作された場合、パーサーは有効な領域の外側のメモリを読み取ろうとし、memcpy() 操作中にアクセス違反を引き起こす可能性があります。
すべての不正な .EGP ファイルがクラッシュを引き起こすわけではありません。
パーサーは、脆弱なコードパスに到達する前にいくつかの整合性チェックを実行します。ファイル構造が破損しすぎている場合、アプリケーションは解析を早期に停止し、成績簿が破損していると報告します。
しかし、一部の変更では、内部構造が初期チェックを通過するのに十分な整合性を保ちながら、解析プロセスの後半で誤ったオフセット値を生成します。
この場合、パーサーはこれらのオフセットが信頼され、メモリコピー操作に使用されるより深いルーチンに到達し、最終的に領域外読み取りを引き起こします。
クラッシュは、有効な .EGP ファイルを変更し、特定のオフセットに制御されたデータを挿入することで発生させることができます。
概念実証は次の手順で機能します:
PoC で使用されるパラメータの例:
この変更により、ファイルは初期チェックを通過できるだけの構造的妥当性を維持しつつ、パーサーが後で使用する内部オフセット計算を破壊し、最終的にアプリケーションのクラッシュを引き起こします。
不正なファイルをデバッガの下で開くと、アプリケーションはメモリコピー操作中にクラッシュします。
観測される例外は、無効なメモリ読み取りによって引き起こされるアクセス違反です。
デバッグ中、memcpy() が使用する無効なポインタは、解析されたファイル構造から導出されたオフセット計算に由来します。これらのオフセットが読み込まれたファイルバッファの外側のメモリを参照すると、ソースポインタがマップされていないアドレスを指し、クラッシュを引き起こします。
これにより、脆弱性がファイル解析中の境界検証の欠如によって引き起こされることが確認されます。
この脆弱性は、ベンダーによってもはやメンテナンスされていないサポート終了(EOL)製品に影響します。
この問題は、教育および研究目的で文書化されており、バグがどのように発生し、実際の脆弱性がどのように見つかるのかを理解するために、ソフトウェアを段階的に分析する方法の具体的な例を初心者に提供します。