SEC401 - Containers, Linux and Mac Security
Lab 6.1 - Linux Permissions
Solo, Lab
Focus: Linux Security
Level: SEC401
Date: Apr 2026
Artifacts: Sanitized terminal screenshots from a Docker-based permissions lab container
TL;DR
- •Started a Docker lab container and connected as user annika
- •Observed default umask 0022 producing 644 files and 755 directories
- •Tightened umask to 0027 (group-readable, world-nothing) and confirmed 640/750 outputs
- •Demonstrated the sticky bit on /tmp (drwxrwxrwt) for shared-directory safety
Skills demonstrated
Note: Course-provided PCAPs and lab instructions are not shared. Only my own captures and sanitized notes are published.
Why this matters
Most Linux privilege escalation findings in CTFs and real audits come down to permissions that should have been tighter. Understanding umask, group-vs-other bits, and the sticky bit is the difference between writing a hardening baseline and copy-pasting one you don't understand. It's also what lets you explain to a dev why their world-readable config file is a finding.
Context
This lab demonstrates the core Linux discretionary access control primitives — file mode bits, umask, and the sticky bit — inside a disposable Docker container. The goal is to understand why default permissions are what they are, how to tighten them for a hardening baseline, and how the sticky bit protects world-writable directories like /tmp from cross-user tampering.
Tools used
Steps taken
1Start the Docker lab container
Ran start_6.1.sh to bring up the lab-61-permissions-1 container on the lab-61_default network. The 'docker stop/rm requires at least 1 argument' lines are the script safely reporting that no prior container existed to clean up. End state: container Running 2/2.
$ cd /sec401/labs/6.1
$ ./start_6.1.sh2Connect into the container as annika
Ran connect.sh to drop into a shell inside the container as user annika. Every subsequent command runs inside this disposable container, so nothing touches the host.
$ ./connect.sh3Create a file with the default umask
Wrote a line to test_perms.txt with echo, cat-ed it back to confirm content, then ls -l to read the mode. Output: -rw-r--r-- 1 annika annika 7. Owner rw, group r, other r — the canonical 644 you get with umask 0022.
$ echo annika > test_perms.txt
$ cat test_perms.txt
$ ls -l test_perms.txt4Read the current umask
umask prints 0022. The mask works by subtracting bits from the base (666 for files, 777 for dirs): 666 - 022 = 644 for files, 777 - 022 = 755 for dirs. That's why the file above landed on 644 without any chmod.
$ umask5Tighten umask to 0027 and retest
Set umask to 0027 (group read only, world nothing), created a new file and directory, and listed them. Output: -rw-r----- for secure.txt and drwxr-x--- for secure_dir. That's 640/750 — the hardening baseline used by most CIS benchmarks because it cuts world access entirely while keeping same-group collaboration working.
$ umask 0027
$ echo annika > secure.txt
$ mkdir secure_dir
$ ls -ld secure*umask 0027mask bits = user 0, group 2, other 7Effectfiles default to 640, dirs to 7506Sticky bit on /tmp
Listed /tmp with ls -ld: drwxrwxrwt. The trailing t is the sticky bit — directory is world-writable, but only the file owner (or root) can rename or delete a file inside it. Created /tmp/sticky_bit_test.txt to demonstrate: any user can write to /tmp, but annika's file is protected from deletion by other users in the same container.
$ ls -ld /tmp
$ echo "only annika may rename or delete this file" > /tmp/sticky_bit_test.txt
$ ls -l /tmp/sticky_bit_test.txtdrwxrwxrwtd=dir, rwx (user), rwx (group), rwt (other with sticky)t without x would display as TKey findings
Outcome / Lessons learned
Walked through the full Linux permission model from first principles: default umask → file mode bits → tightened hardening umask → sticky-bit semantics on a shared directory. End state is a working mental model for why 644/755 is the default, why 640/750 is the hardened baseline, and why /tmp is drwxrwxrwt specifically.
Set umask 0027 (or 0077 for single-tenant hosts) in /etc/login.defs and /etc/profile so it applies to every interactive session. For service accounts, set the umask in the systemd unit's UMask= directive so spawned processes inherit it. Audit existing sensitive paths (/etc, /var/log, /home) for world-readable files that shouldn't be, and confirm every world-writable directory on the filesystem has the sticky bit — find / -perm -0002 -type d ! -perm -1000 is the one-liner to enforce that.
Security controls relevant
- CIS Linux benchmark: default umask 027
- File permission auditing (find -perm)
- Sticky bit on world-writable directories
- Service-account systemd UMask= hardening
- Group-based collaboration without world access
What I took away from this
umask is the one Linux setting that silently shapes every file created on the system. Most hardening guides start with 027 without explaining why — the why is that 027 is the tightest mask that still permits same-group collaboration, and most systems have a legitimate reason to preserve group access (shared dev team, service account + admin group, etc.). 077 is tighter but breaks those workflows.
The sticky bit is a good reminder that Unix permissions aren't just user/group/other — they're a small set of orthogonal tools, and the sticky bit is the one that makes shared directories safe. /tmp, /var/tmp, /dev/shm all rely on it. Any world-writable directory without the sticky bit is a finding worth chasing, because it means any user can delete or rename another user's files inside it.
The docker-compose lab pattern here is understated but useful. You get a clean, throwaway environment for every permission exercise, no risk of clobbering your host, and the setup script handles the network and container lifecycle. Same pattern scales to reproducing customer bugs or running hostile code — it's the defensive version of the sandbox approach.