Skip to content
Breachfolio
Hero illustration for: Linux fundamentals for security, the working subset.
LEVEL 2 CYBERSECURITY

Linux fundamentals for security, the working subset.

The 25 commands you will type every day, the five you will type in a hurry, and the file permissions that decide who owns the box.

13 min read Daniel A. & Óscar S.

Most "intro to Linux" material teaches you Linux as a Unix system. We are going to teach you Linux as a security workstation – what you actually do at the keyboard during recon, triage, and post-exploitation.

The shell, in one paragraph

The shell is a program that reads a line, runs it, and prints output. Three special characters do most of the work: | sends output to another program, > sends it to a file, and && chains two commands so the second only runs if the first succeeds. You can build anything from there.

The shell is not the commands

New arrivals conflate "bash" with "Linux". They are not the same thing, and the distinction matters the first time something breaks. The shell: bash, zsh, or whatever your prompt is running – is one specific program: an interpreter that reads a line of text and either runs it itself or hands it off. The commands you type are, in most cases, separate programs living on disk that the shell finds and launches for you.

Type ls and the shell doesn't "have" an ls feature built in: it searches every directory in the $PATH environment variable, in order, until it finds an executable file named ls, then forks and executes it:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ which ls
/usr/bin/ls
$ type cd
cd is a shell builtin

That last line is the exception that proves the rule. A handful of commands – cd, pwd, alias, export, history – are builtins: code that lives inside the shell process, not a separate file on disk. cd has to be a builtin, because changing "the current directory" only means something if it changes the shell's own state; an external cd program would change its own working directory, exit, and leave your shell exactly where it started. type tells you which kind of thing you're actually invoking – builtin, alias, function, or file – and which does the narrower job of finding an external command's location on $PATH.

Why this matters for security

$PATH is searched in order, and if an attacker can write a malicious file named ls (or sudo, or curl) into a directory that appears earlier in your $PATH than the real one, your next keystroke runs their code instead of the real tool. This is PATH hijacking, a real privilege-escalation technique – never add a world-writable directory, or worse, . (the current directory), to your $PATH.

Swapping bash for zsh changes prompt behaviour and scripting syntax; it does not change what ls, grep, or chmod do, because those live outside the shell entirely. That is why the working vocabulary below transfers no matter which shell you land in.

The 25 commands

If you can use these without thinking, you can read 80% of real-world security workflows.

CommandWhat it doesUsed for
ls -laList files with detailsRecon, permission audit
cd / pwdMove / where am IAlways
cat / lessRead a fileConfigs, logs
grep -rSearch text recursivelyFinding secrets, errors
findSearch by name / size / timeLocating dropped files
chmod / chownChange permissions / ownerHardening, lateral movement
ps -efList processesWhat is running
top / htopLive process viewTriage
netstat -tulpnOpen ports + processWhat is listening
ss -tulpnModern replacementSame
curl -vHTTP from CLIAPI testing
wgetDownload filesPulling tools
ssh / scpRemote shell / copyLateral movement
tarPack / unpack archivesExfil, deploy
systemctlService controlPersistence, audit
journalctlRead systemd logsTriage
tail -fFollow a logLive monitoring
historyWhat was typedForensics
sudoRun as rootPrivilege use / audit
id / whoamiWho am I, what groupsRecon on yourself
uname -aKernel infoExploit selection
iptables -LFirewall rulesEgress mapping
dig / hostDNS lookupRecon
scp / rsyncMove files reliablyBackups, exfil
tmux / screenPersistent sessionsLong jobs

Permissions in five minutes

Every file has three triplets of permissions: owner, group, everyone. Each triplet has three bits: read, write, execute.

$ ls -l /etc/shadow
-rw-r----- 1 root shadow 1832 May  6 14:01 /etc/shadow
   ^ owner=root  group=shadow
   rw- = read+write for owner
   r-- = read for shadow group
   --- = no access for everyone else

Reading the full ls -l line

The shadow example above is one field of interest. A real line has ten, and once you can read all ten without stopping to think, permission problems stop being mysterious.

$ ls -l deploy.sh
-rwxr-xr-- 1 dan devops 812 Jul 22 09:14 deploy.sh
FieldValueMeaning
Type-Regular file. d=directory, l=symlink, s=socket.
Owner permsrwxdan can read, write, and execute this file.
Group permsr-xMembers of devops can read and execute, not write.
Other permsr--Everyone else can only read it.
Link count1Number of hard links pointing at this inode.
OwnerdanThe user who owns the file.
GroupdevopsThe group that owns the file.
Size812Bytes.
ModifiedJul 22 09:14Last write time, not last read.
Namedeploy.shSymlinks show name -> target here instead.

The two most common permission mistakes both live in that first character group: the execute bit missing on something you meant to run, or a file that should be private staying group- or world-readable – an SSH private key with anything looser than 600 will make ssh refuse to use it at all.

Numeric form: each triplet is a 0–7 digit. chmod 750 file means owner=rwx, group=r-x, world=none. The numbers are read=4, write=2, execute=1 – add them per triplet, so rwx = 4+2+1 = 7, r-x = 4+0+1 = 5, r-- = 4+0+0 = 4.

Symbolic notation: the other way to write it

Numeric mode replaces a whole triplet at once – you can't add one permission without knowing the target number for the rest. Symbolic mode edits in place instead, which is what you want for a one-off change on a file whose current permissions you haven't memorised. The grammar is who + operator + what: who is u/g/o/a (user, group, other, all); operator is +/-/= (add, remove, set exactly); what is any combination of r, w, x.

# Make a script executable for its owner only, leave everything else alone
$ chmod u+x deploy.sh

# Remove write access for the group
$ chmod g-w deploy.sh

# Set "other" to read-only, exactly, regardless of what it was before
$ chmod o=r deploy.sh

# Combine several changes in one call
$ chmod u+rwx,g+rx,o-rwx deploy.sh

Use numeric mode when you know the exact target state; use symbolic mode when adjusting one bit on a file you didn't create.

Chmod values you'll actually type

ValueMeaningTypical use
600Owner rw, nobody else anythingSSH private keys, secrets, credential files
644Owner rw, group/other readDefault for most non-executable files
400Owner read-onlyFiles you want protected even from your own accidental overwrite
700Owner rwx, nobody else anythingPersonal scripts, the ~/.ssh directory
750Owner rwx, group rx, other noneService files shared within a team or group, not the world
755Owner rwx, group/other rxDefault for scripts and executables meant to be run by anyone
777Everyone rwxAlmost never correct: a routine finding in security audits
SUID, the one footnote that matters

A file with the SUID bit set runs as its owner, not as you. find / -perm -4000 -type f 2>/dev/null lists all of them. On a compromised box, this is where you look first.

The five "in a hurry" commands

When the alert is fresh and the senior engineer is in a meeting, these are the ones you reach for.

  1. last -a | head -20: who logged in recently, from where.
  2. w: who is on the box right now, what are they running.
  3. ss -tunap: active sockets and the process behind each one.
  4. ls -lat /tmp /var/tmp /dev/shm | head: fresh files in the obvious dumping grounds.
  5. journalctl --since "1 hour ago" -p warning: recent warnings in the system log.

Pipes: the move that doubles your effective vocabulary

Two commands you have can be combined into a third. A few examples worth memorising:

# Top 10 processes by RSS memory
ps -eo pid,rss,cmd --sort=-rss | head -11

# Find every world-writable file under /etc
find /etc -type f -perm -o=w 2>/dev/null

# Recent failed SSH logins, grouped by source IP
journalctl -u ssh | grep "Failed" | awk '{print $NF}' | sort | uniq -c | sort -rn

Memorising these is not the point. The point is internalising the shape: listfiltergroupcount. Once you see it, you will write your own.

When it breaks: two errors you'll see constantly

Two error messages account for a disproportionate share of "why isn't this working" moments. Both are diagnostic, not scary – each one tells you exactly where to look, if you know what it actually means.

"command not found"

This is not a permissions problem and the tool is not broken. It means the shell walked every directory in $PATH, in order, and found no executable file matching that name. Nothing more, nothing less.

$ nmap -sV target.local
bash: nmap: command not found

The first thing to check is not "is my system broken" – it's one of three much more boring possibilities, roughly in order of likelihood:

  1. Typo. ifconfg instead of ifconfig. More common than anyone likes to admit.
  2. Not installed. Confirm with your package manager: dpkg -l | grep nmap (Debian/Ubuntu) or rpm -q nmap (RHEL/Fedora). If it's missing, install it.
  3. Installed, but not on $PATH. Tools installed via pip install --user, go install, or a manual download often land in ~/.local/bin or ~/go/bin, which may not be on $PATH yet. Check with echo $PATH, and if the directory is missing, add it in your shell's startup file (~/.bashrc or ~/.zshrc) and re-source it: source ~/.bashrc.

"Permission denied"

This one has two genuinely different causes that people constantly conflate, and reaching for sudo as a reflex fixes one of them by accident while masking the real problem in the other.

$ ./deploy.sh
bash: ./deploy.sh: Permission denied

Cause one: the execute bit isn't set. You can cat the file fine – you can read it – but reading and executing are separate permission bits, and a freshly downloaded or copied script often keeps its read bit while losing execute. Check with ls -l deploy.sh; if you don't see an x in the owner triplet, that's it. Fix: chmod +x deploy.sh.

Cause two: you genuinely don't have access. Wrong owner, wrong group, or a parent directory that blocks you – you need execute permission on every directory in the path just to traverse into a file, not only on the file itself. Check ownership with ls -l, check your own groups with id, and compare. If you're not in the group that owns the file, the fix is getting added to that group, not reflexively reaching for sudo.

A third, less common cause: the filesystem may be mounted noexec, which blocks execution of any file on it regardless of permission bits – common on /tmp in hardened environments as a deliberate anti-malware control. Check with mount | grep noexec. No amount of chmod fixes that.

None of this becomes instinct from reading it once: it comes from breaking things on purpose, on a machine where breaking things has no consequences. If you don't have one set up yet, our guide on building a home lab on one laptop walks through getting a throwaway Linux VM running in under an hour.

Frequently asked questions

What are the most important Linux commands for cybersecurity?
A working set of about 25 commands covers 80% of real-world security workflows: navigation and file reading (ls -la, cd, cat, less), searching (grep -r, find), permissions (chmod, chown), process and service inspection (ps -ef, top, systemctl, journalctl), network inspection (netstat -tulpn, ss -tulpn, curl, dig), and remote access (ssh, scp). Chaining them with pipes is what turns a short list into a large vocabulary.
How do Linux file permissions work?
Every file has three triplets of permissions – owner, group, and everyone else – and each triplet has three bits: read, write, and execute. In numeric form each triplet is a digit from 0-7, where read=4, write=2, execute=1, so chmod 750 means the owner gets read+write+execute, the group gets read+execute, and everyone else gets nothing.
What is the SUID bit and why does it matter for security?
A file with the SUID bit set runs as its owner rather than as the user who executed it, which makes it a common privilege escalation vector if misconfigured. You can list every SUID file on a system with find / -perm -4000 -type f 2>/dev/null – on a compromised box, this is one of the first things to check.
Which Linux commands are useful during incident response?
When responding to a fresh alert, the fast checks are: last -a | head -20 for recent logins, w for who is active right now, ss -tunap for active sockets tied to processes, ls -lat /tmp /var/tmp /dev/shm | head for freshly dropped files in common dumping grounds, and journalctl --since "1 hour ago" -p warning for recent system warnings.
Who writes this

Daniel A. and Óscar S. run Breachfolio, a small independent site about security and AI. This article was drafted with AI assistance and reviewed by a person before it went live. We write from documentation, vendor sources and published research rather than from original lab benchmarks, and we link a source in the sentence that relies on it. How we work · About us