Linux and developer tools
Bash Basics for Security Work
Learn pipes, redirection, variables, loops, exit codes and set -euo pipefail, then use them to count failed logins and hash a folder.
Linux owners, groups, rwx bits, chmod, umask and setuid, with a lab that tests access as a second user and finds risky file modes.
Linux permissions decide who can read, change and run each file on the system. Most problems you meet in a security review are not exotic exploits. They are a config file that any local account can write, a script that a root service runs but a normal user can edit, or a binary with the setuid bit set that nobody remembers adding. This article builds the mechanism from the bottom up: owners, groups, the rwx bits, chmod, umask, and the special bits that matter most in a review. You will create files, change their modes, test access as a second local user in a lab VM, and search for risky permissions.
The core idea is least privilege. A process should have only the access it needs, and a file should be writable only by the people and programs that are supposed to change it. File modes are the first place Linux enforces that rule, so they are the first thing to check.
Every file and directory carries three things that decide access:
Run this on any file in your home directory:
ls -l notes.txtThe first character is the file type (- is a regular file, d is a directory, l is a symbolic link). The next nine characters are the mode. The owner and group names come after the link count.
The kernel checks permissions every time a process opens a file. A shell, a web server and a cron job all go through the same check. If the check fails, you get Permission denied. The file system does not care whether the process is trusted.
The mode has three triplets, one for each class of user:
Linux checks the classes in order. If your account is the owner, the owner bits apply and the group and other bits are ignored, even when those would grant more. This surprises people. A file whose owner bits are --- will not open for its owner, even if the group bits are rwx and the owner belongs to that group.
Group membership is a separate list. Check yours with:
id
groupsA change to group membership usually takes effect at the next login, not in the shell where you ran the command.
Each triplet holds three bits:
| Bit | On a regular file | On a directory |
|---|---|---|
r (read, value 4) |
Read the contents | List the names inside |
w (write, value 2) |
Change the contents | Create, rename or delete entries |
x (execute, value 1) |
Run it as a program | Enter the directory and reach its entries |
Two points trip up beginners.
First, a directory's x bit is what lets you pass through it. A directory with r but no x lets you see names but not open them. A directory with x but no r lets you open a file whose exact name you already know, but not list the directory.
Second, write permission on a file does not let you delete it. Deleting depends on write and execute permission on the directory that holds the entry. A file you own inside a directory you cannot write to still cannot be removed by you.
chmod accepts two notations for the same thing.
Octal. Each triplet becomes one digit, the sum of its bits: r is 4, w is 2, x is 1. So rw- is 6, r-- is 4, r-x is 5 and rwx is 7. The mode 644 means owner read and write (6), group read (4), other read (4). The mode 600 means only the owner can read or write.
Symbolic. You name the class, the operator and the bits:
u (owner), g (group), o (other), a (all). With no class given, a is assumed, except that umask bits are left alone.+ adds bits, - removes them, = sets exactly those bits in the class and clears the rest.So chmod u=rw,go=r notes.txt gives the same result as chmod 644 notes.txt. chmod g+w notes.txt adds group write and leaves the other bits as they are.
Octal is compact and easy to paste into scripts. Symbolic is safer for a single change, because it does not wipe bits you did not think about. Expect both in a review, in config management files, Dockerfiles and install scripts.
chmod changes the mode. On a file, only its owner or root can run it successfully. Anyone else gets Operation not permitted.
A safe habit is to read the mode, change it, and read it again:
stat -c '%a %A %U:%G %n' notes.txt
chmod 600 notes.txt
stat -c '%a %A %U:%G %n' notes.txtstat -c prints the octal mode (%a), the symbolic mode (%A), the owner and group names (%U, %G) and the file name (%n). You will use this format throughout the lab.
chown changes the owner and chgrp changes the group. Changing ownership to another user normally needs root. Do not run chown -R on a tree until you have listed what it will touch.
When a program creates a file, it asks for a starting mode. For a regular file the starting value is 666. For a directory it is 777. The umask removes bits from that starting value. The shell sets it, and every file and directory the shell and its children create goes through it.
Most Linux distributions default to 022. For a file, 666 minus 022 is 644, so new files are rw-r--r--. For a directory, 777 minus 022 is 755, so new directories are rwxr-xr-x. The umask only removes permissions. A mode can never end up more permissive than the starting value.
Check your value:
umaskA stricter umask such as 077 gives 600 files and 700 directories, which only the owner can access. A looser one such as 002 gives 664 files and 775 directories, which the group can write. A umask set in a shell profile affects that shell and its children. It does not change files that already exist.
Four-digit octal modes add a leading digit for three special bits:
/tmp is the classic example.ls -l shows setuid as s in the owner's execute position, and sticky as t in the other execute position. On Ubuntu 24.04 the line for passwd looks like this:
-rwsr-xr-x 1 root root 64152 May 30 2024 /usr/bin/passwdPrograms like passwd need setuid because they must write protected account files on behalf of a normal user. The risk is that every setuid root binary lets a normal user run code with root rights. A bug in one, such as a memory corruption or an unsafe call to a helper program, can become a local privilege escalation. Reviewers keep a short list of setuid binaries and compare it with a known baseline. A new setuid file that nobody installed on purpose is a finding that needs an answer.
World-writable files are the other common problem. A file with o+w lets any local account change its contents. If that file is a script, a config read by a root service, or a binary in a directory on the PATH, the attacker does not need an exploit. They edit the file. Check directories too. A writable directory on a root-run path can let an attacker replace a file even when the file itself is locked.
Run this in a Linux environment you control: a WSL distribution or a VM created for this course. Do not run it on a production server or a shared machine. The second-user section creates an account, so do that only in your own lab VM.
mkdir ~/permlab && cd ~/permlab
echo 'lab secret' > notes.txt && ls -l notes.txtOn Ubuntu 24.04 under WSL, the new file showed -rw-r--r-- with the owner and group named after the account. Your user name, date and group will differ. Compare the mode.
chmod 600 notes.txt && stat -c '%a %U:%G %n' notes.txt
umaskObserved output:
600 usurio:usurio notes.txt
0022Your account name will differ. 600 means only the owner can read and write. 0022 is the umask in the current shell, and the leading zero is part of the octal form.
Now a directory:
mkdir sub && chmod 750 sub && stat -c '%a %n' subObserved: 750 sub. The owner has full access, the group can read and enter, and other users get nothing.
Symbolic changes, one at a time, with a read after each:
chmod u=rw,go=r notes.txt && stat -c '%a %A %n' notes.txt
chmod g+w notes.txt && stat -c '%a %A %n' notes.txt
chmod o-r notes.txt && stat -c '%a %A %n' notes.txtObserved:
644 -rw-r--r-- notes.txt
664 -rw-rw-r-- notes.txt
660 -rw-rw---- notes.txtEach command changed only the bits it named.
Run umask in a subshell so the change does not stay in your session:
(umask 077; touch private.txt; stat -c '%a %A %n' private.txt)
(umask 002; touch shared.txt; mkdir made_dir; stat -c '%a %A %n' shared.txt made_dir)Observed:
600 -rw------- private.txt
664 -rw-rw-r-- shared.txt
775 drwxrwxr-x made_dirWith 077, the file lost every group and other bit. With 002, the group gained write on both the file and the directory. The directory started from 777 and lost only 002.
This step needs an account you can create in your own VM. Run it there only.
sudo useradd -m labuser
sudo -u labuser cat ~/permlab/notes.txtI did not run these commands. The WSL account needs a password for sudo, and I did not enter one. What to look for: with mode 600, the second command should fail with a Permission denied message, because labuser is neither the owner nor in the group.
Next, relax the file for others:
chmod 644 notes.txt
sudo -u labuser cat ~/permlab/notes.txtWhat to look for: the content prints only if labuser can also enter your home directory and ~/permlab. Many distributions create home directories as 755, but some use 750. If a parent directory denies x to other users, the read still fails, even with a readable file. Check with ls -ld ~ ~/permlab. On the Ubuntu 24.04 WSL install I used, ls -ld ~ printed drwxr-x---, which is 750, so a second user would be blocked there even after chmod 644. This is the directory x bit from earlier, in practice.
List world-writable regular files under your home directory, without crossing into other file systems:
find ~ -xdev -type f -perm -o+w 2>/dev/nullIn a clean lab this prints nothing. To see the search work, make one file writable on purpose:
chmod 666 ~/permlab/notes.txt
find ~ -xdev -type f -perm -o+w 2>/dev/null
chmod 644 ~/permlab/notes.txtObserved: the first search printed /home/usurio/permlab/notes.txt. After the fix, the same search printed nothing.
List setuid binaries in a system directory:
find /usr/bin -perm -4000 -type f 2>/dev/null | sortObserved on Ubuntu 24.04 under WSL, this printed ten files, including /usr/bin/passwd, /usr/bin/sudo, /usr/bin/su, /usr/bin/mount and /usr/bin/umount. Your list depends on the distribution and installed packages. In a review, compare the list with what the package manager says should be setuid. Treat an unexpected entry as a question to answer, not as proof of an attack.
Check the sticky bit on temporary directories:
stat -c '%a %A %n' /tmp /var/tmpObserved: 1777 drwxrwxrwt for both. The t means users can create files there but cannot delete files that belong to someone else.
rm -rf ~/permlab
sudo userdel -r labuseruserdel -r also deletes the account's home directory. Run it only for the lab account you created.
The same first steps in Git Bash for Windows did not behave like Linux. After chmod 600 notes.txt, stat still reported 644, and later chmod calls did not change the output either. Windows stores access in NTFS access control lists, and Git Bash maps only some of the bits. A Windows lab can teach the commands, but it cannot show what a Linux mode does. Use WSL with a Linux distribution, or a VM, for this article.
Using 777 to fix a denied error. It works, and it also lets everyone write and execute the file. Find which class is denied and grant only that class.
Forgetting the directory. A readable file can still be unreachable if a parent directory lacks x for the user. Check every directory on the path.
Using chmod -R on a whole tree. Recursive changes often set execute on regular files or remove it from directories. Use find with -type f or -type d to target one kind.
Trusting the mode of a copied file. cp, tar and archive tools may keep or reset modes depending on flags. Check the result with stat.
Reading symbolic modes as absolute. chmod g+w adds one bit. chmod g=w clears read and execute for the group. The difference is easy to miss.
Reaching for root first. Many read and write problems are fixed with group membership or a directory mode, not with a setuid helper or a root shell.
A permission review starts with one question: who can change what a privileged process reads or runs? Three findings come up again and again:
When you write up a finding, name the path, the mode, the owner, the group, the process that reads or runs it, and the change that fixes it. Test in your own lab. Do not run these searches against systems you do not administer, and do not use sudo -u on someone else's machine.
Once the basic modes are clear, learn access control lists with getfacl and setfacl, which add per-user entries beyond owner, group and other. Read the chattr manual for immutable and append-only flags. Use namei -l /path/to/file to print the mode of every component in a path. It is the quickest way to find the directory that blocks a service.
For the shell habits behind these commands, start with the earlier Field Manual article on Bash basics for security students. The next article, what SSH is and how SSH keys work, covers SSH keys, which depend on the same modes: the private key file must be readable only by its owner, and an SSH client refuses to use a key whose mode is too open.
Least privilege starts with file modes. A process that can write less does less damage when it is wrong, and a file that others cannot change is one less thing to review.
Linux and developer tools
Learn pipes, redirection, variables, loops, exit codes and set -euo pipefail, then use them to count failed logins and hash a folder.
Linux and developer tools
How SSH host and user keys work, why Ed25519 with a passphrase is the default to use, and how to set up and harden a key login on a lab VM you own.
Linux and developer tools
GitHub explained for beginners: what Git and GitHub do, the terms you need, a local Git practice run, and how to check a project before you run it.