Linux commands every ethical hacker needs (with real examples)
find, grep, awk, ss, sudo -l, nc, base64, tmux… Linux commands for ethical hacking organized by task, with real examples and what to look for in the output.
Hacking tools change every year; the Linux terminal doesn't. Almost everything you do in a lab (finding a forgotten password in a config file, understanding why a binary hands you root, checking whether a port is open or decoding a weird string) comes down to a handful of Linux commands combined well. This guide organizes them by task, with realistic examples and, above all, what to look for in the output, which is what really makes the difference.
If you don't have an environment set up yet, start with Kali Linux for beginners, where we already covered the basics (ls, cd, cat…). Here we go a step further. Every example is meant to be run on your own virtual machine or on authorized practice platforms.
1. Moving around and finding things
find: the Swiss army knife of searching
find walks the directory tree and filters by almost any attribute. The structure is always find <where> <conditions> <action>. Add 2>/dev/null to hide the thousands of "Permission denied" errors when searching from /.
# Files with the SUID bit set (a privilege escalation classic)
find / -perm -4000 -type f 2>/dev/null
# Files owned by a specific user
find / -user www-data -type f 2>/dev/null
# Files larger than 50 MB under /home
find /home -type f -size +50M
# Files modified more recently than a reference file
find /var/www -type f -newer /var/www/html/index.php
# Modified in the last 30 minutes
find /etc -type f -mmin -30 2>/dev/null
# Directories your user can write to
find / -type d -writable 2>/dev/null
What to look for: in the SUID list, most entries will be expected binaries (/usr/bin/passwd, /usr/bin/sudo, /usr/bin/mount). The interesting part is whatever doesn't fit: a binary in /opt, a custom script, or a tool like find, vim or python3 with SUID. With -newer and -mmin you spot what changed recently, which is useful both for attacking a lab box and for investigating an incident on your own server.
Watch the -perm syntax: -perm -4000 means "has at least this bit", whereas -perm 4000 (no dash) requires the permissions to be exactly that, and almost never returns anything.
locate: instant search by name
sudo updatedb
locate id_rsa
locate -i backup | grep -E '\.(zip|tar|gz|sql)$'
locate doesn't walk the disk: it queries a database built by updatedb. That's why it's so fast, but it won't see files created after the last update. On Kali it ships as plocate. Use it for quick questions ("is there an SSH key anywhere on this system?") and find when you need filters or fresh results.
which, type and command -v: what actually runs?
which python3
type ll
command -v nc
which tells you which path in your PATH will run; type also tells you whether it's an alias, a function or a shell builtin. This matters when something behaves strangely: if ls turns out to be an alias, or a script in a directory that comes earlier in the PATH, you're not running what you think. That same detail is the basis of an escalation technique known as PATH hijacking.
2. Reading and filtering text
On Linux nearly everything is text: configs, logs, user lists, tool output. Knowing how to filter it is the skill that saves you the most time.
grep: search inside files
# Recursive (-r), case-insensitive (-i), with line numbers (-n)
grep -rni "password" /var/www/ 2>/dev/null
# Only the names of files that contain the string
grep -rli "DB_PASS" /var/www/
# Invert the match: strip comments and blank lines from a .conf
grep -vE '^\s*(#|$)' /etc/ssh/sshd_config
# Extract only the IP addresses from a log
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log
What to look for: files such as wp-config.php, .env, config.php or settings.py with plaintext credentials. And in an sshd_config stripped of comments, lines like PermitRootLogin yes or PasswordAuthentication yes, which point to a weak configuration.
cut and awk: keep only the columns that matter
# Username and shell for every account (fields 1 and 7, ':'-separated)
cut -d: -f1,7 /etc/passwd
# "Human" users (UID >= 1000) with their home directory
awk -F: '$3 >= 1000 {print $1, $6}' /etc/passwd
# Accounts with a login-capable shell
grep -E 'sh$' /etc/passwd
cut is perfect for fixed delimiters; reach for awk when you need conditions or columns separated by variable whitespace. Accounts with /usr/sbin/nologin or /bin/false are service accounts; the ones ending in bash or zsh can log in, which makes them your targets in a lab.
sort | uniq -c: count things and find patterns
# The 10 IPs making the most requests to your web server
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head
This pipeline is a classic: uniq only groups adjacent lines, which is why you have to sort first. The second sort -nr sorts numerically from highest to lowest. A single IP with thousands of requests is usually a scanner or a brute-force attack.
head, tail -f and wc
head -n 20 wordlist.txt # first 20 lines
tail -n 50 /var/log/auth.log # last 50
tail -f /var/log/apache2/access.log # follow the file live
wc -l /usr/share/wordlists/rockyou.txt # number of lines
tail -f is ideal for watching what happens on your test server in real time while you run a tool from another terminal. If you can't find /var/log/auth.log (on recent Kali and Debian, system logs often go only to the systemd journal), the equivalent is journalctl -f. And wc -l tells you how many passwords a wordlist holds before you launch an attack that could take hours.
3. Permissions and users
Reading permissions and changing them with chmod and chown
ls -l script.sh
-rwxr-x--- 1 kali developers 812 Nov 3 10:15 script.sh
The nine characters after the file type are three blocks: owner (rwx), group (r-x) and others (---). In numeric notation r=4, w=2, x=1, so this file is 750.
chmod 600 ~/.ssh/id_rsa # only the owner can read and write
chmod u+x exploit.sh # add execute for the owner
chmod o-r report.txt # remove read for "others"
sudo chown kali:kali notes.txt # change owner and group
SSH refuses to use a private key whose permissions are too open, so chmod 600 is something you'll do often. Conversely, a sensitive file readable by everyone (-rw-r--r-- on a backup full of passwords) is exactly the kind of flaw you look for in an audit.
SUID, explained plainly
Normally a program runs with the permissions of whoever launches it. With the SUID bit set, it runs with the permissions of the file's owner. You'll see it as an s in place of the owner's x:
ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68248 Mar 23 2025 /usr/bin/passwd
passwd needs SUID so a normal user can change their own password in /etc/shadow, which belongs to root. The problem arises when a root-owned SUID binary lets you do more than intended, such as spawning a shell or reading arbitrary files. To check whether a given binary can be abused, the GTFOBins project documents hundreds of cases. Tools like PEASS-ng (LinPEAS) automate this review, but understanding the concept lets you interpret their results instead of copying them.
id and sudo -l: who you are and what you can do
id
uid=1000(kali) gid=1000(kali) groups=1000(kali),27(sudo),998(docker)
sudo -l
User kali may run the following commands on lab:
(root) NOPASSWD: /usr/bin/vim
What to look for: in id, privileged groups such as sudo, wheel, docker, lxd, adm (can read logs) or disk. Being in docker is effectively the same as being root. In sudo -l, any NOPASSWD line and any program that can escape to a shell (editors, interpreters, pagers like less) deserves a close look.
/etc/passwd vs /etc/shadow
| /etc/passwd | /etc/shadow | |
|---|---|---|
| Who can read it | Every user | Only root (and the shadow group) |
| Contents | Username, UID, GID, home directory, shell | Password hash and expiry data |
| Example | kali:x:1000:1000:,,,:/home/kali:/usr/bin/zsh | kali:$y$j9T$...:20390:0:99999:7::: |
The x in the second field of /etc/passwd means the hash lives in /etc/shadow. The hash prefix tells you the algorithm: $6$ is SHA-512 and $y$ is yescrypt, the default on current Debian and Kali. Two serious findings in a lab: /etc/shadow being readable by your user, or /etc/passwd being writable (you could add a user with UID 0). Check with ls -l /etc/passwd /etc/shadow.
4. Processes and services
ps aux: what's running
ps aux | grep -i mysql
ps aux --sort=-%mem | head # top memory consumers
ps -ef --forest # parent-child process tree
What to look for: the first column shows the user. A root process running a script from a directory you can write to is an escalation path. You'll also sometimes spot credentials passed as arguments (mysql -u root -pSecret), visible to any user who lists processes.
ss -tulpn: what's listening
sudo ss -tulpn
Netid State Local Address:Port Process
tcp LISTEN 0.0.0.0:22 users:(("sshd",pid=812,fd=3))
tcp LISTEN 127.0.0.1:3306 users:(("mariadbd",pid=950,fd=21))
The flags mean: tCP, uDP, listening sockets only, the associated process, and numeric output instead of names. The key column is the local address: 0.0.0.0 or [::] means reachable from the network; 127.0.0.1 means local only. An internal service that an external scan couldn't see (like that database) shows up here, and it's common on lab machines. Without sudo you won't see the process name for other users' services.
systemctl and kill
sudo systemctl status ssh
sudo systemctl start apache2
sudo systemctl enable ssh # start at boot
systemctl list-units --type=service --state=running
kill 4321 # ask it to terminate (SIGTERM, signal 15)
kill -9 4321 # force it (SIGKILL), last resort
pkill -f "python3 -m http.server"
On Kali, network services are disabled by default, so systemctl start is what you'll use to bring up a test web or SSH server. Always try plain kill first: it lets the process close files and clean up; -9 doesn't give it that chance.
5. Networking
ip and ping: your starting point
ip a # interfaces and addresses
ip r # routing table and default gateway
ping -c 3 10.10.10.5
When you connect to a practice platform over VPN, a tun0 interface appears: that's the IP to use whenever a target needs to connect back to you. A host not answering ping doesn't mean it's down; many systems (Windows by default) block ICMP.
curl and wget: speaking HTTP from the terminal
curl -I http://10.10.10.5 # headers only
curl -s http://10.10.10.5/robots.txt
curl -v -X POST -d "user=admin&pass=test" http://10.10.10.5/login
wget -q http://10.10.14.2:8000/linpeas.sh -O /tmp/linpeas.sh
What to look for: in the headers, Server and X-Powered-By reveal software and versions; a Set-Cookie without HttpOnly or Secure is a misconfiguration. With -v you see the whole conversation, redirects included. To move files between your lab machines, python3 -m http.server 8000 on the source and wget or curl -O on the destination is the quickest option.
dig: asking DNS
dig example.com +short
dig example.com MX +short
dig -x 8.8.8.8 +short # reverse lookup
dig axfr @nsztm1.digi.ninja zonetransfer.me
The last example attempts a zone transfer against zonetransfer.me, a domain created specifically so you can practice it legally. If a misconfigured DNS server allows it, it hands you every subdomain at once.
nc: checking ports by hand
nc -zv 10.10.10.5 22 80 443 # are they open?
nc -zv -w 2 10.10.10.5 20-25 # a range, 2-second timeout
nc -nv 10.10.10.5 21 # connect and read the banner
-z checks without sending data, -v prints the result and -n skips DNS resolution. For serious scanning you'll use Nmap, but nc is perfect for confirming a single port or grabbing a service banner (FTP, SMTP and SSH usually announce their version when you connect).
6. Archives and encodings
tar -czvf web-backup.tar.gz /var/www/html # create a .tar.gz
tar -tzf web-backup.tar.gz # list without extracting
tar -xzvf web-backup.tar.gz -C /tmp/review # extract to another folder
Remember the letters: create, extract, list, z for gzip, verbose, file (always last, right before the name). List an archive you don't know before extracting it.
echo -n 'admin:admin123' | base64 # YWRtaW46YWRtaW4xMjM=
echo 'YWRtaW46YWRtaW4xMjM=' | base64 -d
xxd mystery.bin | head
file mystery.bin
sha256sum kali-linux.iso
sha256sum -c SHA256SUMS
Use echo -n when encoding: without it a newline sneaks in and the result changes. A string of letters, digits, +, / and one or two trailing = is usually Base64 (for example in an Authorization: Basic header). Remember that Base64 is an encoding, not encryption: anyone can reverse it. xxd shows a hex dump, where the first bytes ("magic numbers") give away the file's real type: 89 50 4e 47 is PNG and 50 4b 03 04 is ZIP, whatever the extension says; file does that check for you. And hashes let you verify that a download hasn't been tampered with.
7. Shell productivity
Pipes and redirections
command > out.txt # overwrite
command >> out.txt # append
command 2> errors.txt # errors only (stderr)
command > all.txt 2>&1 # output and errors to the same file
command 2>&1 | tee log.txt # see it on screen and save it at once
Order matters: in > all.txt 2>&1 you first send output to the file and then say "errors go wherever output goes". The other way round doesn't behave the same. tee is your best friend for documenting: everything you save now will help with the report or the writeup.
History
history | grep nmap # how did I run that scan?
!! # repeat the last command
sudo !! # repeat it with sudo
Ctrl+R # interactive reverse search
In a lab, other users' history files (~/.bash_history, ~/.zsh_history) sometimes contain passwords typed by mistake. Check your own too before sharing screenshots.
tmux: many terminals in one
tmux new -s lab # new session called "lab"
Ctrl+b % # side-by-side panes
Ctrl+b " # top and bottom panes
Ctrl+b arrow keys # move between panes
Ctrl+b c / Ctrl+b n # new window / next window
Ctrl+b d # detach (the session keeps running)
tmux attach -t lab # get back to it
The usual setup: one pane with the VPN, another with a long scan and a third to work in. And if your SSH connection to a remote machine drops, the tmux session keeps running.
Aliases
alias ll='ls -lah'
alias myip="ip -4 addr show tun0 | grep -oP '(?<=inet )[0-9.]+'"
echo "alias ll='ls -lah'" >> ~/.zshrc # make it permanent
Kali uses Zsh, so permanent aliases go in ~/.zshrc (in Bash, ~/.bashrc). Reload with source ~/.zshrc.
Cheat sheet: Linux commands for ethical hacking
| Task | Command | What to look for |
|---|---|---|
| SUID binaries | find / -perm -4000 -type f 2>/dev/null | Unusual binaries |
| Recent changes | find /etc -mmin -30 | Recently modified configs |
| Hunt for credentials | grep -rni "password" /var/www | Plaintext passwords |
| Users with a shell | grep -E 'sh$' /etc/passwd | Accounts that can log in |
| Count occurrences | sort | uniq -c | sort -nr | Values that repeat a lot |
| Identity | id | sudo, docker, lxd, adm groups |
| sudo rights | sudo -l | NOPASSWD and shell-escape programs |
| Processes | ps aux | Root processes, credentials in arguments |
| Local ports | sudo ss -tulpn | Services bound to 127.0.0.1 only |
| Web headers | curl -I URL | Server, X-Powered-By, cookies |
| DNS | dig domain +short | IPs, MX, zone transfers |
| Single port | nc -zv IP PORT | "succeeded" / "open" and banners |
| Decode | base64 -d | Hidden readable text |
| Real file type | file / xxd | head | Magic numbers |
| Keep evidence | cmd 2>&1 | tee log.txt | Everything, for the report |
How to practice these commands
Reading a list of commands isn't enough; you have to use them with a goal. The best place to start is OverTheWire: its Bandit wargame makes you use find, grep, sort | uniq, base64, xxd, tar and nc exactly as in this guide. If you get stuck, we have Bandit solutions explained level by level (try each level before peeking). After that, set up a vulnerable machine on your local network and repeat the permissions and processes sections from inside it: it's exactly the review LinPEAS performs, but understanding every step.
Always practice on your own machine, in local labs or on platforms that explicitly authorize it. Running these searches on other people's systems without permission may be a crime.
If you'd like a guided path with hands-on exercises that build on these commands, take a look at the Hackers en Acción Academy.