Insecure Preserved Inherited Permissions
Description
Insecure Preserved Inherited Permissions is a vulnerability that occurs when a product inherits a set of insecure permissions for an object, for example when copying from an archive file, without user awareness or involvement. Unlike CWE-277 which deals with permissions set by the creating process, this weakness concerns permissions that are embedded in source objects (such as files within archives) and are preserved during extraction or copying operations. When archives, backups, or copied files contain objects with overly permissive settings, these dangerous permissions persist into the target environment, potentially creating security vulnerabilities.
Risk
Preserved insecure permissions create security risks when files are transferred between systems or restored from backups. Archives created on one system with specific permission requirements may be extracted on another system where those permissions are inappropriate. Backup restorations may reintroduce permission vulnerabilities that were previously corrected. Software distribution through archives may install files with the developer's local permissions rather than production-appropriate settings. The risk is amplified because users often assume extracted files will have safe default permissions, unaware that permissions are being preserved from the source. Attackers who can influence archive contents can embed files with dangerous permissions that will persist when victims extract the archive.
Solution
Configure extraction and copying tools to apply appropriate permissions rather than blindly preserving source permissions. Use archive formats and extraction options that allow specification of target permissions. Implement post-extraction verification to check and correct file permissions. When creating archives for distribution, explicitly set appropriate permissions within the archive rather than relying on source file permissions. Document permission requirements for distributed software and provide installation scripts that set correct permissions. Backup and restore procedures should include permission validation as part of the restoration process. Apply the principle of least privilege when determining what permissions should be preserved versus reset during file operations.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality, Integrity | Scope: Confidentiality, Integrity Files extracted with preserved insecure permissions may allow unauthorized reading of sensitive application data or unauthorized modification of critical files. World-readable configuration files may expose credentials, while world-writable executables may allow code injection. |
Example Code
Vulnerable Code (Python)
The following examples demonstrate insecure preserved permission vulnerabilities:
# Vulnerable: Preserves permissions from archive
import tarfile
import zipfile
import os
class VulnerableExtractor:
def extract_tarball(self, archive_path, dest_dir):
# Vulnerable: extractall preserves permissions from archive
with tarfile.open(archive_path, 'r:gz') as tar:
tar.extractall(dest_dir)
# If archive contains world-writable files, they remain world-writable
# If archive contains setuid binaries, they remain setuid!
def extract_zip(self, archive_path, dest_dir):
# Vulnerable: extractall preserves Unix permissions from zip
with zipfile.ZipFile(archive_path, 'r') as zf:
zf.extractall(dest_dir)
# External attributes containing permissions are preserved
def copy_from_backup(self, backup_path, restore_path):
# Vulnerable: shutil.copy2 preserves permissions
import shutil
shutil.copy2(backup_path, restore_path)
# Backup may have had insecure permissions
# Vulnerable: Shell script that preserves archive permissions
#!/bin/bash
# Vulnerable: tar preserves permissions by default
tar -xzf software_package.tar.gz -C /opt/
# Vulnerable: cp -p preserves permissions
cp -p /backup/config.conf /etc/myapp/config.conf
# Vulnerable: rsync -p preserves permissions
rsync -ap /backup/data/ /var/lib/myapp/data/
// Vulnerable: Java extraction preserving permissions
import java.io.*;
import java.util.zip.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
public class VulnerableArchiveHandler {
public void extractZip(String archivePath, String destDir) throws IOException {
try (ZipInputStream zis = new ZipInputStream(
new FileInputStream(archivePath))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
Path destPath = Paths.get(destDir, entry.getName());
if (entry.isDirectory()) {
Files.createDirectories(destPath);
} else {
Files.copy(zis, destPath, StandardCopyOption.REPLACE_EXISTING);
// Vulnerable: Preserves external attributes as permissions
int unixMode = (int) ((entry.getExtra() != null)
? extractUnixMode(entry) : 0644);
// Applies whatever permissions were in the archive
setUnixPermissions(destPath, unixMode);
}
}
}
}
}
Fixed Code (Python)
# Fixed: Applies appropriate permissions during extraction
import tarfile
import zipfile
import os
import stat
class SecureExtractor:
# Define safe permission limits
MAX_FILE_PERMS = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH # 0644
MAX_DIR_PERMS = stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH # 0755
MAX_EXEC_PERMS = stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH # 0755
# Permissions to explicitly deny
DENIED_PERMS = stat.S_ISUID | stat.S_ISGID # No setuid/setgid
def extract_tarball_safely(self, archive_path, dest_dir):
with tarfile.open(archive_path, 'r:gz') as tar:
for member in tar.getmembers():
# Sanitize permissions
self._sanitize_tar_member(member)
# Also prevent path traversal
if member.name.startswith('/') or '..' in member.name:
raise SecurityError(f"Unsafe path in archive: {member.name}")
tar.extract(member, dest_dir)
def _sanitize_tar_member(self, member):
# Remove setuid/setgid bits
member.mode &= ~self.DENIED_PERMS
if member.isdir():
# Limit directory permissions
member.mode = member.mode & self.MAX_DIR_PERMS
elif member.mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH):
# Executable file - limit to max executable permissions
member.mode = member.mode & self.MAX_EXEC_PERMS
else:
# Regular file - limit to max file permissions
member.mode = member.mode & self.MAX_FILE_PERMS
def extract_zip_safely(self, archive_path, dest_dir, default_mode=0o644):
with zipfile.ZipFile(archive_path, 'r') as zf:
for info in zf.infolist():
# Prevent path traversal
if info.filename.startswith('/') or '..' in info.filename:
raise SecurityError(f"Unsafe path: {info.filename}")
# Extract file
extracted = zf.extract(info, dest_dir)
# Apply safe permissions regardless of archive contents
if info.is_dir():
os.chmod(extracted, 0o755)
else:
os.chmod(extracted, default_mode)
def copy_from_backup_safely(self, backup_path, restore_path, mode=0o640):
"""Copy file content without preserving source permissions"""
import shutil
# Copy content only, not permissions
shutil.copy(backup_path, restore_path) # copy, not copy2
# Apply explicit safe permissions
os.chmod(restore_path, mode)
# Set appropriate ownership
import pwd
app_user = pwd.getpwnam('myapp')
os.chown(restore_path, app_user.pw_uid, app_user.pw_gid)
# Fixed: Shell script with permission sanitization
#!/bin/bash
# Extract without preserving permissions
tar --no-same-permissions --no-same-owner -xzf software_package.tar.gz -C /opt/
# Set appropriate permissions after extraction
find /opt/extracted -type f -exec chmod 644 {} \;
find /opt/extracted -type d -exec chmod 755 {} \;
find /opt/extracted -name "*.sh" -exec chmod 755 {} \;
# Copy content without preserving permissions
cp /backup/config.conf /etc/myapp/config.conf
chmod 640 /etc/myapp/config.conf
chown root:myapp /etc/myapp/config.conf
# rsync without permission preservation
rsync -a --no-perms /backup/data/ /var/lib/myapp/data/
# Or explicitly set permissions
rsync -a --chmod=D755,F644 /backup/data/ /var/lib/myapp/data/
// Fixed: Java extraction with permission enforcement
import java.io.*;
import java.util.zip.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.*;
public class SecureArchiveHandler {
private static final Set<PosixFilePermission> SAFE_FILE_PERMS =
PosixFilePermissions.fromString("rw-r--r--");
private static final Set<PosixFilePermission> SAFE_DIR_PERMS =
PosixFilePermissions.fromString("rwxr-xr-x");
private static final Set<PosixFilePermission> SAFE_EXEC_PERMS =
PosixFilePermissions.fromString("rwxr-xr-x");
public void extractZipSecurely(String archivePath, String destDir) throws IOException {
Path destPath = Paths.get(destDir).toRealPath();
try (ZipInputStream zis = new ZipInputStream(
new FileInputStream(archivePath))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
// Prevent path traversal
Path entryPath = destPath.resolve(entry.getName()).normalize();
if (!entryPath.startsWith(destPath)) {
throw new SecurityException("Path traversal detected: " + entry.getName());
}
if (entry.isDirectory()) {
Files.createDirectories(entryPath,
PosixFilePermissions.asFileAttribute(SAFE_DIR_PERMS));
} else {
// Ensure parent exists
Files.createDirectories(entryPath.getParent());
// Extract content
Files.copy(zis, entryPath, StandardCopyOption.REPLACE_EXISTING);
// Apply safe permissions - ignore archive permissions
Set<PosixFilePermission> perms = isExecutable(entry.getName())
? SAFE_EXEC_PERMS : SAFE_FILE_PERMS;
Files.setPosixFilePermissions(entryPath, perms);
}
}
}
}
private boolean isExecutable(String filename) {
return filename.endsWith(".sh") || filename.endsWith(".py")
|| filename.equals("bin") || filename.contains("/bin/");
}
}
The fix applies safe permissions during extraction rather than preserving potentially dangerous permissions from the archive source.
Exploited in the Wild
Archive-Based Permission Attacks (Software Distribution, Ongoing)
Malicious archives distributed through compromised repositories or MITM attacks have included files with setuid bits or world-writable permissions. When victims extracted these archives, the dangerous permissions persisted, enabling privilege escalation or backdoor installation.
Backup Restoration Vulnerabilities (Enterprise Environments, Ongoing)
Backup restorations that preserved permissions from older backups have reintroduced permission vulnerabilities that had been previously remediated. Organizations restored from backups created before security hardening, returning systems to vulnerable states.
Export/Import Permission Leakage (Application Migration, Historical)
CVE-2005-1724 documented software that failed to obey specified permissions during export operations, causing insecure permissions to be preserved when data was imported to new systems.
Tools to Test/Exploit
-
tar inspection tools — Commands like
tar -tvfto inspect permissions within archives before extraction. -
bsdtar — Archive tool with options to control permission preservation.
-
Archive permission analyzers — Tools for analyzing and modifying archive contents.
CVE Examples
- CVE-2005-1724 — Software failed to obey specified permissions during export operations, preserving insecure permissions.
References
-
MITRE Corporation. "CWE-278: Insecure Preserved Inherited Permissions." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/278.html
-
GNU tar manual. "Handling File Permissions." https://www.gnu.org/software/tar/manual/
-
OWASP. "Zip Slip Vulnerability." https://snyk.io/research/zip-slip-vulnerability