Linux Command Line for Security

Cyber Security · Lesson 8 of 31 7 min read Updated

Most web servers, cloud machines and security tools run on Linux, and Kali Linux in your lab is no exception. A security professional who is slow at the command line is slow at everything: reading logs, checking permissions, finding what is running and fixing it.

In this lesson you will practise the commands you will use daily in security work, all on your own Kali and target VMs. You will also see how Linux permissions work, why SUID files deserve attention, and how pipes turn small commands into powerful log analysis.

Moving around and reading files

The shell is the program that reads your commands; on most systems it is Bash or Zsh (Kali uses Zsh by default). These commands work in both:

Terminal
pwd                     # print the current folder
ls -la /etc             # list all files, including hidden ones, with details
cd /var/log             # change folder
cat /etc/os-release     # print a small file
less /etc/ssh/sshd_config   # page through a long file (q to quit)
head -n 5 /etc/passwd   # first 5 lines
tail -f /var/log/syslog # follow a file as new lines arrive (Ctrl+C to stop)

Files whose names start with a dot (such as .bash_history or .ssh) are hidden from a plain ls. Attackers and defenders both look there, because hidden files often hold history, keys and configuration.

Users, groups and sudo

Every process runs as a user. The all-powerful account is root (user ID 0). Normal users get root powers for a single command with sudo, which asks for their own password and logs the action.

Terminal
whoami                  # your user name
id                      # your user ID, group ID and groups
sudo -l                 # which commands you are allowed to run with sudo
getent passwd kali      # the account entry for user kali
sudo cat /etc/shadow    # password hashes: readable only by root

/etc/passwd lists accounts and is readable by everyone; the password hashes live in /etc/shadow, which only root can read. If /etc/shadow is ever readable by normal users, that is a serious misconfiguration. Working as a normal user and using sudo only when needed follows the principle of least privilege: give every person and program only the access it needs.

Permissions and chmod

ls -l shows a permission string such as -rwxr-x---. The first character is the type (- file, d directory, l link). Then come three groups of three letters for the owner, the group and others: r read, w write, x execute. Each group is also written as a number: read 4, write 2, execute 1, added together.

StringNumberTypical use
rw-------600Private SSH key, secrets file
rw-r-----640App config readable by the web server's group
rwxr-xr-x755Programs and public folders
rw-rw-rw-666Almost never correct: anyone can change it
Terminal
chmod 600 ~/.ssh/id_ed25519        # only the owner can read or write the key
sudo chown root:www-data config.php
sudo chmod 640 config.php          # owner rw, group r, others nothing

Two special bits matter for security. SUID (shown as s in the owner's execute spot) makes a program run as its owner, often root. /usr/bin/passwd needs it so normal users can change their own password. A SUID root program with a bug, or an unexpected SUID file, can let a normal user become root, so defenders audit them. The sticky bit (t on a directory such as /tmp) stops users deleting each other's files.

This Python checker turns permission strings into numbers and flags risky ones:

PythonRun it yourself
def to_number(perm):
    total, special = "", 0
    for who, part in enumerate([perm[1:4], perm[4:7], perm[7:10]]):
        value = (4 if part[0] == "r" else 0) + (2 if part[1] == "w" else 0)
        value += 1 if part[2] in "xst" else 0
        if part[2] in "sS":
            special += 4 if who == 0 else 2
        if part[2] in "tT":
            special += 1
        total += str(value)
    return (str(special) if special else "") + total

files = {
    "/usr/bin/passwd": "-rwsr-xr-x",
    "/home/kabir/.ssh/id_ed25519": "-rw-------",
    "/var/www/html/config.php": "-rw-rw-rw-",
    "/tmp": "drwxrwxrwt",
}
for path, perm in files.items():
    note = ""
    if perm[3] in "sS":
        note = "SUID: runs as the file's owner"
    elif perm[8] == "w" and perm[9] in "tT":
        note = "world-writable but sticky (normal for /tmp)"
    elif perm[8] == "w":
        note = "WORLD-WRITABLE: fix this"
    print(f"{perm} {to_number(perm):>4}  {path}" + (f"  {note}" if note else ""))
Output
-rwsr-xr-x 4755  /usr/bin/passwd  SUID: runs as the file's owner
-rw-------  600  /home/kabir/.ssh/id_ed25519
-rw-rw-rw-  666  /var/www/html/config.php  WORLD-WRITABLE: fix this
drwxrwxrwt 1777  /tmp  world-writable but sticky (normal for /tmp)

On your own target VM, you can list every SUID file and every world-writable file. Compare the list with a fresh install; anything new deserves a question.

Terminal
sudo find / -xdev -type f -perm -4000 2>/dev/null
sudo find / -xdev -type f -perm -0002 2>/dev/null

-perm -4000 matches files with the SUID bit, -perm -0002 matches files writable by others, -xdev stays on one file system, and 2>/dev/null hides "permission denied" messages.

Processes, services and network ports

Terminal
ps aux --sort=-%cpu | head      # busiest processes
systemctl status ssh            # is the SSH service running?
sudo systemctl disable --now telnet.socket   # stop and disable a service you do not need
sudo ss -tulpn                  # listening ports and the processes behind them

A defender's habit: for every listening port, name the service, the owner and the reason it exists. Anything you cannot explain should be stopped or investigated.

Logs, pipes and counting

Linux logs live in /var/log and in the systemd journal. On systems that run rsyslog (common on Ubuntu servers), SSH login events also go to /var/log/auth.log; on systems that use only the journal, ask journalctl:

Terminal
journalctl -u ssh --since today
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn

The second line is a classic pipeline: grep keeps failed logins, awk prints the fourth field from the end (the IP address), sort | uniq -c counts each IP, and sort -rn puts the biggest count first. The same logic in Python, on sample lines from a lab target:

PythonRun it yourself
from collections import Counter

log = """Nov  3 10:01:12 target sshd[2211]: Failed password for root from 192.168.56.102 port 51022 ssh2
Nov  3 10:01:15 target sshd[2211]: Failed password for root from 192.168.56.102 port 51022 ssh2
Nov  3 10:02:40 target sshd[2230]: Accepted publickey for meera from 192.168.56.1 port 50110 ssh2
Nov  3 10:03:05 target sshd[2242]: Failed password for invalid user admin from 192.168.56.102 port 51090 ssh2
Nov  3 10:04:44 target sshd[2250]: Failed password for meera from 192.168.56.1 port 50122 ssh2"""

counts = Counter(line.split()[-4] for line in log.splitlines() if "Failed password" in line)
for ip, n in counts.most_common():
    print(f"{n:>7} {ip}")
Output
3 192.168.56.102
1 192.168.56.1

Updates and checking downloads

Most successful attacks use known vulnerabilities that already have patches. Update often:

Terminal
sudo apt update && sudo apt full-upgrade
sha256sum kali-linux-vm.7z

sha256sum prints a file's SHA-256 hash. Compare it with the hash published on the official download page: if even one byte of the file changed, the hash is completely different. You will learn why in Cryptography Basics.

Common mistakes

  • Working as root all the time. One typo can destroy a system, and any program you run gets full power. Use sudo for single commands.
  • Fixing permission errors with chmod 777. It makes the file writable and runnable by everyone. Find the right owner and use the smallest permission that works.
  • Piping internet scripts straight into the shell (curl ... | sudo bash) without reading them first.
  • Ignoring updates on lab VMs. Your Kali host-only adapter still connects to your host computer.

Key points

  • Use ls -la, cat, less, head and tail -f to explore files, including hidden ones.
  • Root is user 0; use sudo for single commands and follow least privilege.
  • Permissions are read 4, write 2, execute 1 for owner, group and others; keys should be 600.
  • Audit SUID and world-writable files with find on your own systems.
  • ss -tulpn, systemctl and journalctl show what is running and what happened.
  • Pipes such as grep | awk | sort | uniq -c turn logs into answers.

Exercise

Add two more entries to the permission checker: /etc/shadow with -rw-r----- and /opt/backup.sh with -rwxrwxrwx. Then add a rule that flags any file whose permission string gives others write access and execute access together as DANGEROUS: anyone can change and run it.

Show solution
Python
files = {
    "/etc/shadow": "-rw-r-----",
    "/opt/backup.sh": "-rwxrwxrwx",
}
for path, perm in files.items():
    if perm[8] == "w" and perm[9] in "xt":
        note = "DANGEROUS: anyone can change and run it"
    elif perm[8] == "w":
        note = "WORLD-WRITABLE: fix this"
    else:
        note = "ok"
    print(perm, path, note)

/opt/backup.sh is flagged as dangerous. If a root cron job runs it, any user could edit the script and get their commands run as root.

Quick Quiz

1. Which permission number makes a private key readable and writable only by its owner?

2. Which file holds Linux password hashes?

3. Why do defenders audit SUID files?

4. In the pipeline sort | uniq -c, what does uniq -c do?

Learn Cyber Security with a mentor

Free lessons get you started. Our Ethical Hacking & Cyber Security training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call