Skip to content

Path traversal via content provider DISPLAY_NAME #75

@pubdev-research

Description

@pubdev-research

Summary

The flutter_sharing_intent plugin (v2.0.4, Android) contains a path traversal vulnerability (CWE-23) where file names obtained from content providers are used directly in java.io.File constructors without sanitization. A malicious app sharing content via Intent.ACTION_SEND can supply a crafted DISPLAY_NAME containing path traversal sequences (e.g., ../../) to write files outside the intended cache directory.

Affected Code

MyFileDirectory.ktgetDataColumn() (line 107–110)

The _display_name from a content provider query is used directly as a file name:

val columnIndex = cursor.getColumnIndexOrThrow(column)  // "_display_name"
val fileName = cursor.getString(columnIndex)
Log.i("FileDirectory", "File name: $fileName")
targetFile = File(context.cacheDir, fileName)

The file content is then written to this unsanitized path:

  context.contentResolver.openInputStream(uri)?.use { input ->
      FileOutputStream(targetFile).use { fileOut ->
          input.copyTo(fileOut)
      }
  }

A malicious content provider can return a DISPLAY_NAME like ../../shared_prefs/malicious.xml, causing the file to be written outside cacheDir.

Entry point: FlutterSharingIntentPlugin.kt (line 151–152)

  val uri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
  val path = uri?.let{ MyFileDirectory.getAbsolutePath(applicationContext, it) }

The URI from an incoming share intent is passed directly to getAbsolutePath() without validation.

Attack Scenario

  1. A malicious app shares a file to the victim app (which uses flutter_sharing_intent)
  2. The malicious app's content provider returns ../../shared_prefs/config.xml as DISPLAY_NAME
  3. flutter_sharing_intent writes the shared content to {cacheDir}/../../shared_prefs/config.xml, escaping the cache directory
  4. The attacker can overwrite app files within the writable scope

Suggested Fix

Sanitize file names before using them in File constructors:

  val safeName = File(fileName).name  // strips path components

// Or validate canonical path

  val targetFile = File(context.cacheDir, fileName)
  if (!targetFile.canonicalPath.startsWith(context.cacheDir.canonicalPath)) {
      throw SecurityException("Path traversal detected")
  }

Environment

  • flutter_sharing_intent version: 2.0.4 (latest on pub.dev)
  • Platform: Android
  • Tested on Android emulator (API 34)

References

  • CWE-23: Relative Path Traversal
  • OWASP Path Traversal

Offer

If you'd like, I can submit a pull request with the fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions