HackBlue

Tools · Linux command line for security · 15 / 28

Which process is listening on a port? Using ss and lsof on Linux

Map every listening socket and established connection to a process and user with ss -tulpn and lsof -i, and spot services bound where they should not be.

9 min read · practice only with authorization

ss replaces netstat

ss -tulpn shows TCP and UDP listening sockets, numeric addresses, and the owning process. Run it as root to see processes for every user. The Local Address column tells you what interface a service is bound to: 127.0.0.1 or ::1 means only local clients, 0.0.0.0 or [::] means every interface.

ss -tnp state established shows active connections and which process owns each one.

Snippet
sudo ss -tulpn
sudo ss -tnp state established
sudo ss -tnp dst :443

lsof for detail

lsof -i :5432 lists processes with sockets on port 5432. lsof -p PID lists every file, library, and socket a process has open, which is useful after you find a suspicious PID. lsof -i -n -P avoids slow name lookups.

A process with a socket to an unfamiliar external address on a high port deserves the questions from the previous tutorial: who, what started it, and where is the binary.

Snippet
sudo lsof -i :5432
sudo lsof -i -n -P | grep ESTABLISHED
sudo lsof -p 1234 | head -40

Lab: find a misbound database

Install PostgreSQL or Redis on a lab VM and change its listen setting to all interfaces. Use ss to see it on 0.0.0.0, scan it from your scanner VM to confirm reachability, then change it back to 127.0.0.1 and verify both locally and with Nmap.

This is one of the most common real findings: a database that only the local application needs, listening on every interface because a tutorial said to.

Snippet
sudo sed -i "s/^#\?listen_addresses.*/listen_addresses = '*'/" /etc/postgresql/*/main/postgresql.conf
sudo systemctl restart postgresql
sudo ss -tlnp | grep 5432
# fix
sudo sed -i "s/^listen_addresses.*/listen_addresses = 'localhost'/" /etc/postgresql/*/main/postgresql.conf
sudo systemctl restart postgresql && sudo ss -tlnp | grep 5432

Baseline listeners

Save sudo ss -tulpn output for each server role. A new listener that does not match a deployment is worth investigating, because backdoors often open a port.

Remember that a firewall can block a 0.0.0.0 listener, but binding to localhost removes the exposure even if the firewall is later changed.

Diagram

A database bound to all interfaces versus localhost

Same service, different exposure.

0.0.0.0:5432

Who can connect. Anyone who can reach any interface.

Depends on. Firewall rules staying correct.

Typical cause. Copied configuration.

127.0.0.1:5432

Who can connect. Only local processes.

Depends on. Nothing else.

Fit. App and database on the same host.

Check yourself

1. What does a Local Address of 0.0.0.0:6379 mean?

2. Which command lists every file and socket a given process has open?

3. Why is binding to localhost better than relying only on the firewall?