サイト内の現在位置

トレーニングコンテンツ:脆弱なAndroidアプリ「InsecureBankv2」の紹介

NECセキュリティブログ

2021年2月19日

NECサイバーセキュリティ戦略本部セキュリティ技術センターの中島です。本記事では、Androidアプリのセキュリティの基礎について学ぶことができるAndroidアプリである「InsecureBankv2」をご紹介します。

※Google Playストアで配布されているアプリを制作者や配布者の許可なくリバースエンジニアリングしないでください。本ブログに記載の内容を悪用した行為などは絶対におやめください。

InsecureBankv2とは

InsecureBankv2(new windowhttps://github.com/dineshshetty/Android-InsecureBankv2)は、dineshshetty氏が作成したツールです。本ツールは、Androidアプリのセキュリティや開発に関わる人がAndroidアプリのセキュリティや実装方法を学べるように、Androidアプリの基本的な脆弱性を盛り込んで作られたアプリケーション(いわゆるやられ環境)です。
以下に挙げる4つの点からAndroidアプリのセキュリティを基礎から学ぶ題材として取り組みやすいコンテンツだと思います。

  1. 本コンテンツの開発者が仕込んだ脆弱性があらかじめ提示されていること。そのため見つけなければならない脆弱性(目標)がわかりやすい。
  2. 一部の脆弱性についてWalkthroughという形で脆弱性の見つけ方が示されていること。そのため、何をしてよいのかわからないという点が緩和される。
  3. 比較的容易に見つけることが可能な欠陥が実装されていること。そのためAndroidアプリ解析が初めてでも脆弱性を発見できて楽しい。
  4. MITライセンスで公開されており解析や改変が可能でアプリ解析の手順や解析ツールなど様々な手法を試す練習台として使えること。

特に、1・2点目の特徴は他の似たような脆弱なAndroidアプリでは提示されていないことが多く、初めての解析練習にお勧めです。今回、InsecureBankv2の紹介ということで、解析に使うツールや環境の構築方法には深く触れず簡単な説明とします。

使用したツール、ガイドライン

InsecureBankv2の解析に使用したツールを列挙します。各種ツールのライセンスやURLは本記事最後の参考欄に記載しております。

Android Virtual Device Manager(Android Studio付属):エミュレータとして使用

QARK:Androidアプリ(.apk)を複数のデコンパイラを使ってデコンパイルするために使用

Android Debug Bridge(adb):Android OSのコマンドラインツール。アプリのインストールやデバッグ、ファイルの転送等で使用

QPDF(zlib-flateコマンド):Androidアプリのバックアップデータの展開に使用

CyberChef:AESで暗号化された文字列の解析に使用

一般社団法人 日本スマートフォンセキュリティ協会
Androidアプリのセキュア設計・セキュアコーディングガイド
PDFhttps://www.jssec.org/dl/android_securecoding.pdf

今回、InsecureBankv2はエミュレータ上のAndroid 5.1にインストールして動かしています。InsecureBankv2のWalkthroughでは、dex2jarやjadxを主に静的解析ツールとして使用していますが、本記事ではqarkコマンド(QARK)を使用しています。 qarkをインストールすれば、apktool、dex2jar、procyon、cfr、fernflowerといったツールを個別に導入する必要がありません。また、解析に当たって、" Androidアプリのセキュア設計・セキュアコーディングガイド"を解析時の注目点や実装時の対策の参考としています。

InsecureBankv2のセットアップ

それではInsecureBankv2を使うためのセットアップを行います。
セットアップ手順はGithub上のガイドを参考に行いました。
PDFhttps://github.com/dineshshetty/Android-InsecureBankv2/blob/master/Usage%20Guide.pdf

プロジェクトを手元にクローンしておきます。
$ git clone https://github.com/dineshshetty/Android-InsecureBankv2.git

InsecureBankv2を使うにはAndroidアプリ上で動作するInsecureBankv2本体に加え、アプリの通信先バックエンドサーバーであるAndroLabServerを動作させる必要があります。

まず、バックエンドサーバーであるAndroLabServerの設定を行います。AndroLabServerの動作に必要なファイル類は、Gitプロジェクトのホームディレクトリ上のAndroLabServer配下に配置してあります。app.pyを実行するとポート番号8888で動作し始めます。ポート番号を変えたい場合は--portオプションを使用します。
$ cd AndroLabServer
$ python2 app.py
もしくは
$ python2 app.py --port 12345

このAndroLabServerはpython2で動作するので、今回私はDocker(new windowhttps://github.com/moby/moby)コンテナ上で動作させました。Python2が動作すればよいので、python仮想環境管理ツール等ご使用の方はpython2に切り替えた上で実行してください。Dockerコンテナのイメージとして、frolvlad/alpine-python2(new windowhttps://github.com/Docker-Hub-frolvlad/docker-alpine-python2)を使用しました。

それではDocker上でバックエンドサーバーAndroLabServerを実行します。なお、<pathtodir>としているパスは別途自身の環境に置き換えてください。
$ docker pull frolvlad/alpine-python2
//イメージのpull
$docker run -it -d -p 8888:8888 -v <pathtodir>/Android-InsecureBankv2/AndroLabServer:/opt/AndroLabServer frolvlad/alpine-python2
//コンテナの立ち上げ。AndroLabServerのディレクトリをマウント
$docker ps
//コンテナのID確認
$ docker exec -it <CONTAINER ID> /bin/sh
//コンテナに入る
/ # cd /opt/AndroLabServer/
/opt/AndroLabServer # pip install -r requirements.txt
//必要なパッケージをインストール
/opt/AndroLabServer # python app.py
//バックエンドサーバーの実行

The server is hosted on port: 8888と表示されればバックエンドサーバーは動作しています。

次にAndroidに InsecureBankv2アプリのインストールを行います。今回は実機ではなくエミュレータ上で動作するAndroidにインストールします。Android Virtual Device managerからエミュレータイメージを作成します。今回は、"5.4 FWVGA API 22" Android 5.1を選択しました。エミュレータ上でAndroidを起動しておきます。

Gitプロジェクトのホームディレクトリ配下にある" InsecureBankv2.apk"がアプリ本体です。この" InsecureBankv2.apk"をエミュレータ画面上にドラッグアンドドロップするとインストールすることができます。インストールだけならこちらが便利です。

もしくはコマンドラインからadb installでインストールを行います。
Android SDK Platform-Tools付属の Android Debug Bridge(adb)コマンドを使用してアプリのインストールを行います。まず、adbがデバイスを認識しているか確認します。
$ adb devices
List of devices attached
emulator-5554   device

アプリをインストールします。
$ adb install InsecureBankv2.apk
Performing Push Install
InsecureBankv2.apk: 1 file pushed, 0 skipped. 711.7 MB/s (3462429 bytes in 0.005s)
        pkg: /data/local/tmp/InsecureBankv2.apk
Success

アプリ一覧からインストールされていることを確認できます。

続いてアプリを立ち上げてバックエンドサーバーとの通信の設定を行います。右上の三点リーダーからアプリのメニューを開き"Preferences"を選択します。

サーバーのIPアドレスとポート番号を指定します。今回はAndroidエミュレータを使っていますので10.0.2.2としています。またポート番号はバックエンドサーバー立ち上げ時に任意の番号を選択した場合、その番号を入力します。Submitで設定完了です。

"InsecureBankv2"の機能確認

具体的なアプリの解析に入る前に、まずはこのアプリが持つ機能を簡単に調べてみます。アプリでログインしてみます。ログインに使うクレデンシャルとして以下の二つが提示されています。
dinesh/Dinesh@123$
jack/Jack@123$

ログインすると以下のような画面になっていました。

Transfer、View Statement、Change Passwordの3画面があるようです。また、"Device not Rooted!!"とありAndroidデバイスがRoot化されているかのチェックを行っていそうです。各画面を確認してみます。

まずは、Transfer画面から確認します。InsecureBankv2というアプリ名にある通り、送金を模した画面と思われます。

次のViewStatement画面では上記Transferで実行した内容が表示されるようです。

最後に Change Password画面です。パスワードが変更できそうです。

以上、アプリに必要なバックエンドサーバーの実行とアプリのインストールの手順とアプリの簡単な画面確認でした。

Androidアプリ"InsecureBankv2"の解析

InsecureBankv2アプリの解析を行ってみます。Androidアプリ解析には大きく分けて静的解析と動的解析があります。今回は、静的解析をメインに解析したいと思います。GitプロジェクトのREADME.markdownファイルに書かれていた脆弱性の内、以下の星印 ★が付いた7つを見ていきます。

Flawed Broadcast Receivers
Intent Sniffing and Injection
Weak Authorization mechanism
Local Encryption issues ★4
Vulnerable Activity Components
Root Detection and Bypass ★5
Emulator Detection and Bypass
Insecure Content Provider access
Insecure Webview implementation
Weak Cryptography implementation ★4
Application Patching
Sensitive Information in Memory
Insecure Logging mechanism ★1
Android Pasteboard vulnerability
Application Debuggable
Android keyboard cache issues
Android Backup vulnerability ★2, 3
Runtime Manipulation
Insecure SDCard storage
Insecure HTTP connections
Parameter Manipulation
Hardcoded secrets ★4
Username Enumeration issue
Developer Backdoors ★6
Weak change password implementation

本記事では★7つに対して以下の確認を簡単に行います。

  1. adb logcatでログの確認
  2. マニュフェストの確認
  3. バックアップファイルの確認
  4. 暗号関連の確認
  5. Root化検知の実装確認
  6. 開発者用バックドアの確認

1. adb logcatでログの確認

まず、adb logcatを使用してアプリが吐き出すログを確認します。以下のコマンドを実行しておき、アプリを操作しつつシステムメッセージへ出力されるログを見ていきます。
$ adb logcat
オプション無しでは大量にログが出力されるので、フィルタリングして後から見直せるように結果をファイルに書き込みます。
$ adb logcat *:E 2>&1 | tee adblogcatlog.txt
なお"E"のところを、ロギングしたい重要度によって適宜変更可能です。以下、adbコマンドhelpより抜粋。
where <tag> is a log component tag (or * for all) and priority is:
  V    Verbose
  D    Debug
  I    Info
  W    Warn
  E    Error
  F    Fatal
  S    Silent (supress all output)

それでは、adb logcatを使って実際に吐き出されるログの内容を確認します。アプリでログインを行います。その際のlogcatの内容です。
D/Successful Login:( 3914): , account=dinesh:Dinesh@123$
と、ログイン時のUsernameとpasswordが出力されています。

また、Change Passwordの画面でパスワードを変えてみます。

その際のlogcatの内容です。こちらは電話番号と旧パスワード、新たに設定したパスワードが出力されています。
I/System.out( 3914): For the changepassword - phonenumber: +15555215554 password is: Updated Password from: Dinesh@123$to: Dinesh@123456789$

では、どこでこれらのログ出力が行われているのでしょうか。後ほど紹介するqarkコマンドでInsecureBankv2.apkをデコンパイルした結果を確認してみます。ログイン時の出力は以下のファイルに記述がありました。"Successful Login~"というログへの出力はLog.d()で出力されていました。qarkでデコンパイルして生成されたファイル
"<pathtoqarkdecompile>/build/qark/procyon/com/android/insecurebankv2/DoLogin.java"
の217行目の記述です。

ChangePasswordでの出力は以下のファイルに記述がありました。" For the changepassword~"から始まるログはSystem.out.printlnで出力されていました。 qarkでデコンパイルして生成されたファイル
"<pathtoqarkdecompile>/build/qark/procyon/com/android/insecurebankv2/MyBroadCastReceiver.java"の31行目の記述です。

QARKでアプリをデコンパイルして解析する

QARKというツールを使用してアプリをデコンパイルします。QARKは複数のデコンパイラでデコンパイルしてくれます。QARKはGithubやpipで入手できます。今回は比較的きれいにデコンパイルできていたprocyonによる結果のみを使用します。通常はデコンパイラごとに上手くデコンパイルできている/できていないファイルがありますので複数のデコンパイル結果を参照します。

qarkコマンドでapkファイルをデコンパイルします。しばらく時間がかかります。
$ qark --apk InsecureBankv2.apk
上記のコマンドを実行するとqarkがレポートを出力してくれます。重複が多いため別途フィルタリングして参考程度に内容を確認しました。気になった検出事項は以下の通りです。

WARNING Backup is allowed in manifest
WARNING External storage used
WARNING Broadcast sent without receiverPermission
VULNERABILITY Empty pending intent found
WARNING Ordered broadcast sent with receiverPermission with minimum SDK under 21
WARNING Webview enables DOM Storage
VULNERABILITY Manifest is manually set to debug

2.マニュフェストの確認

続いて、AndroidManifest.xmlというファイルを確認してアプリの概要を把握して注目点を洗い出します。 AndroidManifest.xmlはアプリのコンポーネントやパーミッション、アプリが必要とする機能等が宣言されています。

AndroidManifest.xmlでは以下の事項を確認します。
  - activityの確認
  - permissionの確認
  - provider(Content provider)
  - receiver(Broadcast Receiver)
  - Service
  - 共有メモリ
  - デバッグバージョンであるか確認
  - バックアップが許可されているか確認

activityの確認

最初に起動する画面はLoginActivityという画面とわかりました。

permissionの確認

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

WRITE_EXTERNAL_STORAGEが気になりました。
SDカードへの書き込みがありそうです。

Content Providerの確認

ファイル内をproviderで検索します。

Content Providerはアプリ間データ共有のインターフェースを規定しています。Content Providerは、あるアプリが持つデータを他のアプリでも利用する機能を提供します。
公開 Content Providerが存在するか調査します。

以下の Content Providerが定義されており、 android:exported="true"となっているのが気になります。

今回のデコンパイル結果では以下のファイルがこの実装となりそうです。
" <pathtoqarkdecompile> /build/qark/procyon/com/android/insecurebankv2/TrackUserContentProvider.java"

今回の記事ではContent Providerの詳細確認は省略します。

Broadcast Receiverの確認

Broadcast Receiverには、静的Broadcast Receiverと動的Broadcast Receiverの2種類あります。公開Broadcast Receiverは、AndroidManifest.xmlに記載される静的Broadcast Receiverでのみ構成(定義)可能であるとのことで、公開Broadcast Receiverを探す場合AndroidManifest.xmlを調査します。ファイル内をreceiverで検索したところ、2つ Broadcast Receiverが定義されていました。その内、1つが exported="true"により、明示的に公開設定されていました。

今回のデコンパイル結果では以下のファイルがこの実装となりそうです。
" <pathtoqarkdecompile> /build/qark/cfr/com/android/insecurebankv2/MyBroadCastReceiver.java"

今回の記事ではBroadcast Receiverの詳細確認は省略します。

Serviceの確認

ファイル内をServiceで検索します。今回、Serviceはありませんでした。

共有メモリの確認

Serviceにて定義されます。今回 Serviceがなかったため共有メモリはありません。念のためデコンパイルして生成されたファイルで"import android.os.SharedMemory"が使われていないか検索します。今回は存在しませんでした。共有メモリを使用している場合、共有メモリにセンシティブな情報を書き込んでいないか調査する必要があります。

デバッグ可能か・バックアップ可能か確認

android:debuggable="true"となっておりユーザーモードのデバイス上でもアプリをデバッグ可能です。
android:allowBackup="true"となっており、アプリをバックアップ可能です。

3.バックアップファイルの確認

実際にアプリをバックアップしてみます。
$ adb backup –apk –shared com.android.insecurebankv2
WARNING: adb backup is deprecated and may be removed in a future release
Now unlock your device and confirm the backup operation...
デバイス上でバックアップを確認するように言われるので、Androidの画面を操作します。この時パスワードを指定できます。BACK UP MY DATAを押してバックアップを実行します。カレントディレクトリにbackup.abが作成されます。

このbackup.abの中身を確認するため、InsecureBankv2のWalkthroughにあったコマンドで展開します。途中でzlib-flateというコマンドを使用します。このコマンドを使用するためにはqpdfが必要でapt install qpdfでインストールできます。
$ cat backup.ab | (dd bs=24 count=0 skip=1; cat) | zlib-flate -uncompress > backup.tar
$ tar -xvf backup.tar

tarを展開するとappsというディレクトリができますのでその中を確認します。
$ cd apps/com.android.insecurebankv2/sp
sp配下のファイルにクレデンシャルが複数含まれているようです。mySharedPreferences.xmlには暗号化されたクレデンシャルが含まれていました。今回記載されていた文字列は"v/sJpihDCo2ckDmLW5Uwiw==" です。後ほどデコンパイルで生成されたコードを解析した結果を使って復号してみます。なお、mySharedPreferences.xmlはadb shellでデバイスに接続し、/data/data/com.android.insecurebankv2/shared_prefs/上にある mySharedPreferences.xmlでバックアップせずとも直接確認できます。

AndroidManifest.xmlの確認を行い、注目するファイル・箇所を見つけたら次はファイル全体を特定のキーワードでgrepします。また、目を付けたコードを読みます。

4.暗号関連の確認

デコンパイルして生成されたファイルに対して、特定のキーワードで検索します。今回はKey、AES、IV、CBCなど暗号関連の単語で検索してみました。コマンドの例です。
$ find ./ -name '*.java' -type f | xargs grep -l "key"
$ grep -rin "key"

" <pathtoqarkdecompile> /build/qark/procyon/com/android/insecurebankv2/CryptoClass.java"というファイルが気になりました。CryptoClassというクラスを覗いてみます。31、32行目のkeyとivBytesそして43行目のaes256encryptメソッドに注目します。

KeyとIVが判明しました。暗号方式はAES、暗号利用モードはCBC、パディング方式はPKCS5Paddingということもわかります。
暗号化に使われた情報がわかりましたので、先ほどバックアップファイルの中から見つけたmySharedPreferences.xmlファイルに記載された"superSecurePassword"の値を復号してみます。まず、"superSecurePassword"の値はBase64でエンコードされているようですのでデコードしてbyte単位のHex表記で出力します。
$ echo "v/sJpihDCo2ckDmLW5Uwiw==" |base64 -d |xxd -g1

続いてCyberChef(new windowhttps://github.com/gchq/CyberChef)を使ってAESの復号を行います。CyberChefはオフライン版を使用しています。上記コマンドで出てきた"bf fb 09 a6 28 43 0a 8d 9c 90 39 8b 5b 95 30 8b"という値をInputに入れます。Key、IV、Modeには CryptoClass.javaのコードからわかった値を入力します。するとOutputにJack@123$というパスワードが出力されました。

5.Root化検知の実装の確認

アプリのトップ画面からログイン後に"Device not Rooted!!"と表示されこのアプリはRoot化のチェックをしていることが推測できます。デコンパイルして生成されたファイルにPostLogin.javaというファイルがあり、ここでRoot化の有無を検証する実装がありました。 " <pathtoqarkdecompile> /build/qark/procyon/com/android/insecurebankv2/PostLogin.java"

doesSuperuserApkExist()で/system/app/Superuser.apkの存在有無をチェックしています。doesSUexist()では、"/system/xbin/which", "su"を実行してsuの存在有無をチェックしています。この2つのチェックでRoot化の検証をしているようです。InsecureBankv2ではこのroot検知機能のバイパスも試すことができます。

6.開発者用バックドアの確認

DoLogin.javaというログインに関連するコードを読んでいたところ開発者用のバックドアをとみられる実装を見つけました。
" <pathtoqarkdecompile> /build/qark/procyon/com/android/insecurebankv2/DoLogin.java"の194行目からのpostDataメソッド、特に202行目の以下の一行が気になりました。
    if (DoLogin.this.username.equals("devadmin")) {

ユーザーネームにdevadminを入れるとサーバーの/devloginに接続するようです。
それ以外(通常)は/loginへ接続するようです。アプリで実際に試してみます。Usernameに"devadmin"とだけ入れてパスワードなしでLoginボタンを押しました。すると、ログインできることを確認できました。

まとめ

本記事では、InsecureBankv2という脆弱性を盛り込んだ学習用のAndroidアプリを紹介いたしました。本アプリは組み込まれた脆弱性があらかじめ提示されており、学習用途として解析の目標を明確にしやすいです。また解析が初めてでも容易に見つかる脆弱性が含まれていること、そして一部はWalkthroughという形で手法が紹介されており自分で手を動かしながら解析手法を学べるので、Androidアプリの解析が初めてでも楽しめるのではないでしょうか。さらに本アプリを練習台に様々な解析用のツールを試すこともできるでしょう。本アプリは、Androidアプリの基礎的なセキュリティ・解析手法を学ぶ最初の一歩として最適なのではないかと思います。本記事では静的解析を主に実施しましたが、次のステップとしてより深い静的解析や一部パラメータを書き換えて実際にアプリを動かしながら動的解析を実施して他の脆弱性を見つけていくことになります。動的解析にはFrida(new windowhttps://github.com/frida/frida)等のツールを使用できます。

参考

- InsecureBankv2
new windowhttps://github.com/dineshshetty/Android-InsecureBankv2
MIT License

- QARK(Quick Android Review Kit)
new windowhttps://github.com/linkedin/qark
Apache License, Version 2.0

- procyon
new windowhttps://github.com/ststeiger/procyon
Apache License, Version 2.0

- cfr
new windowhttps://github.com/leibnitz27/cfr
MIT License

- fernflower
new windowhttps://github.com/fesh0r/fernflower
Apache Licence, Version 2.0

- Android Studio
- Android Virtual Device Manager
new windowhttps://developer.android.com/studio?hl=ja
Apache 2.0

- Android SDK Platform-Tools(adb)
new windowhttps://developer.android.com/studio/releases/platform-tools.html
Apache 2.0

- Docker
new windowhttps://github.com/moby/moby
Apache License 2.0

- Alpine-python2(Dockerイメージ)
new windowhttps://github.com/Docker-Hub-frolvlad/docker-alpine-python2
MIT License

- QPFD
new windowhttps://github.com/qpdf/qpdf
Apache License, Version 2.0

- CyberChef
new windowhttps://github.com/gchq/CyberChef
Apache-2.0 License

- Apktool
new windowhttps://github.com/iBotPeaches/Apktool
Apache-2.0 License

- dex2jar
new windowhttps://github.com/pxb1988/dex2jar
Apache-2.0 License

- jadx
new windowhttps://github.com/skylot/jadx
Apache-2.0 License

- frida
new windowhttps://github.com/frida/frida
wxWindows Library Licence, Version 3.1

一般社団法人 日本スマートフォンセキュリティ協会
Androidアプリのセキュア設計・セキュアコーディングガイド
new windowhttps://www.jssec.org/dl/android_securecoding.pdf

執筆者プロフィール

中島 健児(なかしま けんじ)
セキュリティ技術センター リスクハンティングチーム

リスクハンティングチームにて、NECがお客様へ納品するシステム・製品へのリスクアセスメント/脆弱性検査/ペネトレーションテストを通じて、安全・安心なシステム構築を支援する業務に従事
情報処理安全確保支援士試験合格
CISSP Associateを保持