MobSFがCI上で動かないからセキュリティ診断をしてみる(Flutter/Quark-Engine/Google apkanalyzer)
2025-09-16
azblob://2025/09/16/eyecatch/2025-09-16-azurepipeline-app-mobsf-000.jpeg

脆弱性診断を行うために、MobSFをDockerコンテナで起動し、ビルドしたAPKを自動でアップロード・スキャンし、スキャン結果をJSON形式で取得してアーティファクトとして保存するまでの構成を書きます。

こんなことを前のBlogで言っていましたが、MobSFの可変するAPIも取れているのになぜか落ちる。。。と言うのが続いていたため、脆弱性診断から、セキュリティ診断に切り替えました。

なぜMobSFが動かせなかったかの調査結果と、セキュリティ診断のスクリプトを紹介します。

正直、セキュリティ診断は本当にしょうもないというか、ファイルの誤爆検知レベルのことしかできていないなという印象なので、誰の参考にもならないと思いますが、前回構成を書くと宣言しているのと、MobSFのREST APIキーをログから取得し、APIキーを渡す箇所が以下のスクリプトの構成であれば安定稼働できるはずなので誰かのために出します。

APKのビルドはこちらの記事で記述しているため、割愛します。

まず、MobSFとは何か、MobSF CLIとの違いについて説明します。

MobSF(Mobile Security Framework)でできること


MobSFはモバイルアプリ(Android/iOS/Windows)向けのセキュリティ解析プラットフォームです。
MobSF CLIはMobSFサーバーのAPIをコマンドラインから操作するラッパーツールです。
MobSFサーバーが起動していれば、CLIやAPI経由で以下の機能を利用できます。


1.静的解析(Static Analysis)

  • APK/IPA/XAPファイルやソースコード(Java/Kotlin/Swift/Objective-C)を解析
  • 危険なパーミッション、インテント、サービス、レシーバー、アクティビティの検出
  • マニフェストや設定ファイルのセキュリティリスク抽出
  • 暗号化・証明書・ハードコードされたキーやパスワードの検出
  • APIキーや認証情報の漏洩チェック
  • 依存ライブラリの脆弱性チェック(CVE照合)
  • サードパーティSDKの検出
  • コードの難読化・デバッグ・バックアップ設定の検出

2.動的解析(Dynamic Analysis)

  • Androidエミュレータ上でアプリを実行し、動作を監視
  • ネットワーク通信のキャプチャ・解析(HTTP/HTTPSリクエスト、レスポンス内容)
  • ファイルシステム・データベースアクセスの監視
  • インテント送信・受信の監視
  • アプリの挙動に対する自動化テスト(UI操作の自動化)

3.API連携・自動化

  • REST API経由で解析ジョブの投入・結果取得が可能
  • MobSF CLIでAPIキーを使い、解析・レポート取得・ファイルダウンロードなどを自動化

4.脅威インテリジェンス・レポート生成

  • 詳細なHTML/PDF/JSONレポートの自動生成
  • CVSSスコアや脆弱性情報の一覧化
  • セキュリティリスクの優先度付け

5.その他の機能

  • ソースコードのセキュリティLint
  • バイナリの逆コンパイル・DEX解析
  • iOSアプリのPlist・Entitlements・証明書解析
  • Windowsアプリ(UWP/XAP)の静的解析

MobSF CLIでできること(MobSFサーバー必須)


MobSFサーバーをコマンドラインから操作するためのラッパーツールです。MobSFサーバーが起動していることが前提で、CLIからAPIを叩いて解析ジョブを自動化します。MobSF CLI単体では解析できず、必ずMobSFサーバーが必要です。


  • MobSFサーバーのAPIキーを使い、コマンドラインから
    • ファイルアップロード
    • 静的解析・動的解析の実行
    • レポートの取得・ダウンロード
    • 解析結果の一覧取得・削除
    • MobSFサーバーの各種API操作を行います。

MobSFが動かせない理由・原因・対処法


私の場合はリソース制約に伴い可変するAPIの管理ができなかったことが原因でした。

Azure DevOps Hosted Agentは短時間で破棄される一時VMであり、リソース制限が厳しく、MobSFの解析処理が途中で落ちる・タイムアウトしてしまい、MobSFサーバーの永続化ができないため、APIの管理が不安定になり動かない。という状態に陥っていました。

MobSFはSelf-hosted Agentでの運用が前提であるとわかりましたが、ここについての知見がないため、断念しました。

MobSFを起動できない場合には以下の項目をチェックしてみてほしいです。

 

ネットワーク・ポート制約


  • MobSFはWebサーバー(デフォルトでlocalhost:8000)を起動し、APIやWeb UI経由で操作します。
  • Hosted Agentでは外部からのポートアクセスやローカルサーバーの永続化ができません。
  • MobSF CLIも内部でWebサーバーを起動するため、CI環境のネットワーク制約で通信が遮断されることがあります。

ファイルシステム・永続化の問題


  • MobSFは解析結果や一時ファイルをローカルディスクに大量に書き込むため、CIの一時環境ではディスク容量不足やファイル消失が発生しやすいです。

MobSF APIキーの可変・管理の難しさ


  • MobSFのAPIキーはMobSFサーバー起動ごとに再生成される場合があり、CI環境で安定してAPIキーを取得・利用するのが難しいです。
  • Hosted AgentではMobSFサーバーの永続化ができないため、APIキーの管理が不安定になります。

MobSFの依存パッケージ・環境構築の複雑さ


  • MobSFはPython、Node.js、Java、Android SDKなど多くの依存パッケージやツールが必要です。
  • CI環境でこれらを全て正しくセットアップするのは非常に手間がかかり、バージョン不一致やインストール失敗が起こりやすい。

以下のスクリプトはSelf-hosted Agentでは動くはず。。。。泣

自分は使えなかったですが、誰かの参考になれば嬉しいです。

trigger:
  - main
  - develop

pr:
  branches:
    include:
      - '*'

variables:
  - group: NATIVE_APP
  - name: APK_PATH
    value: 'artifacts/app-x86_64-release.apk'

pool:
  vmImage: 'ubuntu-latest'

jobs:
- job: BuildAndScan
  pool:
    vmImage: 'ubuntu-latest'
  steps:
    - script: |
        set -x

        # システム情報の確認(メモリ・ディスク・CPU使用率)
        sudo apt-get update
        sudo apt-get install -y jq

        echo "=== Host memory ==="
        free -h
        echo "=== Host disk ==="
        df -h
        echo "=== Host top ==="
        top -b -n 1 | head -20

        # MobSFのDockerイメージを取得・起動
        docker pull opensecurity/mobile-security-framework-mobsf:v4.3.0
        docker run -d -p 8000:8000 --name mobsf opensecurity/mobile-security-framework-mobsf:v4.3.0

        # MobSFの初期化完了まで待機(最大60回、2秒間隔)
        for i in {1..60}; do
          if docker logs mobsf 2>&1 | grep -q "Roles Created Successfully!"; then
            echo "MobSF is fully ready."
            break
          fi
          echo "Waiting for MobSF full initialization... ($i/60)"
          sleep 2
        done

        # MobSFが初期化できなかった場合はエラー出力して終了
        if ! docker logs mobsf 2>&1 | grep -q "Roles Created Successfully!"; then
          echo "MobSF did not finish initialization."
          docker logs mobsf
          exit 1
        fi

        # MobSFのREST APIキーをログから取得
        MOBSF_API_KEY=$(docker logs mobsf 2>&1 | grep 'REST API Key:' | tail -1 | awk '{print $4}' | tr -d '\r\n ')
        echo "Latest MobSF API Key: [$MOBSF_API_KEY]"

        # APIキーが取得できなければエラー
        if [ -z "$MOBSF_API_KEY" ]; then
          echo "API key not found after full init."
          docker logs mobsf
          exit 1
        fi

        # MobSF APIサーバーの安定化待機(追加20秒)
        echo "Waiting extra 20 seconds for MobSF API server to be fully ready..."
        sleep 20

        # MobSFコンテナ内のメモリ・CPU使用率確認
        echo "=== MobSF container memory ==="
        docker exec mobsf free -h || true
        echo "=== MobSF container top ==="
        docker exec mobsf top -b -n 1 | head -20 || true

        # APKファイルの存在確認
        if [ ! -f "${APK_PATH}" ]; then
          echo "APK file not found: ${APK_PATH}"
          ls -l artifacts
          exit 1
        fi

        # MobSFログの最新30行を表示
        echo "MobSF log before upload (last 30 lines):"
        docker logs mobsf --tail 30

        # MobSF APIへAPKファイルをアップロード(最大20回リトライ)
        for i in {1..20}; do
          echo "Uploading APK... (try $i/20)"
          UPLOAD_RESPONSE=$(curl -v -s -w "%{http_code}" -F "file=@${APK_PATH}" -H "X-Mobsf-Api-Key: $MOBSF_API_KEY" http://localhost:8000/api/v1/upload 2>&1)
          HTTP_CODE=$(echo "$UPLOAD_RESPONSE" | tail -c 3)
          BODY=$(echo "$UPLOAD_RESPONSE" | head -c -3)
          echo "HTTP_CODE: $HTTP_CODE"
          echo "BODY: $BODY"
          if [ "$HTTP_CODE" = "200" ] && ! echo "$BODY" | grep -q "error"; then
            echo "Upload succeeded."
            break
          fi
          if echo "$BODY" | grep -qi "unauthorized"; then
            echo "MobSF not ready yet, waiting 10s..."
            sleep 10
          else
            echo "Upload failed: $BODY"
            docker logs mobsf --tail 100
            exit 1
          fi
          if [ "$i" -eq 20 ]; then
            echo "MobSF never became ready for upload."
            docker logs mobsf --tail 100
            exit 1
          fi
        done

        # アップロード結果から解析ハッシュ(scan hash)を取得
        SCAN_HASH=$(echo "$BODY" | jq -r '.hash')
        if [ -z "$SCAN_HASH" ] || [ "$SCAN_HASH" = "null" ]; then
          echo "Failed to get scan hash."
          docker logs mobsf --tail 100
          exit 1
        fi

        # MobSF APIで静的解析を開始
        echo "Starting scan..."
        SCAN_RESPONSE=$(curl -s -X POST -H "X-Mobsf-Api-Key: $MOBSF_API_KEY" -H "Content-Type: application/x-www-form-urlencoded" -d "hash=$SCAN_HASH" http://localhost:8000/api/v1/scan)
        echo "Scan response: $SCAN_RESPONSE"

        # MobSF APIから解析レポート(JSON)を取得・保存
        echo "Fetching report..."
        curl -s -H "X-Mobsf-Api-Key: $MOBSF_API_KEY" -H "Content-Type: application/x-www-form-urlencoded" -d "hash=$SCAN_HASH" http://localhost:8000/api/v1/report_json -o mobsf_report.json
        echo "Report saved to mobsf_report.json"

        # MobSFの最新ログ100行を表示
        docker logs mobsf --tail 100

        # MobSFコンテナの停止・削除(クリーンアップ)
        docker stop mobsf
        docker rm mobsf
      displayName: 'Run MobSF scan via API and cleanup'

あまりにも悔しいのと、嫌すぎて本題の紹介するの忘れてました(^^)

セキュリティリスク評価スクリプトで使用した2つのツールを紹介します。(^O^)

Quark-Engineによるマルウェアスキャン


Quark-Engineは、Androidアプリ(APKファイル)を対象にしたマルウェア・脅威インジケータ検知ツールです。

  • APKファイルを静的解析し、悪意のあるコードや挙動(マルウェアの特徴)を検出します。
  • 独自のルールセット(quark-rules)を使い、既知のマルウェアパターンや危険なAPI呼び出し、疑わしい権限要求などを自動判定します。
  • 結果はJSON形式でレポート出力され、CI/CDパイプラインで自動化・保存が可能です。

Google apkanalyzerによるパーミッションチェック


apkanalyzerはGoogle公式のコマンドラインツールで、APKファイルの構造やメタ情報を解析できます。

  • apkanalyzer manifest permissions [APKファイル] コマンドで、アプリが要求する全パーミッション一覧を抽出します。
  • 危険なパーミッション(例:カメラ、位置情報、連絡先など)や不要な権限の有無を確認できます。
  • CI/CDパイプラインで自動実行し、結果(permissions.txt)をアーティファクトとして保存できます。

セキュリティリスクの早期発見に役立つぐらいの効果しかないです。

APKビルドスクリプトの後に追加すれば使えます。

      - script: |
          pip install quark-engine
          git clone https://github.com/quark-engine/quark-rules.git
          APK_PATH=$(find build/app/outputs/flutter-apk/ -name "*.apk" | head -1)
          echo "APK_PATH=$APK_PATH"
          quark -a "$APK_PATH" -r ./quark-rules -o quark_report.json
        displayName: 'Run Quark-Engine malware scan'

      - task: PublishBuildArtifacts@1
        inputs:
          PathtoPublish: 'quark_report.json'
          ArtifactName: 'quark-report'
        displayName: 'Publish Quark-Engine Report'

      # Google apkanalyzerによるパーミッションチェック
      - script: |
          export ANDROID_SDK_ROOT=$HOME/android-sdk
          export PATH=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$PATH
          APK_PATH=$(find build/app/outputs/flutter-apk/ -name "*.apk" | head -1)
          echo "APK_PATH=$APK_PATH"
          ls $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/
          which apkanalyzer
          apkanalyzer manifest permissions "$APK_PATH" > permissions.txt
        displayName: 'Run APK Analyzer'

      - task: PublishBuildArtifacts@1
        inputs:
          PathtoPublish: 'permissions.txt'
          ArtifactName: 'apk-permissions'
        displayName: 'Publish APK Permissions Report'

まとめ


APKの脆弱性検知やりたかった。