Linux Fundamentals

Processes & Services

ps, top, kill, jobs, and systemd/systemctl - see and control what's running.

Medium 18 minprocessessystemdservices

Every running program on a Linux system is a process - and understanding what's running, who's running it, and how to interact with it is the difference between owning a system and being confused by one.

Process Interaction Requires Authorization

Killing processes, inspecting process memory, and exploiting services are actions that affect system stability and constitute system access. Only perform these actions on systems you own or have explicit written authorization to test. Killing critical processes on a production system can cause outages.

What Is a Process?

When you run a program, the OS creates a process - an instance of that program in memory, with its own PID (Process ID), memory space, file descriptors, and execution state.

Every process has:

  • PID - unique process ID
  • PPID - parent process ID (the process that spawned it)
  • UID/GID - the user and group it runs as
  • State - running, sleeping, stopped, zombie
  • Priority/nice value - scheduling priority

Process IDs are assigned sequentially. PID 1 is always the init system (systemd on modern Linux). PID 2 is typically a kernel thread manager.

ps - Snapshot of Running Processes

ps shows a point-in-time snapshot of processes. Two forms you need to know:

kali@vr4cs: ~
 

The aux flags: a = all users, u = user-oriented format, x = include processes without a controlling terminal.

Column meanings:

ColumnMeaning
USERWho owns the process
PIDProcess ID
%CPUCPU usage percentage
%MEMMemory usage percentage
VSZVirtual memory size (KB)
RSSResident set size - actual RAM used (KB)
TTYTerminal (? = no terminal, background process)
STATProcess state
STARTWhen it started
TIMETotal CPU time used
COMMANDThe command line

Process states (STAT column):

CodeMeaning
SSleeping (waiting for I/O or event)
RRunning or runnable
ZZombie (finished but parent hasn't cleaned up)
TStopped
sSession leader
+In foreground process group
lMulti-threaded

The other common form, ps -ef, uses UNIX syntax and is common on non-Linux systems too:

kali@vr4cs: ~
 

-ef shows the PPID (parent PID), which is useful for understanding the process tree.

Security-relevant ps patterns:

# Find processes running as root
ps aux | grep "^root"
 
# Find processes with interesting names
ps aux | grep -E "python|nc|ncat|netcat|socat|bash.*-i"
 
# Show full command lines (truncated by default in some systems)
ps auxww
 
# Process tree view
ps axjf

Netcat Listening? That's a Flag

ps aux | grep -E "nc|ncat|netcat" finding a nc -lvp 4444 process means someone has a listener open. During incident response or post-exploitation recon, spotting reverse shell processes in ps output is common. Attackers often rename their processes (/proc/<PID>/comm is settable), but the full command line in cmdline and /proc/<PID>/exe still reveals the truth.

top and htop - Live Process Monitoring

ps is a snapshot. top and htop give you a live, updating view.

top       # built-in, always available
htop      # enhanced version, needs to be installed
kali@vr4cs: ~
 

top keybindings:

  • q - quit
  • k - kill process (prompts for PID and signal)
  • r - renice (change priority)
  • M - sort by memory
  • P - sort by CPU (default)
  • u - filter by user

htop is friendlier: color-coded, mouse support, F-key shortcuts, process tree view. Install it with apt install htop.

Signals and kill

Processes communicate via signals - predefined numeric messages the OS can send to processes.

kill PID               # send SIGTERM (15) - polite request to stop
kill -9 PID            # send SIGKILL - forceful immediate termination
kill -SIGTERM PID      # same as kill PID
kill -l                # list all signal names
kali@vr4cs: ~
 

The signals you'll actually use:

SignalNumberMeaning
SIGTERM15Terminate gracefully (can be caught/ignored)
SIGKILL9Kill immediately (cannot be caught or ignored)
SIGHUP1Hangup - many daemons reload config on SIGHUP
SIGINT2Interrupt (same as Ctrl+C)
SIGSTOP19Pause process (same as Ctrl+Z, cannot be caught)
SIGCONT18Continue a stopped process
kill -9 1234           # force-kill PID 1234
killall nginx          # kill all processes named nginx
pkill -u alice         # kill all processes owned by alice
pkill -f "python3 backdoor.py"   # kill by full command line match

SIGKILL Can Cause Data Corruption

kill -9 doesn't give the process any chance to clean up - close files, flush buffers, release locks. For databases and applications that write to disk, this can cause data corruption. Always try SIGTERM first and only escalate to SIGKILL if the process doesn't respond.

Job Control - Background and Foreground Processes

When you run a command in the terminal, it takes over the terminal (foreground). Job control lets you manage this:

command &             # start command in background immediately
Ctrl+Z               # suspend foreground process (SIGSTOP)
bg                   # resume suspended process in background
fg                   # bring background process to foreground
fg %2                # bring job number 2 to foreground
jobs                 # list background/suspended jobs
kali@vr4cs: ~
 

nohup - Surviving Logout

When you log out of a shell, all its child processes receive SIGHUP and typically die. nohup (no hangup) makes a process immune to SIGHUP, letting it run after you disconnect:

nohup command &                          # run in background, survive logout
nohup long_scan.sh > scan_output.txt &   # capture output to file

nohup in Penetration Testing

On a compromised system, attackers use nohup (or disown, screen, tmux) to keep their implants and reverse shells running after they disconnect. During forensics, finding nohup'd processes or screen/tmux sessions is a sign of persistent access. Defenders: check for orphaned processes not attached to any terminal (TTY '?' in ps output).

systemd and systemctl - Service Management

Modern Linux systems use systemd as PID 1 and the service manager. Almost everything that runs automatically at boot is a systemd unit - a .service, .timer, .socket, or .target file.

Key systemctl commands:

# Query service status
systemctl status ssh
systemctl status nginx
systemctl is-active mysql
systemctl is-enabled apache2
 
# Control services (needs root)
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
systemctl reload nginx      # reload config without restarting
 
# Enable/disable at boot
systemctl enable ssh        # start on boot
systemctl disable ssh       # don't start on boot
 
# List all services
systemctl list-units --type=service
systemctl list-units --type=service --state=running
kali@vr4cs: ~
 

Where service unit files live:

  • /lib/systemd/system/ - unit files installed by packages (don't edit these)
  • /etc/systemd/system/ - local overrides and custom services (edit these)
  • /run/systemd/system/ - runtime units (temporary)
# See what a service actually runs
cat /lib/systemd/system/nginx.service
systemctl cat ssh          # show the service file cleanly
 
# Check for user-level services
systemctl --user list-units --type=service

Services as Attack Surface

From a security perspective, every running service is a potential entry point:

Understanding what's exposed:

# What services are running?
systemctl list-units --type=service --state=running
 
# What's listening on network ports? (covered in depth in Networking module)
ss -tlnp
netstat -tlnp
 
# Find custom/non-standard services
systemctl list-units --type=service | grep -v "systemd\|dbus\|NetworkManager"
kali@vr4cs: ~
 

That backup-sync.service runs a custom shell script as root. If /opt/backup/sync.sh is world-writable:

ls -la /opt/backup/sync.sh
# -rwxrwxrwx 1 root root 128 May 27 09:00 /opt/backup/sync.sh

Game over - append a reverse shell to the script, wait for it to restart, get root. This exact pattern - root-owned services running writable scripts - appears constantly in CTFs and real pentest engagements.

Service Security Checklist

When auditing a Linux system's services: (1) list all running services, (2) find non-standard/custom ones, (3) read their unit files to see what commands they run and as what user, (4) check if those scripts/binaries are writable by non-root users, (5) check if the service creates files in writable locations. Any writable path in a root-owned service is a privilege escalation vector.

Timers (the systemd equivalent of cron) are also worth checking:

systemctl list-timers --all

These run commands on a schedule. If a timer runs a world-writable script, you can inject code and wait.

Key Takeaways

  • Every running program is a process with a PID, owner, parent PID, and state. ps aux gives you a full snapshot; top/htop give live views.
  • kill sends signals to processes. SIGTERM (15) is a polite request; SIGKILL (9) is an immediate forced termination that bypasses cleanup.
  • Job control (&, Ctrl+Z, bg, fg, jobs) lets you manage foreground and background processes. nohup makes processes survive logout - used by attackers to maintain persistence.
  • systemd is the service manager on modern Linux. systemctl status/start/stop/enable/disable are the core commands.
  • Services are attack surface. Custom services running as root that execute world-writable scripts are classic privilege escalation vectors. Always check what services are running, what they execute, and who can write those executables.
  • systemctl list-units --type=service --state=running combined with systemctl cat <service> gives you a complete picture of what the system is running and how.