Cannot get the real file path - Android Oreo 8+ - android

I'm downloading a file using Download Manager and saving into Download Folder.
After download finish i'm picking up the folder path like this way:
int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
String downloadedPackageUriString = cursor.getString(uriIndex);
Then i need to use this path to unzip the file downloaded. The code to unzip it is below:
SouceFile is the path from downloadmanager.
unzip(String sourceFile, String destinationFolder)
try {
zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(sourceFile)));
ZipEntry ze;
int count;
byte[] buffer = new byte[BUFFER_SIZE];
while ((ze = zis.getNextEntry()) != null) {
String fileName = ze.getName();
fileName = fileName.substring(fileName.indexOf("/") + 1);
File file = new File(destinationFolder, fileName);
File dir = ze.isDirectory() ? file : file.getParentFile();
Log.i("MainService", "Unzipping fileName: " + fileName);
file_path = destinationFolder + "/" + fileName;
if (!dir.isDirectory() && !dir.mkdirs())
throw new FileNotFoundException("Invalid path: " + dir.getAbsolutePath());
if (ze.isDirectory()) continue;
FileOutputStream fout = new FileOutputStream(file);
try {
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
} finally {
fout.close();
}
list_filenames.add(file_downloaded);
}
Log.d("MainService", "TAM:" + tam);
} catch (IOException ioe) {
Log.d("MainService", "Oiiiiiiiiii " + ioe);
return list_filenames;
} finally {
if (zis != null)
try {
zis.close();
} catch (IOException e) {
}
}
The code to unzip works on Android 6 (My phone), but on Android Oreo + it doesnt.
I'm getting (No such file or directory) from zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(sourceFile)));
Reinforcing: I'm not getting this error on Android 6. Just on 8+
Thanks for any help.
I tried some suggestions from other guys here with similar problem, but doesnt works for me.

I'm not getting this error on Android 6
At most, you are not getting this error on the one device that you tested on Android 6.0. There are many device models running Android 6.0, not just one.
COLUMN_LOCAL_URI is supposed to give you a string representation of a Uri. That will not work with FileInputStream, because a Uri is not a file.
Replace:
zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(sourceFile)));
with:
zis = new ZipInputStream(new BufferedInputStream(cr.openInputStream(Uri.parse(sourceFile))));
...where cr is a ContentResolver that you get by calling getContentResolver() on some Context.

Related

Fixing a Zip Path Traversal Vulnerability In Android

I have uploaded My Application in Google Play Store and Google has given warning that is "Android Security".
In Application, we downloaded the Zip folder and save this Zip folder in internal Storage and than unZip that folder in internal Storage of device.
here is the UnZip Folder Code:
public static void doUnzip(String inputZipFile, String
destinationDirectory, ZipProgressListener zipProgressListener) throws
IOException, RuntimeException {
Log.e(TAG, "doUnzip:inputZipFile: " + inputZipFile);
Log.e(TAG, "doUnzip:destinationDirectory: " + destinationDirectory);
int BUFFER = 6 * 1024;
List zipFiles = new ArrayList();
File sourceZipFile = FileUtils.createValidFile(inputZipFile);
File unzipDestinationDirectory =
FileUtils.createValidFile(destinationDirectory);
unzipDestinationDirectory.mkdir();
String newPath = unzipDestinationDirectory.getAbsolutePath() +
File.separator +
FileUtils.getFileNameWithoutExtension(sourceZipFile.getName());
new File(newPath).mkdir();
ZipFile zipFile;
// Open Zip file for reading
zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);
int entries = zipFile.size();
int total = 0;
Log.e(TAG, "doUnzip: entries Found !!" + entries);
// Create an enumeration of the entries in the zip file
Enumeration zipFileEntries = zipFile.entries();
if (zipProgressListener != null) {
zipProgressListener.onZipStart();
}
// Process each entry
while (zipFileEntries.hasMoreElements()) {
// grab a zip file entry
ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
String currentEntry = entry.getName();
Log.i(TAG, "[doUnzip] " + currentEntry);
File file = new File(newPath);
File destFile = new File(newPath, currentEntry);
Log.i(TAG, "doUnzip getCanonicalPath : " +
destFile.getCanonicalPath());
if (Build.VERSION.SDK_INT <= VERSION_CODES.LOLLIPOP) {
Log.i(TAG, "doUnzip: LOLLIPOP");
if
(!destFile.getCanonicalPath().startsWith(destinationDirectory)) {
throw new RuntimeException(destFile.getCanonicalPath() +
" is outside of targetDirectory: " + destinationDirectory);
}
} else {
Log.i(TAG, "doUnzip: Above ");
if(!destFile.getCanonicalPath().contains(file.getName()) &&
!destFile.getCanonicalPath().contains("/")){
throw new RuntimeException(destFile.getCanonicalPath() +
" is outside of targetDirectory: " + destinationDirectory);
}
}
if (currentEntry.endsWith(".zip")) {
zipFiles.add(destFile.getAbsolutePath());
}
// grab file's parent directory structure
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
try {
// extract file if not a directory
if (!entry.isDirectory()) {
BufferedInputStream is = new
BufferedInputStream(zipFile.getInputStream(entry));
int currentByte;
// establish buffer for writing file
byte data[] = new byte[BUFFER];
// write the current file to disk
FileOutputStream fos = new FileOutputStream(destFile);
BufferedOutputStream dest = new BufferedOutputStream(fos,
BUFFER);
// read and write until last byte is encountered
while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
dest.write(data, 0, currentByte);
}
Log.e(TAG, "unzip:outPath: =>" +
destFile.getAbsolutePath() + "\nFile size: " + destFile.length()
/ 1024);
dest.flush();
dest.close();
is.close();
}
int progress = 0;
if (zipProgressListener != null) {
progress = (total++ * 100 / entries);
zipProgressListener.onZipProgressUpdate(progress);
}
Log.e(TAG, "unzip: PROGRESS::" + progress);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
zipFile.close();
for (Object zipFile1 : zipFiles) {
String zipName = (String) zipFile1;
Log.i(TAG, "doUnzip: ");
doUnzip(zipName, destinationDirectory + File.separator +
zipName.substring(0, zipName.lastIndexOf(".zip")),
zipProgressListener);
}
if (zipProgressListener != null) {
Log.i(TAG, "doUnzip: " + sourceZipFile.getName());
zipProgressListener.onZipCompleted(destinationDirectory +
File.separatorChar + sourceZipFile.getName().substring(0,
sourceZipFile.getName().lastIndexOf(".zip")));
}
}
Here is Google warning :
This information is intended for developers with the app(s) that contain unsafe unzipping patterns, which may potentially lead to a Zip Path Traversal attack. Locations of vulnerable app classes containing unsafe unzipping patterns can be found in the Play Console notification for your app.
Additional details
Zip files can contain an entry (file or directory) having path traversal characters (“../”) in its name. If developers unzip such zip file entries without validating their name, it can potentially cause a path traversal attack, leading to writes in arbitrary directories or even overwriting the files in the app's private folders.
We recommend fixing this issue in your app by checking if canonical paths to unzipped files are underneath an expected directory. Specifically, before using a File object created using the return value of ZipEntry's getName() method, always check if the return value of File.GetCanonicalPath() belongs to the intended directory path. For example:
InputStream is = new InputStream(untrustedFileName);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
while((ZipEntry ze = zis.getNextEntry()) != null) {
File f = new File(DIR, ze.getName());
String canonicalPath = f.getCanonicalPath();
if (!canonicalPath.startsWith(DIR)) {
// SecurityException
}
// Finish unzipping…
}
How can I solve this warning in Above Android OS-6?
Check vulnerability like this
InputStream is = new InputStream(untrustedFileName);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
while((ZipEntry ze = zis.getNextEntry()) != null) {
File outputFile = new File(outputDir, ze.getName());
try {
ensureZipPathSafety(outputFile, outputDir);
} catch (Exception e) {
e.printStackTrace();
return;
}
// Finish unzipping…
}
private void ensureZipPathSafety(final File outputFile, final String destDirectory) throws Exception {
String destDirCanonicalPath = (new File(destDirectory)).getCanonicalPath();
String outputFilecanonicalPath = outputFile.getCanonicalPath();
if (!outputFileCanonicalPath.startsWith(destDirCanonicalPath)) {
throw new Exception(String.format("Found Zip Path Traversal Vulnerability with %s", canonicalPath));
}
}

Download(copy?) a file from my res/raw folder to the default Android download location?

I am making a soundboard for practice and I want to give the user the ability to download the sound (that I have included in the app in the res/raw folder) onClick of a menu item but I can only find information about downloading from an internet url, not something that I already included in the apk.
What is the best way to do this? I would like to give them the option to save to an SD card also if this is possible. A point towards the correct class to use in the documentation would be great! I've been googling to no avail.
Thanks!
Try something like this:
public void saveResourceToFile() {
InputStream in = null;
FileOutputStream fout = null;
try {
in = getResources().openRawResource(R.raw.test);
String downloadsDirectoryPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
String filename = "myfile.mp3"
fout = new FileOutputStream(new File(downloadsDirectoryPath + filename));
final byte data[] = new byte[1024];
int count;
while ((count = in.read(data, 0, 1024)) != -1) {
fout.write(data, 0, count);
}
} finally {
if (in != null) {
in.close();
}
if (fout != null) {
fout.close();
}
}
}
I don't know about the raw but I did a similar thing in my app using the assets folder. My files are under the assets/backgrounds folder as you can probably guess from the code below.
You can modify this code and make it work for you (I know I will only have 4 files which is why I have i go from 0 to 4 but you can change this to whatever you want).
This code copies the file starting with prefix_ (like prefix_1.png, prefix_2.png, etc) to my cache directory but you can obviously change the extension, the filename or the path you would like to save the assets to.
public static void copyAssets(final Context context, final String prefix) {
for (Integer i = 0; i < 4; i++) {
String filename = prefix + "_" + i.toString() + ".png";
File f = new File(context.getCacheDir() + "/" + filename);
if (f.exists()) {
f.delete();
}
if (!f.exists())
try {
InputStream is = context.getAssets().open("backgrounds/" + filename);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
FileOutputStream fos = new FileOutputStream(f);
fos.write(buffer);
fos.close();
} catch (Exception e) {
Log.e("Exception occurred while trying to load file from assets.", e.getMessage());
}
}
}

Files.list returns null for android data/data device is rooted

I'm trying to generate a list of all the files in a particular directory in /data/data/ e.g. /data/data/com.package.ect so that I can zip them. My device is rooted and I've granted Super User to my app. When pass the path to the directory it gets past the if statement, so it recognises that what I'm trying to access is a directory, yet File.list() returns null. I'm assuming I'm still lacking some sort of permission.
/**
* Traverse a directory and get all files,
* and add the file into fileList
* #param node file or directory
*/
public void generateFileList(File node) {
//add file only
if (node.isFile()) {
fileList.add(generateZipEntry(node.getAbsoluteFile().toString()));
}
if (node.isDirectory()) {
String[] subNote = node.list();
for (String filename : subNote) {
generateFileList(new File(node, filename));
}
}
}
*EDIT: As requested where generateFileList is called
public void zipDirectory(String outputPath){
byte[] buffer = new byte[1024];
try{
checkStorageDirExists(SDCARD, STORAGE_LOCATION);
generateFileList(new File(path));
FileOutputStream fos = new FileOutputStream(SDCARD + STORAGE_LOCATION + outputPath);
ZipOutputStream zos = new ZipOutputStream(fos);
setZipFileName(path);
Log.i(TAG, "Output to Zip : " + outputPath);
for(String file : this.fileList){
System.out.println("File Added : " + file);
ZipEntry ze= new ZipEntry(file);
zos.putNextEntry(ze);
FileInputStream in =
new FileInputStream(path + "/" + file);
int len;
while ((len = in.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
in.close();
}
zos.closeEntry();
zos.close();
Log.i(TAG, "Zip of " + getZipFileName() +" Completed");
}catch(IOException ex){
ex.printStackTrace();
}

Android unzip open failed: ENOENT (No such file or directory)

I am using the following code to unzip a set of files (containing folders as well):
private boolean unpackZip(String path, String zipname)
{
InputStream is;
ZipInputStream zis;
try
{
String filename;
is = new FileInputStream(path + zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null)
{
// zapis do souboru
filename = ze.getName();
// Need to create directories if not exists, or
// it will generate an Exception...
if (ze.isDirectory()) {
File fmd = new File(path + filename);
fmd.mkdirs();
continue;
}
FileOutputStream fout = new FileOutputStream(path + filename);
// cteni zipu a zapis
while ((count = zis.read(buffer)) != -1)
{
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
The code fails on FileOutputStream fout = new FileOutputStream(path + filename) with the error:
java.io.FileNotFoundException: /storage/emulated/0/BASEFOLDER/FOLDER1/FILE.png
BASEFOLDER already exists, that is where I am trying to unzip the folder to. If I manually (or programmatically) create FOLDER1, the code runs fine and successfully unzips. I believe it is crashing because the very first file (ze) is named FOLDER1/FILE.png and FOLDER1 hasn't been created yet. How do I get around this? I know other people have used this code, I find it unlikely that it randomly doesn't work for me...
Have you got this in your AndroidManifest.xml file?
Add Write to external Storage permission
uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
I had the same problem. After several investigation I found that. put following single line in your code:
if (ze.isDirectory()) {
File fmd = new File(path + filename);
fmd.mkdirs();
zis.closeEntry(); // <<<<<< ADD THIS LINE
continue;
}
Sometime the extract files has been extracted before its parent directory is created, for example:
File A inside directory B. But B directory is not created, index of files listing below cause the issue:
dir_b/file_a.txt
dir_b/
dir_b/file_c.txt
So, to sure directory created before file extracting, you need to create parent directories first, for example:
val targetFile = File(tempOutputDir, zipEntry.name)
if (zipEntry.isDirectory) {
targetFile.mkdirs()
} else {
try {
try {
targetFile.parentFile?.mkdirs() // <-- ADD THIS LINE
} catch (exception: Exception) {
Log.e("ExampleApp", exception.localizedMessage, exception)
}
val bufferOutputStream = BufferedOutputStream(
FileOutputStream(targetFile)
)
val buffer = ByteArray(1024)
var read = zipInputStream.read(buffer)
while (read != -1) {
bufferOutputStream.write(buffer, 0, read)
read = zipInputStream.read(buffer)
}
bufferOutputStream.close()
} catch (exception: Exception) {
Log.e("ExampleApp", exception.localizedMessage, exception)
}
}

Android: Unzipping files throws data errors or CRC errors

I'm working on a project that downloads a zip file and unzips locally. The issue I'm hitting is that the unzip process works like 5% of the time.
It's a mystery to me at this point because sometimes it works, but most of the time it throws data or crc errors. It'll even switch between erros even though the zip file hasn't changed.
I've tried zip files that were created by numerous tools wondering if the format was incorrect. But to no avail. Even zips created in the terminal don't work.
Here's my unzipping code:
try {
String _location = model.getLocalPath();
FileInputStream fin = new FileInputStream(localFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
byte[] buffer = new byte[1024];
while((ze = zin.getNextEntry()) != null) {
if(_cancel) break;
System.out.println("unzipping " + ze.getName());
if(ze.isDirectory()) {
File f = new File(_location + ze.getName());
f.mkdirs();
} else {
FileOutputStream fout = new FileOutputStream(_location + ze.getName());
for(int c = zin.read(buffer); c > 0; c = zin.read(buffer)) {
fout.write(buffer,0,c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
if(_cancel) {
handler.post(dispatchCancel);
return;
}
} catch(Exception e) {
System.out.println("UNZIP ERROR!");
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
}
And here's how I typically create the zip file.
$>zip -r myzip.zip myzip/
Here are the two error outputs:
java.util.zip.ZipException: CRC mismatch
at java.util.zip.ZipInputStream.readAndVerifyDataDescriptor(ZipInputStream.java:209)
at java.util.zip.ZipInputStream.closeEntry(ZipInputStream.java:173)
at com.XX.XX.XXIssueDownloader$7.run(XXIssueDownloader.java:222)
at java.lang.Thread.run(Thread.java:1020)
java.util.zip.ZipException: data error
at java.util.zip.ZipInputStream.read(ZipInputStream.java:336)
at java.io.FilterInputStream.read(FilterInputStream.java:133)
at com.XX.XX.XXIssueDownloader$7.run(XXIssueDownloader.java:219)
at java.lang.Thread.run(Thread.java:1020)
Anyone have any idea why I might get these errors? I'm not getting anywhere with these.
There are two things very important when loading Zip files.
Make sure you're using a request method that doesn't contain the Accept-Encoding: header. If it's in the request then the response is not a zip file, it's a gzip compressed zip file. So if you're writing that directly to disk while it's downloading then it won't actually be a zip file. You can use something like this to load the zip file:
URL url = new URL(remoteFilePath);
URLConnection connection = url.openConnection();
InputStream in = new BufferedInputStream(connection.getInputStream());
FileOutputStream f = new FileOutputStream(localFile);
//setup buffers and loop through data
byte[] buffer = new byte[1024];
long total = 0;
long fileLength = connection.getContentLength();
int len1 = 0;
while((len1 = in.read(buffer)) != -1) {
if(_cancel) break;
total += len1;
_Progress = (int) (total * 100 / fileLength);
f.write(buffer,0,len1);
handler.post(updateProgress);
}
f.close();
in.close();
When using input and out streams, do NOT use the read(buffer) or write(buffer) method, you need to use read/write(buffer,0,len). Otherwise what you're writing or reading may end up with garbage data in it. The former (read(buffer)) will always read the entire buffer, but there may actually not be a full buffer, for example if the last iteration of the loop only read 512 bytes. So here's how you'd unzip the file:
String _location = model.getLocalPath();
FileInputStream fin = new FileInputStream(localFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while((ze = zin.getNextEntry()) != null) {
if(_cancel) break;
System.out.println("unzipping " + ze.getName());
System.out.println("to: " + _location + ze.getName());
if(ze.isDirectory()) {
File f = new File(_location + ze.getName());
f.mkdirs();
} else {
byte[] buffer2 = new byte[1024];
FileOutputStream fout = new FileOutputStream(_location + ze.getName());
for(int c = zin.read(buffer2); c > 0; c = zin.read(buffer2)) {
fout.write(buffer2,0,c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();

Categories

Resources