MuntashirAkon/AppManager · critical · IOException

Zip slip vulnerability detected!\nExpected dest: " + new Fil

Error message

Zip slip vulnerability detected!\nExpected dest: " + new File(realDestPath, entry.getName()) + "\nActual path: " + realFilePath

What it means

A second, post-resolution zip-slip check in AndroidBackupExtractor: even when the entry name looked safe, the real filesystem path of the target (following symlinks) is compared against realDestPath. If the resolved path escapes the destination, extraction is aborted with an IOException.

Source

Thrown at app/src/main/java/io/github/muntashirakon/AppManager/backup/adb/AndroidBackupExtractor.java:98

                if (!filename.startsWith(relativeDirInAb)) {
                    throw new IOException("Unsupported file in AB: " + filename);
                }
                // Remove apps/{packageName}/ part
                filename = filename.substring(relDirSize);
                Path file;
                if (entry.isDirectory()) {
                    file = dest.createDirectoriesIfRequired(filename);
                } else file = dest.createNewArbitraryFile(filename, null);
                // Check if the given entry is a link.
                if (entry.isSymbolicLink() && file.getFilePath() != null) {
                    String linkName = entry.getLinkName();
                    file.delete();
                    file.createNewSymbolicLink(linkName);
                } else {
                    // Zip slip vulnerability might still be present
                    String realFilePath = file.getRealFilePath();
                    if (realDestPath != null && realFilePath != null && !realFilePath.startsWith(realDestPath)) {
                        throw new IOException("Zip slip vulnerability detected!" +
                                "\nExpected dest: " + new File(realDestPath, entry.getName()) +
                                "\nActual path: " + realFilePath);
                    }
                    if (!entry.isDirectory()) {
                        try (OutputStream os = file.openOutputStream()) {
                            IoUtils.copy(tis, os);
                        }
                    }
                }

                // Categorize and build TarArchiveEntry
                int category = getCategory(filename);
                if (category == CAT_UNK && filename.equals(Constants.BACKUP_MANIFEST_FILENAME)) {
                    // Ignore manifest file
                    continue;
                }
                TarArchiveEntry targetEntry = getTargetArchiveEntry(entry, filename);
                List<TargetTarEntry> targetTarEntries = mCategoryTargetEntriesMap.get(category);

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Refuse the archive — it likely contains a malicious symlink chain
  2. Extract into a fresh, empty destination directory with no pre-existing symlinks
  3. Remove suspicious symlinks from the destination before re-running the restore
  4. Regenerate the backup from a trusted source

Example fix

// before (destination reused across restores)
Path dest = new Path("/data/local/tmp/restore");
extractor.extract();
// after (clean destination each run)
Path dest = new Path("/data/local/tmp/restore-" + System.nanoTime());
dest.mkdirsIfNotExists();
extractor.extract();
Defensive patterns

Strategy: validation

Validate before calling

// Ensure destination contains no symlinks before extraction
Files.walk(destDir)
     .filter(p -> Files.isSymbolicLink(p))
     .forEach(p -> { throw new RuntimeException("Symlink in dest: " + p); });

Try / catch

try {
    extractor.extract();
} catch (IOException e) {
    if (e.getMessage().contains("Zip slip")) {
        Log.e(TAG, "Symlink-based traversal attempt blocked", e);
        wipeAndRecreate(destDir);
    } else throw e;
}

Prevention

When it happens

Trigger: A TAR entry containing a symbolic link (or hard-to-spot path) whose resolved target lies outside the extraction directory — e.g. a symlink previously created pointing to /data, followed by an entry writing through it.

Common situations: Maliciously crafted backups using symlink chains to escape the destination; archives restored into directories that already contain attacker-influenced symlinks.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/9e0ac7411e2c3d01. Report an issue: GitHub.