| Título | Reader Tools All PDF Reader (com.pdf.viewer.pdftool.reader.document) Version 98.8 (versionCode 988) Path Traversal |
|---|
| Descripción | All PDF Reader for Android (package com.pdf.viewer.pdftool.reader.document, version 98.8 / versionCode 988, minSdkVersion 24, targetSdkVersion 35) is affected by path traversal (CWE-22) through an attacker-delivered content URI, an instance of the Android "Dirty Stream" pattern.
AFFECTED COMPONENT
The exported entry activity com.pdf.viewer.pdftool.reader.document.ui.activity.splash.ActSplashNew declares intent filters for ACTION_SEND, ACTION_SEND_MULTIPLE, ACTION_VIEW and ACTION_EDIT over a broad MIME set including application/pdf and application/octet-stream. Any installed application can send it a content:// URI.
ROOT CAUSE
When the activity receives a content URI it copies the referenced stream to internal storage using the URI's _display_name column verbatim as the on-disk filename. The filename is joined to a fixed base directory with no path canonicalization and no separator handling, so a _display_name containing "../" escapes the intended directory.
SOURCE TO SINK (verified at the bytecode level with androguard 4.1.4)
1. ActSplashNew.handleDeeplink() reads the URI from EXTRA_STREAM (SEND), ClipData / EXTRA_STREAM list item 0 (SEND_MULTIPLE), or getData() (VIEW), then calls DocumentViewModel.validateFileUri, which calls com.ezteam.baseproject.utils.fileUtils.GetRealPath.getPathFiles.
2. getPathFiles falls through to writeStreamToLocal when PathUtils.getPath returns null. A malicious provider returning a null cursor for the _data projection forces getPath to null. The sink is also reached directly when the storage-permission check is false.
3. writeStreamToLocal builds the destination and opens an output stream:
File out = new File(getFilesDir() + "/temp/" + File.separator + getFileName(uri));
FileOutputStream fos = new FileOutputStream(out); // no append, an existing target is truncated
It then copies the content stream into fos.
4. getFileName returns the raw _display_name value. The basename-normalization branch runs only when _display_name is null and is skipped when _display_name is present, so path separators survive. There is no canonicalization, no containment check, no allowlist, and no separator rejection.
IMPACT (confirmed on a physical device)
Private-directory write, no permissions required: a _display_name of "../../databases/document_database" resolves to /data/data/<pkg>/databases/document_database and overwrites the application's database. Verified: file owner u0_a405, size 109 bytes equal to the payload length, containing the attacker marker.
Shared-storage write: with MANAGE_EXTERNAL_STORAGE (declared by the application), a _display_name of "../../../../../../../../../storage/emulated/0/Download/DIRTYSTREAM_POC.txt" resolves to /storage/emulated/0/Download/DIRTYSTREAM_POC.txt and writes an attacker-controlled file to shared storage. Verified on device.
In both cases the write executes as the target application's UID with attacker-controlled file contents.
PROOF OF CONCEPT
A malicious application provides a ContentProvider that returns the traversal string "../../../../../../../../../storage/emulated/0/Download/DIRTYSTREAM_POC.txt" for the _display_name query, a null cursor for the _data query, and the payload bytes from openFile, and an activity that sends ACTION_SEND with the provider URI in EXTRA_STREAM to the exported component. On-device confirmation for the shared-storage case:
grep -rl <marker> -> /sdcard/Download/DIRTYSTREAM_POC.txt
ls -la /sdcard/Download/DIRTYSTREAM_POC.txt -> 109 DIRTYSTREAM_POC.txt
realpath (files/temp/ + 9x "../" + storage/emulated/0/Download/DIRTYSTREAM_POC.txt) -> /storage/emulated/0/Download/DIRTYSTREAM_POC.txt
REMEDIATION
Reduce _display_name to a bare filename unconditionally (new File(name).getName()) and reject any value containing a path separator. Enforce canonical containment of the destination under the temp base directory before writing. Prefer a random, content-independent filename for the copied stream. The vulnerable code resides in the shared com.ezteam.baseproject library |
|---|
| Fuente | ⚠️ https://docs.google.com/document/d/1g8q6UDV2EBm0UfEz2tc1eVBSxXqX5Zk8Ei30FuKAYa8/edit?usp=sharing |
|---|
| Usuario | Actuator (UID 67941) |
|---|
| Sumisión | 2026-07-11 07:01 (hace 2 meses) |
|---|
| Moderación | 2026-09-02 15:10 (2 months later) |
|---|
| Estado | Aceptado |
|---|
| Entrada de VulDB | 398130 [Reader Tools PDF Reader App 98.8 en Android File ActSplashNew.handleDeeplink _display_name recorrido de directorios] |
|---|
| Puntos | 20 |
|---|