コンテンツにスキップ

Insecure Dynamic Library Loading

安全でない動的ライブラリの読み込み

説明

ライブラリの動的な読み込みを安全に実装しないと、アプリケーションのセキュリティと整合性に深刻なリスクをもたらす、さまざまな重大な脆弱性が発生する可能性があります。

安全でない読み込みパス: この脆弱性は、アプリケーションが信頼できない場所や操作された場所からライブラリを読み込む場合に発生します。アプリケーションが悪意のあるソースからライブラリを無意識に読み込むと、潜在的な攻撃への扉が開かれます。攻撃者は、信頼できない場所に悪意のあるライブラリを配置することでこの弱点をエクスプロイトし、昇格した特権でのコード実行や、機密リソースへの不正アクセスを引き起こす可能性があります。

ライブラリハイジャック: ライブラリハイジャックは、攻撃者が正当なライブラリを悪意のあるライブラリに置き換えた場合に発生します。ライブラリの読み込みプロセスを改ざんすることで、攻撃者はアプリケーションを騙して悪意のあるライブラリを読み込ませることができます。これにより、コードインジェクション、特権の昇格、機密データへの不正アクセスなど、幅広い攻撃が可能になります。

これらの脆弱性を放置すると、深刻な結果を招く可能性があります。攻撃者はアプリケーションに悪意のあるコードを挿入し、その整合性を侵害し、重要なリソースへの不正アクセスを取得して、他のセキュリティの脆弱性をエクスプロイトする可能性があります。

脆弱な実装の例:

void loadDependency(String unsanitized_user_input) {
  // 入力をサニタイズせずにライブラリを動的に読み込む
  final dylib = DynamicLibrary.open(unsanitized_user_input);

  // 悪意のあるコードを含む、読み込まれたライブラリから関数を解決して呼び出す
  final libraryMethod =
      dylib.lookupFunction<Pointer<Utf8> Function(), Pointer<Utf8> Function()>(
          'getSensitiveData');
  final result = libraryMethod();
}
func loadDynamicLibrary(unsanitized_user_input: String) {

    // ユーザー入力をサニタイズせずにライブラリを動的に読み込もうとする 
    if let libraryHandle = dlopen(unsanitized_user_input, RTLD_NOW) {
        // ライブラリが正常に読み込まれ、悪意のあるコードが含まれる可能性のある関数を解決する
        if let method = dlsym(libraryHandle, "libraryMethod") {
            typealias FunctionType = @convention(c) () -> String
            let function = unsafeBitCast(libraryMethod, to: FunctionType.self)
            let result = function()
        }
        dlclose(libraryHandle)
    }
}
fun loadDependency(unsanitized_user_input : String) {

    // 入力をサニタイズせずにライブラリを動的に読み込む
    System.load(unsanitized_user_input)

    // 動的に読み込まれたライブラリを使用して何らかの操作を実行する
    val result = libraryMethod()
}

推奨事項

動的ライブラリの読み込みを処理する場合、開発者は以下を行う必要があります。

  • ディレクトリトラバーサル(ディレクトリ・トラバーサル)やインジェクション攻撃を防ぐために、堅牢な入力検証およびサニタイズ技術を実装する。
  • 信頼できる検証済みのソースからのみライブラリを読み込み、ライブラリの整合性と信頼性を確保する。
  • 承認されていないライブラリの読み込みを防ぐために、厳格なアクセス制御を適用する。
  • 既知の脆弱性に対処するために、ライブラリを定期的に更新してパッチを適用する。
  • 強力なコード署名と整合性検証メカニズムを採用して、動的に読み込まれるライブラリの整合性を確保する。

安全な実装の例:

void validation(String input){

    // ライブラリファイルの整合性を確認する
    Uint8List fileBytes = libraryFile.readAsBytesSync();
    Digest fileDigest = SHA256Digest().process(fileBytes);
    Uint8List expectedChecksum = Uint8List.fromList([...]);

    if (fileDigest.bytes != expectedChecksum) {
        return false; // 無効なファイルチェックサム
    }

    if (/* .. パターンをチェックする */ )
        return false; // 無効なパス

    // 実装に固有の追加の検証チェックを追加する
    return true;
}

void loadDependency(String unsanitized_user_input) {

    // ユーザー入力をサニタイズする
    if (validation(unsanitized_user_input) == false)
        throw FormatException("invalid input");

    // 検証された入力でライブラリを動的に読み込む
    final dylib = DynamicLibrary.open(unsanitized_user_input);

    // 読み込まれたライブラリから関数を解決して呼び出す
    final libraryMethod = 
    dylib.lookupFunction<Pointer<Utf8> Function(), Pointer<Utf8> Function()>(
        'getSensitiveData');
    final result = libraryMethod();
}
func validation(unsanitized_user_input: String){

    guard let fileData = fileManager.contents(atPath: libraryURL.path) else {
        return false // ファイルデータの読み取りに失敗しました
    }
    let fileDigest = SHA256.hash(data: fileData)

    // 計算されたチェックサムと期待されるチェックサムを比較する
    let expectedChecksum: [UInt8] = [ /* 期待されるチェックサムに置き換えてください */ ]
    if fileDigest != expectedChecksum {
        return false // 無効なファイルチェックサム

    if (/* .. パターンをチェックする */ )
        return false; // 無効なパス

    // 実装に固有の追加の検証チェックを追加する
    return true;
  }
}

func loadDynamicLibrary(unsanitized_user_input: String) {

    // ユーザー入力を検証する
    if !validation(unsanitized_user_input){
      throw CustomValidationError.invalidInput
    }

    // 検証されたユーザー入力でライブラリを動的に読み込もうとする 
    if let libraryHandle = dlopen(unsanitized_user_input, RTLD_NOW) {
        // ライブラリが正常に読み込まれ、悪意のあるコードが含まれる可能性のある関数を解決する
        if let method = dlsym(libraryHandle, "libraryMethod") {
            typealias FunctionType = @convention(c) () -> String
            let function = unsafeBitCast(libraryMethod, to: FunctionType.self)
            let result = function()
        }
        dlclose(libraryHandle)
    }
}
fun validation(input : String){

    // ライブラリファイルの整合性を確認する
    val fileBytes = Files.readAllBytes(libraryFile.toPath())
    val digest = MessageDigest.getInstance("SHA-256")
    val fileDigest = digest.digest(fileBytes)
    val expectedChecksum = byteArrayOf(...)

    if (!fileDigest.contentEquals(expectedChecksum)) {
        return false
    }

    if (/* .. パターンをチェックする */ )
        return false  // 無効なパス

    // 実装に固有の追加の検証チェックを追加する
    return true
}

fun loadDependency(unsanitized_user_input : String) {

    // ユーザー入力を検証する
    if (!validation(unsanitized_user_input))
        throw IllegalArgumentException("Invalid input")

    // 検証された入力でライブラリを動的に読み込む
    System.load(unsanitized_user_input)

    // 動的に読み込まれたライブラリを使用して何らかの操作を実行する
    val result = libraryMethod()
}

リンク

標準

  • OWASP_MASVS_L1:
    • MSTG_PLATFORM_3
  • OWASP_MASVS_L2:
    • MSTG_PLATFORM_3
  • PCI_STANDARDS:
    • REQ_6_2
    • REQ_6_3
    • REQ_11_3
  • OWASP_MASVS_v2_1:
    • MASVS_CODE_4
  • SOC2_CONTROLS:
    • CC_2_1
    • CC_4_1
    • CC_7_1
    • CC_7_2
    • CC_7_4
    • CC_7_5
  • HIPAA_CONTROLS:
    • SECURITY212
    • SECURITY213
    • SECURITY255