Flatt Security Blog

株式会社Flatt SecurityのBlogです。

PacketProxyとapk-meditを使いこなす!最新ツールを使用したスマホゲーム診断

f:id:flattsecurity:20200727173938j:plain

こんにちは。株式会社 Flatt Security セキュリティエンジニアの村上(@0x003f)です。

弊社ではセキュリティ診断サービスの1つとしてスマートフォンゲーム診断を提供させていただいています。しかし実際にスマートフォンゲームの診断というのがどのように行われているのかご存知の方は多く無いと思います。

本記事では実際の診断の流れや観点などについて簡単に触れつつ、タイトルに挙げた2つのオープンソースソフトウェア(以下OSS)を使用して実際にどのように診断を行うかを説明したいと思います。

チートについて

まず、スマートフォンゲームに置いて開発者や我々セキュリティエンジニアが防ぎたい「チート」について簡単に説明します。

チートとは「ゲームに備わっていない機能を用いることで、使用者が優位になる行為(Wikipedia)」です。チートが行われることでゲームのバランスが著しく乱されます。ゲームの開発者はこれによりユーザーの体験が悪化することを懸念して、ゲームのセキュリティ強化に努めています。

チートには一定の需要が存在し有償でチートを代行するユーザーが存在していたり、チート済みと思われるアカウントの取引が可能なWebサービスが存在していたり、ビジネス化している側面もあります。また、対策も根本的な解決が不可能なものがあり開発者には負担となっています。

弊社ではこのようなチートも含め、実際に依頼を頂いたスマートフォンゲームに対して攻撃を行い、どのようなチートが可能か検証しそれについての対策の提案等を行っております。

f:id:flattsecurity:20200727111717j:plain

診断の流れ

続いて実際に診断としてゲームのチートを行うための流れについて説明します。ここでは診断の流れやある程度の手法を説明することになりますが、絶対に一般に公開されているアプリに対して行わないでください。

診断は大きく分けてサーバーへの診断とクライアントへの診断に別れます。

サーバー

サーバーに対する診断では、クライアントとサーバーの間でやりとりされるパケットの改ざんを試みることで行います。パケット改ざんを行う場合にはProxyツール等を利用して中間者攻撃を行います。

f:id:flattsecurity:20200727112205j:plain

このとき、いくつかのゲームでは単に中間者攻撃を行っただけでは通信の内容を読み取れないように通信内容の暗号化(TLSではなく独自の暗号化技術を用いることが多いです)や改ざん検知を行っている場合があります。そのような場合には診断時にその暗号を解くプログラムを記述して復号し、改ざんを加えてから再び暗号化して送信します。

また、セキュリティレベルの高いアプリケーションでは単に中間者攻撃用の証明書をスマホに入れるだけでは攻撃できないようSSL Pinningが行われている場合もあり、もしこのような実装がされている場合はそちらの解除から行う場合もあります。

f:id:flattsecurity:20200727112231j:plain

クライアント

クライアントの診断では主にメモリの改ざんを行ってゲーム内に現れる数値を改ざんし、有利にすすめるような攻撃を行います。ただし多くの場合はクライアントでそのような攻撃ができないように多層的な防御が行われている場合があります。そのような場合にはサーバー同様にその防御機構を解読し、解除する必要がありますが、今回はその解読を行ったあとにどのように改ざんを行うかについて説明します。

こちらは攻撃のために用意したサンプルアプリです。普通に押していると数値が1回のタップにつき1しか増えませんが、メモリを改ざんすることでいきなり数字を大幅に増やすことができます。

f:id:flattsecurity:20200727112948j:plain

メモリ改ざんではこのようにして数字を増やしたり減らしたりして(例えば対戦ゲームで自分の攻撃力を増やす、相手のHPをへらす。取得経験値を増やす、ゴールドを増やす etc...)ゲームを有利にすすめることができます。

また、この他にもファイル改ざんやその他いくつかのクライアントに対する攻撃観点がありますが今回は割愛します。

ツールの紹介

今回紹介したサーバー・クライアントの診断を行う際に便利な2つのツールを紹介します。

PacketProxy

github.com

PacketProxyは株式会社ディー・エヌ・エーの公開しているOSSのプロキシツールになります。プロキシツールは他にも有名なものとしてBurp SuiteやFiddlerと言ったものになりますが、PacketProxyはディー・エヌ・エーのセキュリティチームが開発しており、ゲームの診断に特化した様々な機能を備えています。

今回の記事のために用意したサンプルのサーバーサイド/クライアントサイドアプリケーション(以下サーバーアプリ、クライアントアプリ)を対象として簡単な使い方を紹介したいと思います。先程のサーバーの診断の流れで説明した中間者攻撃をPacketProxyで行います。

f:id:flattsecurity:20200727113640j:plain

こちらがPacketProxyでクライアントアプリから発信された通信をキャプチャした様子になります。今回のアプリでは通信時に独自の暗号化を施しているため、「damage=LZU5prKBnpgoXakpYxoIig==」 のようになっており、実際にどのような値がパラメータとして送られているのかがわかりません。

PacketProxyではこのような通信を復号・再暗号化する処理を「Encoder」として自分で追加して簡単に取り込めるようにするための仕組みがあります。実際にどのように実装をすればよいかは

PacketProxy/EncodeSample.java at master · DeNA/PacketProxy · GitHub

を見るとわかりますが、decodeClientRequest, encodeClientRequest, decoderServerResponse, encodeServerResponseの4つのメソッドが用意されていて、この中で必要な分を実装していきます。今回は「decodeClientRequest」「encodeClientRequest」を実装してサーバーに送信するデータの改ざんを行ってみます。

実装を行って、ビルドをしPacketProxyを起動するとEncoderに先程追加したものが選べるようになっています。

f:id:flattsecurity:20200727113715j:plain

f:id:flattsecurity:20200727113730j:plain

ここでこちらを選択して同様にキャプチャを行うと暗号化が解除された状態でデータが表示されます。

f:id:flattsecurity:20200727113750j:plain

このままここで「damage=10」の値を「damage=20」の値に書き換えて「send」ボタンを押して送信します。すると以下のように暗号化を再現実装した同じロジックによって値が再び暗号化され、サーバーに送信されます。

f:id:flattsecurity:20200727113806j:plain

この流れを図にすると以下のようになります(図はPacketProxyWikiのものを参考にしています)

f:id:flattsecurity:20200727113831j:plain

実際の診断ではこのようにゲームごとにEncoderを開発して診断を行います。PacketProxyはJavaで様々な形態の通信プロトコル(FirebaseDB, Protocol Buffers over HTTP, MQTT… 等, 実装例はこちら)に対するエンコードモジュールがすでに実装されており、それを継承することで効率よく開発を行うことが可能です。

apk-medit

github.com

apk-meditは株式会社アカツキの公開しているOSSであり、スマートフォンゲーム向けのメモリ検索・書き換えツールになります。一般にはこのような挙動をするツールは他にも広く普及しているものがありますが、それらはデバイスのroot権限を要求されるため、スマートフォン自体のセキュリティ機構が向上している現代では技術的にも、ポリシー的にもハードルが上がっています。apk-meditはroot権限を必要とせずにメモリの検索・書き換えが行えるので検証環境のセットアップが比較的容易になり、そこが優位点となっています。

今回は画像のような値をカウントアップしていくサンプルアプリを作成して試してみました。

f:id:flattsecurity:20200727113932j:plain

まず前準備としてアプリの「AndroidManifest.xml」を修正して「debuggable属性」を「true」にし、リザインしておきます。

apk-meditは動作にroot権限を必要としないのでREADMEに従いGitHubのreleasesからダウンロードして、指示通りadbコマンドを利用してエミュレーターにバイナリを転送し、run-asコマンドでアプリの権限を利用して起動することができました。

準備ができたところで、どのような手順でメモリ改ざんを行うかをまず記します。

  1. 書き換えたい数字を「find」する(今回は下の画像に表示されている「41」という値を書き換えます)

  2. findで出た候補を「filter」コマンドで絞り込みます。今回は数字自体のコントロールが可能なので何度かカウントアップするボタンを押して適当に他に出てこなさそうな数字に変更しました。

  3. 候補が十分に絞り込めたら「patch」コマンドを使って書き換える

以上の手順をスクリーンショット付きで順番に解説していきます。

書き換えたい数字を「find」する

まず書き換えたい数字を決めるために「find」コマンドで探してみます。このとき探す数字の候補が多いと簡単に絞り込めないので適当にカウントアップして41にしたのでそれを探してみます。

f:id:flattsecurity:20200727114015j:plain

この状態から「find 41」とすることで「Found: 128!!」という結果が得られている事がわかります。

f:id:flattsecurity:20200727114045j:plain

findで出た候補を「filter」で絞り込む

このまま数字を改変すると、プログラム中の、改ざんしたい値とは関係ないが動作に必要な数字を一緒に書き換えてしまう可能性が高いです。本当に書き換えたい数字だけを改変するために絞り込みを行う必要があります。今回のプログラムでは数字をカウントアップできるので、適当に他にかぶらなさそうな「58」という数字に変更してから「filter」コマンドを使って絞り込みます。

f:id:flattsecurity:20200727114111j:plain

f:id:flattsecurity:20200727114130j:plain

「filter」コマンドはfindコマンドで見つけた候補の中から更に絞り込むコマンドなので、58という数字で絞り込みを行うことで候補が2件まで減りました。

「patch」コマンドを使って書き換える

十分に絞り込めたので書き換えを行います。書き換えは「patch」コマンドで行えます。さっそくやってみます。

f:id:flattsecurity:20200727114146j:plain

「patch 10000」で適当に大きなわかりやすい数字に書き換えを実行してみました。すると画面に表示されていた「58」が「10000」に書き換えられたことがわかります。

f:id:flattsecurity:20200727114159j:plain

ゲームを開発する際はこのようにメモリ中の値を書き換えて実行されることに備えておく必要があります。

終わりに

今回は弊社で実際に行っているスマートフォンゲーム診断の流れと、診断に使用できるOSSの紹介を行いました。実際には、ここでは紹介しきれなかった弊社独自のチートの観点や、アプリ解析ノウハウ等を駆使してゲームのセキュリティを高めるための一助になるような診断や提案を行います。

スマートフォンゲーム診断や他のサービスに関して相談したいという場合は、ぜひ https://flatt.tech/contact のお問い合わせフォームよりご連絡ください!