skip to content

A developer fixes a permissions problem on a Linux server by running `chmod -R 777` over an application's directory tree. What is wrong with that, and how would you set permissions correctly on a tree that mixes directories and data files?

level: seniorimportance: should knowfreq 45%

answer

  1. world-writable code is attacker-writable code
  2. files are not directories
  3. the old modes are gone forever
  4. capital X exists for exactly this
  5. split by type, then fix ownership

basics

~20 s

It makes every file world-writable, so any local process can rewrite the application's code and configuration, and it marks data files executable. It is also destructive: the original per-file modes are gone. Set directories and files separately instead.

solid answer

~60 s

Three problems, in order of seriousness. First, world-writable code and config means **any** local account or compromised process on the host can modify what the application runs — that is a straight path from a low-privilege foothold to running your code. Second, it marks data files executable, which is meaningless for a CSV and noisy for anything auditing the tree. Third, it is irreversible: `chmod -R` overwrites every mode in place and nothing recorded what they were, so "undo it" is not an option — you are reconstructing from a package manifest or a backup. The correct approach separates the two object types, because directories need the execute bit and plain files usually must not have it: either walk the tree by type and apply distinct modes, or use symbolic mode with a capital `X`, as in `chmod -R u=rwX,g=rX,o= dir`, which sets execute only on directories and on files that already had an execute bit. Then fix ownership with `chown -R` so the application account owns what it must write, and grant write only where it genuinely writes.

code

bash · 11 lines
bash
# Record the current state first so the change is reversible
find /srv/app -printf '%m %u %g %p\n' > /root/app-modes.txt

# Option A: split by object type
find /srv/app -type d -exec chmod 750 {} +
find /srv/app -type f -exec chmod 640 {} +

# Option B: one pass, capital X sets execute only where it belongs
chmod -R u=rwX,g=rX,o= /srv/app

chown -R app:app /srv/app

go deeper

for a junior

Never reach for 777. Know that directories and files need different modes, and that a permission error is usually about ownership or a parent directory rather than about opening everything up.

for a middle

Explain why one numeric mode cannot be right for a mixed tree, and demonstrate the two correct techniques: splitting by object type, or a symbolic mode using capital X in a single recursive pass.

for a senior

Lead with the security mechanism — world-writable code and config means any local process can change what your service executes — and treat an already-run 777 as an incident requiring verification against a known-good source, not just a mode reset.

for a principal

Remove the failure mode from the environment: have permissions and ownership declared in deployment tooling so they are asserted rather than hand-applied, and make hand-run recursive chmods on production trees something the platform makes unnecessary.

## Why 777 is the wrong answer to any question Mode `777` grants read, write and execute to every account on the machine. For an application tree that means: - **Any local process can rewrite the code.** A compromised unrelated service, a shared CI account, a second tenant on the box — anything running as any UID can edit a script or a configuration file that your application later loads and executes. This converts "attacker has some local access" into "attacker runs code as your service account", which is the escalation step you were supposed to prevent. - **Any local process can rewrite the data**, silently, with no audit trail beyond filesystem timestamps. - **Nothing is protected from accident either.** World-writable is also everyone-can-truncate. And it very often does not even fix the original problem, because the original problem is frequently ownership or a missing search bit on a parent directory rather than the modes on the files themselves. `777` is applied because it is the biggest hammer available, not because it addresses the diagnosis. ## Why recursion makes it worse `chmod -R` applies one mode to two fundamentally different kinds of object. Directories need `x` to be traversable; plain data files should almost never have `x`. So *every* recursive numeric chmod is wrong in one direction or the other: - `chmod -R 755` — directories are correct, and now every data file is executable. - `chmod -R 644` — files are correct, and every directory has lost its search bit, so the tree becomes unreachable and even `ls -l` returns question marks. - `chmod -R 777` — both are "working" and everything is world-writable. ## The destructiveness is the part people miss There is no journal of previous modes. Before the recursive chmod the tree may have held a private key at `600`, a socket directory at `750`, and a handful of helper scripts at `755`. Afterwards that structure is simply gone, and the security-relevant ones are the ones you most needed. Recovery means reconstructing intent: - For files owned by a distribution package, the package manager can restore the modes it recorded — RPM-based systems expose this directly (`rpm --setperms <package>`), and on Debian-based systems the package's recorded metadata plus reinstallation serves the same purpose. - For application files, from your deployment tooling or configuration management, which is the real argument for having permissions declared there rather than applied by hand. - For anything else, from a backup — or from judgement. Before any wide chmod, capture the current state so the change is reversible: a listing of every path with its octal mode and owner is a few seconds of work and turns an irreversible operation into a reversible one. ## Doing it correctly **Split by object type.** Directories and files get different modes: ``` find /srv/app -type d -exec chmod 750 {} + find /srv/app -type f -exec chmod 640 {} + ``` **Or use symbolic mode with capital X.** In `chmod`, `X` means "execute, but only for directories, or for files that already have at least one execute bit set". That makes a single recursive pass safe: ``` chmod -R u=rwX,g=rX,o= /srv/app ``` Directories become `750`, ordinary data files become `640`, and existing executables keep their execute bit. Lowercase `x` in the same command would have marked everything executable — the case of the letter is the whole difference. **Fix ownership as well as mode.** Most "permission denied" reports are really ownership problems: `chown -R app:app /srv/app` and then grant write only in the specific subdirectories the service writes to. A tree where code is owned by a deploy account and readable-but-not-writable by the runtime account is meaningfully harder to attack than one where the service can rewrite its own code. **Grant the narrowest thing that works.** If a group needs access, add the group and use `640`/`750` rather than opening to others. If exactly one extra account needs one file, that is what per-file access-control entries exist for. ## A caution about recursion itself `chmod -R` operates on what it walks. It does not change the permissions of symbolic links themselves, and it will not descend into a symlinked directory by default — but a tree containing bind mounts or a symlink you did not expect can still put you somewhere you did not intend. Know what is under the path before you recurse over it, and never recurse from `/` or a home directory root. ## What a good answer sounds like Name the security consequence first (world-writable code is remote-ish code execution for any local foothold), then the correctness consequence (files should not be executable, directories must be), then the operational one (the previous modes are unrecoverable), and finish with the type-split or capital-`X` technique plus fixing ownership. Saying "it works but it's bad practice" is not enough — the interviewer wants the specific mechanism of harm.

  • What exactly does capital X mean in a chmod symbolic mode?
    It grants execute conditionally: for directories always, and for other files only if at least one execute bit is already set. That is what makes it safe in a recursive pass — directories stay traversable, existing executables keep working, and plain data files are left without the execute bit. Lowercase `x` grants it unconditionally to everything it touches.
  • Someone has already run chmod -R 777 on a production tree. What is your recovery plan?
    Treat it as an incident, not a typo. Assume anything world-writable may have been modified while it was open, so verify the code against a known-good source rather than only fixing modes. Restore the intended permissions from the package manager's recorded metadata, your configuration management, or a backup, then redeploy from source of truth and check for unexpected changes in the window.
  • When is a world-writable directory actually legitimate?
    Shared drop directories such as the system temporary directory, where unrelated accounts must all create files. Those are made safe by an additional bit that stops one user deleting another's files, and by keeping the directory itself free of anything anyone executes. That is the narrow legitimate case — it is never the right shape for an application's code or configuration tree.

saying these in an interview costs you the question

  • Treating 777 as a normal fix that is merely untidy
  • Not recognising that world-writable code allows local privilege escalation
  • Applying one numeric mode recursively to both files and directories
  • Assuming a recursive chmod can be undone
  • Confusing lowercase x with capital X in symbolic mode

context