CVE-2017-20181: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: A vulnerability classified as critical was found in hgzojer Vocable Trainer up to 1.3.0 on Android. This vulnerability affects unknown code of the file src/at/hgz/vocabletrainer/VocableTrainerProvider.java. The manipulation leads to path traversal. Attacking locally is a requirement. Upgrading to version 1.3.1 is able to address this issue. The name of the patch is accf6838078f8eb105cfc7865aba5c705fb68426. It is recommended to upgrade the affected component. The identifier of this vulnerability is VDB-222328.
CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVSS v3 base score: 5.5
Published: 2023-03-07T00:15Z
Repository: https://github.com/hgzojer/vocabletrainer
Language: XML
Files changed: AndroidManifest.xml, src/at/hgz/vocabletrainer/VocableTrainerProvider.java
Commit date: 2017-10-01 10:46:03 +0200
Commit message: Bugfix Path Traversal Vulnerability - see https://support.google.com/faqs/answer/7496913

Diff: diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index df270b3..ce75a73 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="at.hgz.vocabletrainer"
-    android:versionCode="9"
-    android:versionName="1.3.0" >
+    android:versionCode="10"
+    android:versionName="1.3.1" >
 
     <uses-sdk
         android:minSdkVersion="14"
         android:targetSdkVersion="21" />
 
diff --git a/src/at/hgz/vocabletrainer/VocableTrainerProvider.java b/src/at/hgz/vocabletrainer/VocableTrainerProvider.java
index 1b81786..77f204b 100755
--- a/src/at/hgz/vocabletrainer/VocableTrainerProvider.java
+++ b/src/at/hgz/vocabletrainer/VocableTrainerProvider.java
@@ -1,9 +1,10 @@
 package at.hgz.vocabletrainer;
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
@@ -11,15 +12,23 @@ import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
 public class VocableTrainerProvider extends ContentProvider {
 	
 	@Override
-	public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {       
-	     File cacheDir = getContext().getCacheDir();
-	     File privateFile = new File(cacheDir, uri.getLastPathSegment());
+	public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+		try {
+			String cacheDir = getContext().getCacheDir().toString();
+			File privateFile = new File(cacheDir, uri.getLastPathSegment());
 
-	     return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY);
+			if (!privateFile.getCanonicalPath().startsWith(cacheDir)) {
+				throw new IllegalArgumentException();
+			}
+
+			return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY);
+		} catch (IOException e) {
+			throw new RuntimeException(e.getMessage(), e);
+		}
 	}
 	
 	@Override
 	public int delete(Uri arg0, String arg1, String[] arg2) {
 		// TODO Auto-generated method stub

Vulnerable code:     android:versionCode="9"
    android:versionName="1.3.0" >
	public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {       
	     File cacheDir = getContext().getCacheDir();
	     File privateFile = new File(cacheDir, uri.getLastPathSegment());
	     return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY);
Fixed code:     android:versionCode="10"
    android:versionName="1.3.1" >
import java.io.IOException;
	public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
		try {
			String cacheDir = getContext().getCacheDir().toString();
			File privateFile = new File(cacheDir, uri.getLastPathSegment());
			if (!privateFile.getCanonicalPath().startsWith(cacheDir)) {
				throw new IllegalArgumentException();
			}

			return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY);
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage(), e);
		}