Safeguard
Application Security•

CWE-732 Incorrect Permission Assignment: How to Find and Fix It

World-writable files, open sudoers entries and public storage buckets all come from the same mistake. Learn how permissions go wrong and how to lock them down.

Safeguard Security Team
6 min read

CWE-732 describes a product that sets permissions on a security-critical resource in a way that lets unintended actors read or modify it. The resource might be a configuration file, an executable, a directory, a socket, a service, or a cloud storage account.

It hits installers, desktop and server software, and cloud deployments alike. If your software creates anything that other users or processes can touch, it applies to you.

How it happens

MITRE puts the root cause plainly: the resource is given a permission setting that grants access to a wider range of actors than required. This is especially dangerous when the resource relates to program configuration, execution or sensitive user data. MITRE's own example is a misconfigured cloud storage account that a public or anonymous user can read or write.

The usual routes are small and boring. A developer runs chmod 777 to get past a permission error and never goes back. An installer creates a directory with default permissions inherited from whoever ran it. A service is registered with a security descriptor that lets every user start and stop it.

The record's observed examples show how real these are:

  • CVE-2022-29527: a Go application for cloud management created a world-writable sudoers file, allowing local attackers to inject sudo rules and escalate to root by winning a race condition.
  • CVE-2009-3482: an anti-virus product set "Everyone: Full Control" on files under the Program Files folder, so attackers could replace executables with Trojan horses.
  • CVE-2009-3897: a product created directories with 0777 permissions at installation, letting users gain privileges and reach a socket used for authentication.
  • CVE-2009-3489: a photo editor installed a service with an insecure security descriptor, so users could stop or start it or run commands as SYSTEM.

Here is the pattern in code.

# Vulnerable: anyone on the machine can read or edit the config
import os
os.makedirs("/opt/app/conf", mode=0o777, exist_ok=True)
with open("/opt/app/conf/app.env", "w") as f:
    f.write(secret_settings)

What an attacker gains

MITRE lists three outcomes.

  1. Read access. An attacker can read sensitive information from the resource, such as credentials or configuration data stored in a file.
  2. Privilege gain. An attacker can modify critical properties of the resource to gain privileges, for example replacing a world-writable executable with a Trojan horse.
  3. Integrity damage. An attacker can destroy or corrupt critical data, such as deleting records from a database.

The anti-virus and sudoers cases above are the second outcome. A file that a privileged process trusts is writable by someone who is not privileged. Whoever writes it controls what runs.

How to find it

MITRE lists several methods, and they complement each other.

  • Automated static analysis can find calls that change permissions, then analyze what values they set. Look for modes like 0777 and 0666, and for APIs that grant broad access.
  • Automated dynamic analysis checks permissions on resources while the program runs. MITRE cautions that the intended security policy might allow loose permissions for certain operations, so findings need a human decision.
  • Manual analysis such as penetration testing and threat modeling can be more effective than automation where custom permission models are involved.
  • Manual static analysis can spot custom permission functions, after which an analyst checks each use against the intended policy.

A fast practical check is to install your software on a clean machine and list everything it created with owner, group and mode. Anything writable by group or world needs a reason. In the cloud, review storage buckets and containers for public access settings.

Click through it at your own pace, or open it full screen.

How to fix it

MITRE's mitigations, in plain steps:

  1. Check before you trust. When the program uses a critical resource such as a configuration file, test whether its permissions are insecure, for example modifiable by any regular user. Raise an error, or even exit, if an unauthorized party could have changed it.
  2. Divide the software into areas. Define anonymous, normal, privileged and administrative areas, map them to data, functions and resources, then set permissions to match. MITRE rates this as moderately effective and notes that it can be time consuming when resources or user types are many, or features change quickly.
  3. Sandbox where you can. Jails and confinement such as chroot, AppArmor and SELinux can restrict which files a process reaches. MITRE rates this as limited, because it reduces the scope of an attack rather than removing the flaw.
  4. Set a restrictive umask at startup. Explicitly set default permissions or the umask to the most restrictive setting possible, and set the right permissions at installation. This stops you inheriting loose permissions from whoever installs or runs the program. MITRE rates this as high effectiveness.
  5. Lock configuration, executables and libraries so only the software's administrator can read and write them. Also rated high.
  6. Write safe documentation. Do not recommend insecure configuration changes, especially ones that reach resources outside your application, and do not assume an administrator will change settings by hand.
  7. Harden the environment. Make sure the software runs properly under a recognized hardening baseline such as the United States Government Configuration Baseline or an equivalent.
  8. Close public cloud storage. For S3 buckets, Azure blobs and Google Cloud Storage, use the provider's controls to disable public access.

The corrected version of the earlier snippet:

# Fix: owner-only directory and file
import os
os.makedirs("/opt/app/conf", mode=0o700, exist_ok=True)
fd = os.open("/opt/app/conf/app.env", os.O_WRONLY | os.O_CREAT, 0o600)
with os.fdopen(fd, "w") as f:
    f.write(secret_settings)

Note that the mode is passed at creation. Creating a file first and tightening it afterwards leaves a window in which it is exposed, and a race condition was part of the first example above.

Quick check

Why is a world-writable executable more serious than a world-readable one?

Write access lets an attacker replace the program. If a privileged user or service runs it later, the attacker's code runs with that privilege.

Why set the mode at creation instead of calling chmod afterwards?

Between creation and chmod the file has the looser default permissions. Setting the mode when creating it removes that window.

Does a sandbox make loose permissions safe?

No. MITRE rates sandboxing as limited. It reduces the scope of an attack but does not fix the permission itself.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.