Improper Preservation of Permissions
Description
Improper Preservation of Permissions is a vulnerability that occurs when a product does not preserve permissions or incorrectly preserves permissions when copying, restoring, or sharing objects, which can cause them to have less restrictive permissions than intended. Unlike CWE-278 which focuses on inheriting insecure permissions from source objects, this weakness concerns the failure to maintain intended security settings during operations that should preserve them. This can occur during backup restoration, file cloning, data export/import, or sharing operations where the target objects end up with different (typically weaker) permissions than the source.
Risk
Improper permission preservation creates security vulnerabilities by silently degrading access controls during common operations. Backup restorations that don't preserve ACLs may expose previously protected data. File cloning operations that reset permissions to defaults can make sensitive files world-readable. Export operations that strip permission metadata leave exported data unprotected in destination systems. The risk is amplified because administrators and users typically expect these operations to maintain security settings, and may not verify permissions after completion. Symlink attacks during backup restoration can further corrupt permission assignments. Systems with complex ACL requirements are particularly vulnerable as simple permission copying may lose extended attributes.
Solution
Ensure all copy, backup, restore, and sharing operations preserve complete permission information including owner, group, standard permissions, ACLs, and extended attributes. Use backup and restoration tools that properly handle all permission metadata for the target platform. Verify permissions after restoration or copy operations, especially for security-critical files. Implement permission verification as part of backup restoration validation. When exporting data between systems with different permission models, document the expected permissions and provide mechanisms to apply them at the destination. For operations where permissions cannot be preserved, warn users and apply restrictive default permissions rather than permissive ones. Consider using immutable backups that cryptographically verify permission integrity.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality, Integrity | Scope: Confidentiality, Integrity Unauthorized reading of application data occurs when copied or restored files have more permissive read permissions than originals. Unauthorized modification occurs when write permissions are not properly restricted after operations that should preserve them. |
Example Code
Vulnerable Code (Python)
The following examples demonstrate improper permission preservation:
# Vulnerable: Backup restoration doesn't preserve permissions
import os
import shutil
import tarfile
class VulnerableBackupRestore:
def restore_from_tar(self, archive_path, dest_dir):
# Vulnerable: extractall may not preserve all permissions
with tarfile.open(archive_path, 'r:gz') as tar:
tar.extractall(dest_dir)
# Permissions may differ from original backup
# ACLs and extended attributes may be lost
def copy_file_simple(self, source, dest):
# Vulnerable: shutil.copy doesn't preserve permissions
shutil.copy(source, dest)
# Destination file has different permissions than source
def clone_directory(self, source_dir, dest_dir):
# Vulnerable: copytree uses default permissions for new files
shutil.copytree(source_dir, dest_dir)
# If source had restrictive permissions, they may not be preserved
def restore_with_symlink_vulnerability(self, backup_file, dest_path):
# Vulnerable: Symlink can redirect restoration
# Attacker creates symlink before restoration
with open(dest_path, 'wb') as f:
f.write(read_backup(backup_file))
# If dest_path is symlink, writes to wrong location
# Permissions applied to wrong file
// Vulnerable: C file copy without permission preservation
#include <stdio.h>
#include <sys/stat.h>
int vulnerable_copy_file(const char *source, const char *dest) {
FILE *src = fopen(source, "rb");
FILE *dst = fopen(dest, "wb");
if (!src || !dst) return -1;
char buffer[8192];
size_t bytes;
while ((bytes = fread(buffer, 1, sizeof(buffer), src)) > 0) {
fwrite(buffer, 1, bytes, dst);
}
fclose(src);
fclose(dst);
// Vulnerable: Destination file has default permissions (from umask)
// Source file permissions not preserved
return 0;
}
int vulnerable_restore_backup(const char *backup, const char *dest) {
// Vulnerable: Doesn't preserve ACLs
struct stat st;
stat(backup, &st);
// Only copies basic permissions, not ACLs or extended attributes
copy_file_content(backup, dest);
chmod(dest, st.st_mode);
// Extended ACLs and capabilities are lost
return 0;
}
# Vulnerable: Shell operations losing permissions
#!/bin/bash
# Vulnerable: cp without -p loses permissions
cp /secure/sensitive.conf /backup/sensitive.conf
# Backup file has default permissions
# Vulnerable: tar may not preserve extended attributes
tar -czf backup.tar.gz /secure/data/
# Restore won't have original ACLs
# Vulnerable: rsync without proper flags
rsync /source/data/ /dest/data/
# Permissions may not be preserved
Fixed Code (Python)
# Fixed: Proper permission preservation during operations
import os
import shutil
import tarfile
import stat
class SecureBackupRestore:
def restore_from_tar(self, archive_path, dest_dir):
# Verify destination doesn't contain symlinks that could redirect
if os.path.exists(dest_dir):
for root, dirs, files in os.walk(dest_dir):
for name in dirs + files:
path = os.path.join(root, name)
if os.path.islink(path):
raise SecurityError(f"Symlink in restore path: {path}")
with tarfile.open(archive_path, 'r:gz') as tar:
# Check for path traversal and symlink attacks
for member in tar.getmembers():
if member.name.startswith('/') or '..' in member.name:
raise SecurityError(f"Unsafe path: {member.name}")
if member.issym() or member.islnk():
# Handle symlinks carefully
link_target = os.path.normpath(
os.path.join(dest_dir, member.linkname)
)
if not link_target.startswith(dest_dir):
raise SecurityError(f"Symlink escape: {member.name}")
# Extract with permission preservation
tar.extractall(dest_dir, numeric_owner=True)
# Verify permissions were preserved
self.verify_permissions(archive_path, dest_dir)
def copy_file_with_permissions(self, source, dest):
# Use copy2 which preserves permissions and metadata
shutil.copy2(source, dest)
# Verify permissions match
src_stat = os.stat(source)
dst_stat = os.stat(dest)
if src_stat.st_mode != dst_stat.st_mode:
# Force permissions if copy2 failed
os.chmod(dest, src_stat.st_mode)
# Copy ACLs if available (Linux)
try:
import posix1e
acl = posix1e.ACL(file=source)
acl.applyto(dest)
except (ImportError, OSError):
pass # ACL support not available
def clone_directory_securely(self, source_dir, dest_dir):
# Create destination with same permissions as source
src_stat = os.stat(source_dir)
def copy_function(src, dst):
shutil.copy2(src, dst)
# Also copy extended attributes
try:
import xattr
for attr in xattr.listxattr(src):
xattr.setxattr(dst, attr, xattr.getxattr(src, attr))
except (ImportError, OSError):
pass
shutil.copytree(
source_dir, dest_dir,
copy_function=copy_function,
dirs_exist_ok=False
)
# Verify directory permissions
os.chmod(dest_dir, src_stat.st_mode)
def verify_permissions(self, archive_path, dest_dir):
"""Verify restored files have correct permissions"""
with tarfile.open(archive_path, 'r:gz') as tar:
for member in tar.getmembers():
dest_path = os.path.join(dest_dir, member.name)
if os.path.exists(dest_path) and not os.path.islink(dest_path):
actual_mode = os.stat(dest_path).st_mode & 0o7777
expected_mode = member.mode & 0o7777
if actual_mode != expected_mode:
raise PermissionVerificationError(
f"Permission mismatch for {member.name}: "
f"expected {oct(expected_mode)}, got {oct(actual_mode)}"
)
// Fixed: C file copy with permission preservation
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/xattr.h>
#include <sys/acl.h>
int secure_copy_file(const char *source, const char *dest) {
struct stat st;
if (stat(source, &st) != 0) {
return -1;
}
// Open source for reading
int src_fd = open(source, O_RDONLY);
if (src_fd < 0) return -1;
// Create destination with source permissions
int dst_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
if (dst_fd < 0) {
close(src_fd);
return -1;
}
// Copy content
char buffer[8192];
ssize_t bytes;
while ((bytes = read(src_fd, buffer, sizeof(buffer))) > 0) {
if (write(dst_fd, buffer, bytes) != bytes) {
close(src_fd);
close(dst_fd);
return -1;
}
}
close(src_fd);
close(dst_fd);
// Explicitly set permissions (in case umask affected creation)
chmod(dest, st.st_mode);
// Preserve ownership (requires root)
chown(dest, st.st_uid, st.st_gid);
// Copy ACLs
acl_t acl = acl_get_file(source, ACL_TYPE_ACCESS);
if (acl != NULL) {
acl_set_file(dest, ACL_TYPE_ACCESS, acl);
acl_free(acl);
}
// Copy extended attributes
char xattr_list[1024];
ssize_t list_len = listxattr(source, xattr_list, sizeof(xattr_list));
if (list_len > 0) {
char *name = xattr_list;
while (name < xattr_list + list_len) {
char value[1024];
ssize_t value_len = getxattr(source, name, value, sizeof(value));
if (value_len > 0) {
setxattr(dest, name, value, value_len, 0);
}
name += strlen(name) + 1;
}
}
return 0;
}
# Fixed: Shell operations preserving permissions
#!/bin/bash
# Copy with permission preservation
cp -p /secure/sensitive.conf /backup/sensitive.conf
# Or use rsync with full preservation
rsync -aAX /source/data/ /dest/data/
# -a: archive mode (preserves permissions, ownership, timestamps)
# -A: preserve ACLs
# -X: preserve extended attributes
# Tar with extended attributes and ACLs
tar --xattrs --acls -czf backup.tar.gz /secure/data/
# Restore with permissions
tar --xattrs --acls -xzf backup.tar.gz -C /restore/
# Verify permissions after restore
find /restore/ -exec stat -c '%a %n' {} \; > restored_perms.txt
The fix ensures all permission metadata (basic permissions, ACLs, extended attributes) is preserved during copy and restore operations.
Exploited in the Wild
Backup Restoration ACL Loss (Enterprise Environments, Ongoing)
Backup restorations using tools that don't properly preserve ACLs have exposed previously protected data. CVE-2002-2323 documented backup restoration using symbolic links resulting in incorrect ACLs.
File Cloning Permission Degradation (Unix Systems, Historical)
CVE-2001-1515 documented file system inheritance causing unintended permission changes during copy operations, weakening security on cloned files.
Backup File Default Permissions (Various Applications, Ongoing)
CVE-2005-1920 documented backup files created with default permissions less restrictive than originals, exposing sensitive data from backup operations.
Tools to Test/Exploit
-
getfacl/setfacl — Tools for viewing and manipulating ACLs to verify preservation.
-
rsync — File synchronization tool with options for ACL and extended attribute preservation.
-
tar with ACL support — Archive tool with options for preserving extended attributes.
CVE Examples
-
CVE-2002-2323 — Backup restoration using symbolic links resulted in incorrect ACLs.
-
CVE-2001-1515 — File system inheritance causing unintended permission changes.
-
CVE-2005-1920 — Backup files created with default permissions less restrictive than originals.
-
CVE-2001-0195 — Files became world-readable during cloning operations.
References
-
MITRE Corporation. "CWE-281: Improper Preservation of Permissions." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/281.html
-
Linux man pages. "acl(5) - Access Control Lists." https://man7.org/linux/man-pages/man5/acl.5.html
-
OWASP Foundation. "Broken Access Control." OWASP Top 10. https://owasp.org/Top10/A01_2021-Broken_Access_Control/