Cos'è#
Un socket è un endpoint di comunicazione che permette a due processi di scambiare dati, sia sulla stessa macchina che attraverso la rete. In Linux, un socket è un file con un proprio File Descriptor, gestito dal Kernel.
Un socket è un endpoint di comunicazione. Pensalo come una presa elettrica: da sola non fa nulla, ma quando due prese vengono collegate da un cavo, l'energia (i dati) può fluire.
In pratica, quando un servizio come SSH vuole ricevere connessioni, fa tre cose:
- crea un socket,
- lo "aggancia" a un indirizzo IP + porta (questo si chiama bind),
- e poi si mette in ascolto (listen).
- Da quel momento, quel socket diventa raggiungibile da chiunque conosca quell'indirizzo e porta. 
Cos'è un Socket a Livello Kernel#
Un socket non è un dispositivo fisico. È una struttura dati (struct socket) che il kernel crea in memoria quando un processo chiama la system call socket(). Questa struct contiene tutto il necessario per comunicare:
struct socket (semplificata)
┌──────────────────────────────────────┐
│ Protocollo: TCP (o UDP) │
│ IP locale: 0.0.0.0 │
│ Porta locale: 22 │
│ IP remoto: (vuoto se LISTEN) │
│ Porta remota: (vuoto se LISTEN) │
│ Stato: LISTEN / ESTABLISHED │
│ Buffer invio: [dati in uscita] │
│ Buffer ricezione: [dati in arrivo] │
│ File Descriptor: FD 3 │
└──────────────────────────────────────┘Qualsiasi processo può crearne uno: un server web, il demone SSH, nc, uno script Python, il browser (uno per ogni tab), un malware.
La system call è sempre la stessa: socket().
Dopo la creazione, il kernel assegna un File Descriptor (FD) e da quel momento il processo legge e scrive sul socket come se fosse un file normale.
Come funziona#
Il Ciclo di Vita di un Socket (Server-side)#
PROCESSO (es. sshd) KERNEL RETE
│ │ │
1. socket() ──────────────► Crea FD (es. FD 3) │
│ │ │
2. bind(FD 3, IP:22) ────► Aggancia a 0.0.0.0:22 │
│ │ │
3. listen(FD 3) ──────────► Stato: LISTEN │
│ │ │
4. accept() ◄───────────── Nuova connessione! ◄─── │ (client si connette)
│ │ │
Crea FD 4 ◄───────────── Socket dedicato │
│ │ │
5. read(FD 4) / write(FD 4) Scambio dati ◄────────► │
│ │ │
6. close(FD 4) ───────────► Chiude connessione │Punto chiave: Il socket in LISTEN (FD 3) resta aperto per accettare nuovi client. Per ogni client che si connette, il kernel crea un nuovo socket (FD 4, 5, 6...) dedicato a quella conversazione.
Perché "Everything is a File" conta qui#
File Descriptor Table (processo sshd)
┌─────┬──────────────────────────────────┐
│ FD │ Cosa punta │
├─────┼──────────────────────────────────┤
│ 0 │ stdin (standard input) │
│ 1 │ stdout (standard output) │
│ 2 │ stderr (standard error) │
│ 3 │ SOCKET TCP *:22 (LISTEN) │ ◄── il "centralino"
│ 4 │ SOCKET TCP 192.168.1.5:22→ │ ◄── client connesso
│ │ 192.168.1.10:54321 │
│ 5 │ /var/log/auth.log (file aperto) │
└─────┴──────────────────────────────────┘Leggere da un socket (FD 4) usa la stessa system call read() di leggere da un file (FD 5). Scrivere su un socket usa la stessa write(). Il kernel sa internamente che FD 4 è un socket e instrada i dati sulla rete, ma per il processo non fa differenza.
Due Famiglie di Socket#
┌─────────────────────────────────────────────────────────────┐
│ SOCKET │
│ │
│ ┌─────────────────────┐ ┌────────────────────────────┐ │
│ │ NETWORK SOCKET │ │ UNIX DOMAIN SOCKET │ │
│ │ (TCP / UDP) │ │ (IPC locale) │ │
│ │ │ │ │ │
│ │ Identificato da: │ │ Identificato da: │ │
│ │ IP + Porta │ │ Percorso su filesystem │ │
│ │ │ │ │ │
│ │ Es: 0.0.0.0:22 │ │ Es: /var/run/docker.sock │ │
│ │ 127.0.0.1:30000 │ │ /run/systemd/journal/ │ │
│ │ │ │ │ │
│ │ Passa dallo stack │ │ Bypass stack di rete │ │
│ │ TCP/IP del kernel │ │ (più veloce, solo locale) │ │
│ └─────────────────────┘ └────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘Network socket (INET): Usano IP + porta. Possono comunicare tra macchine diverse. Sono quelli che vedi con lsof -i e ss -tlnp.
Esempio: SSH su porta 22, il servizio di Bandit 14 su porta 30000.
Unix domain socket (UNIX): Usano un percorso sul filesystem come indirizzo. Comunicazione solo tra processi sulla stessa macchina. Più veloci perché non attraversano lo stack di rete. Esempio: Docker usa /var/run/docker.sock per ricevere comandi dal client docker.
Presa Esterna vs Presa Interna: il confronto pratico#
NETWORK SOCKET (presa esterna) UNIX DOMAIN SOCKET (presa interna)
───────────────────────────────── ─────────────────────────────────
Indirizzo: IP + Porta Indirizzo: percorso su filesystem
Esce dalla macchina: SÌ Esce dalla macchina: MAI
Visibile con: lsof -i Visibile con: lsof -U
ss -tlnp ss -xl
Esempi: Esempi:
sshd → 0.0.0.0:22 Docker → /var/run/docker.sock
nginx → 0.0.0.0:443 systemd → /run/systemd/journal/stdout
nc → 127.0.0.1:30000 MySQL → /var/run/mysqld/mysqld.sockCaso Concreto: docker.sock#
Quando scrivi docker ps, il client Docker non apre una connessione TCP verso una porta. Scrive sul file /var/run/docker.sock, e il demone Docker legge da quel file. Tutto locale, nessun traffico di rete.
[ docker ps ] [ dockerd ]
(client CLI) (demone)
│ │
│── write() ──► /var/run/docker.sock ◄── read() ──│
│ (Unix Domain Socket) │
│◄── read() ─── /var/run/docker.sock ──► write() ─│
│ │
▼ ▼
Stampa la lista Interroga i container
dei container e restituisce i datiPerché docker.sock è un rischio Blue Team#
Docker usa un Unix socket invece di una porta TCP perché è più veloce e non espone nulla sulla rete. Ma il socket è un file, quindi ha permessi Unix:
$ ls -la /var/run/docker.sock
srw-rw---- 1 root docker 0 Mar 11 06:00 /var/run/docker.sock
# │ │ │
# │ │ └─ Solo il gruppo "docker" può accedere
# │ └─ Proprietario: root
# └─ "s" = socket (tipo file speciale)Se un attaccante ottiene accesso al gruppo docker o i permessi vengono allargati (es. chmod 777), può controllare Docker completamente: creare container privilegiati, montare il filesystem dell'host, e scalare a root. Verificare i permessi su /var/run/docker.sock è hardening base.
Analogia Mondo Reale: La Prolunga Elettrica#
CLIENT (il tuo PC) SERVER (es. web server)
┌──────────────┐ ┌──────────────┐
│ Socket │ PROLUNGA (il cavo) │ Socket │
│ 192.168.1.5 │ ═══════════════════════════════════ │ 93.184.1.10 │
│ :54321 │ ◄── qui dentro passa il traffico ► │ :80 │
│ (presa |_| ) │ │ (presa |_| ) │
└──────────────┘ └──────────────┘
IP + Porta IP + PortaIl socket è la presa (endpoint). Il tool che usi è il cavo (prolunga). Il traffico che passa dentro può essere in chiaro o cifrato, a seconda del cavo scelto:
CAVO (Tool) CIFRATURA ANALOGIA
────────────────────────────────────────────────────────────
nc Nessuna Prolunga senza guaina
telnet Nessuna Prolunga senza guaina (legacy)
ssh / ssh -L AES cifrato Prolunga dentro tubo blindato
openssl s_client TLS cifrato Prolunga dentro tubo blindato
TESTER (Tool) COSA FA ANALOGIA
────────────────────────────────────────────────────────────
ss / netstat Mostra socket Multimetro: misura i cavi
lsof -i Mostra chi usa Multimetro + etichette
tcpdump Intercetta dati Pinza amperometrica sul cavoIl cavo non cambia le prese. Le prese (socket) sono sempre le stesse: IP + porta da un lato, IP + porta dall'altro. Cambia solo cosa passa dentro — byte in chiaro (leggibili da tcpdump) o byte cifrati (illeggibili senza la chiave).
Analogia Mondo Reale: Prese Elettriche e Compatibilità#
Un socket è una presa. Il tool che usi (nc, ssh, telnet) è il programma che crea la presa e stende il cavo in un colpo solo. Ma le prese devono essere compatibili per agganciarsi.
Livello 1 — La forma della presa (Protocollo: TCP vs UDP)#
Server SSH :2222 (TCP) Client
┌─────────────────┐
│ ○ ○ ○ TCP │ ◄────────── nc server 2222 ✅ TCP ↔ TCP
│ (presa Schuko) │ ◄────────── ssh -p 2222 server ✅ TCP ↔ TCP
│ │ ◄────────── telnet server 2222 ✅ TCP ↔ TCP
│ │ ◄── ✗ ──── nc -u server 2222 ❌ UDP ↔ TCP
└─────────────────┘
Presa italiana in Schuko = non entraSe il server ascolta in TCP, il client deve creare un socket TCP. Un socket UDP (flag -u in nc) è come una spina italiana a tre poli in una presa Schuko tedesca: non si aggancia.
Livello 2 — Il voltaggio (Protocollo applicativo)#
Anche se la presa entra (TCP ↔ TCP), il server potrebbe rifiutarti perché non parli la sua lingua:
PRESA COMPATIBILE, MA VOLTAGGIO DIVERSO?
nc server 2222 → Presa entra (TCP ✅)
Ma nc parla raw bytes, SSH aspetta
handshake crittografico → il server
manda il banner e poi chiude.
(utile per banner grabbing!)
ssh -p 2222 server → Presa entra (TCP ✅)
ssh parla il protocollo SSH → handshake,
cifratura, sessione aperta ✅
openssl s_client → Presa entra (TCP ✅)
Parla TLS → si aggancia a servizi
HTTPS/SSL sulla porta 443 ✅È come attaccare un forno (230V) a una presa da lampada (230V): la spina entra, ma il circuito non regge il carico. La forma (protocollo di trasporto) è giusta, il voltaggio (protocollo applicativo) no.
Riepilogo Tool#
TOOL CREA SOCKET + CAVO CIFRATURA PROTOCOLLO APP
──────────────────────────────────────────────────────────────────────
nc TCP (o UDP con -u) Nessuna Raw bytes
telnet TCP Nessuna Raw bytes (legacy)
ssh / ssh -L TCP AES SSH protocol
openssl s_client TCP TLS TLS handshake
TOOL DI ISPEZIONE COSA FA ANALOGIA
──────────────────────────────────────────────────────────────────────
ss / netstat Mostra socket attivi Multimetro
lsof -i Mostra chi usa cosa Multimetro + etichette
tcpdump Intercetta traffico Pinza amperometricaVedere i Socket con lsof#
lsof -i mostra i network-defense socket. Ecco come interpretare l'output:
$ sudo lsof -i -P | grep LISTEN
sshd 1234 root 3u IPv4 12345 0t0 TCP *:22 (LISTEN)
# │ │ │ │ │ │ │ │ │
# │ │ │ │ │ │ │ │ └─ Stato: in attesa
# │ │ │ │ │ │ │ └─ Porta 22
# │ │ │ │ │ │ └─ Protocollo TCP
# │ │ │ │ │ └─ Inode del socket
# │ │ │ │ └─ Tipo: IPv4
# │ │ │ └─ FD 3, modalità u (read/write)
# │ │ └─ Utente root
# │ └─ PID del processo
# └─ Nome del comandoIndagine pratica: chi ascolta su questa macchina?#
# Tutti i socket TCP in stato LISTEN
sudo lsof -i TCP -P | grep LISTEN
# Output tipico su un server con SSH e un web server:
# sshd 1234 root 3u IPv4 12345 TCP *:22 (LISTEN)
# nginx 5678 www 6u IPv4 67890 TCP *:80 (LISTEN)
# nginx 5678 www 7u IPv4 67891 TCP *:443 (LISTEN)
# Socket specifico: chi occupa la porta 30000?
sudo lsof -i :30000 -P
# Tutti i file aperti da un processo sospetto (socket + file normali)
sudo lsof -p 1234Connessione attiva vs LISTEN#
STATO SIGNIFICATO LSOF OUTPUT
─────────────────────────────────────────────────────────────
LISTEN "Sto aspettando connessioni" TCP *:22 (LISTEN)
Il socket è aperto ma nessuno
è collegato.
ESTABLISHED "Sto parlando con qualcuno" TCP 192.168.1.5:22->
Dati fluiscono in entrambe 192.168.1.10:54321
le direzioni. (ESTABLISHED)
CLOSE_WAIT "L'altro ha chiuso, io no ancora" Possibile leak o
processo bloccato.Socket e Servizi Linux: il flusso completo#
[ systemd ]
│
│ legge /lib/systemd/system/ssh.service
│
▼
[ sshd ] (PID 1234)
│
│ socket() → bind(0.0.0.0:22) → listen()
│
▼
[ Socket FD 3: *:22 LISTEN ] ◄── visibile con: lsof -i :22
│
│ un client si connette (nc o ssh)
│
▼
[ Socket FD 4: ESTABLISHED ] ◄── visibile con: lsof -i | grep ESTABLISHED
│
│ scambio dati (read/write)
│
▼
[ journald ] registra l'evento ◄── visibile con: journalctl -u sshQuesto è il motivo per cui lsof e journalctl sono complementari: lsof ti dice cosa sta succedendo adesso (socket aperti, connessioni attive), journalctl ti dice cosa è successo nel tempo (login riusciti/falliti, errori).
Perché è importante per Blue Team#
- Detection: Un socket LISTEN su una porta inaspettata (es. 4444, 8888) è un potenziale indicatore di backdoor.
lsof -i -P | grep LISTENè uno dei primi comandi durante un incident response. - Lateral Movement: Un attaccante che apre un reverse shell crea un socket ESTABLISHED verso il suo server C2.
lsof -i TCP | grep ESTABLISHEDlo rivela. - Unix Socket Abuse: Un attaccante con accesso al file
/var/run/docker.sockpuò controllare Docker e scalare privilegi. Verificare i permessi su questi file è hardening base. - Persistenza: Un servizio malevolo registrato in
systemdapre un socket ad ogni boot.systemctl list-units --type=socketmostra i socket gestiti da systemd.
Scenario Reale#
Durante un incident response, noti traffico anomalo in uscita. Esegui sudo lsof -i TCP -P | grep ESTABLISHED e trovi un processo chiamato update-check con PID 9876 che ha un socket aperto verso 45.33.xx.xx:443. Non riconosci il processo. Indaghi con lsof -p 9876 per vedere tutti i file che ha aperto (binario, librerie, file di configurazione) e con systemctl show update-check -p FragmentPath per scoprire da dove viene caricato. Se il path non è in /lib/systemd/system/, hai probabilmente trovato una backdoor persistente.
Dove l'ho incontrato#
- bandit-14 — Connessione TCP a un socket locale sulla porta 30000 con
nc - progetto-lab-vm — Socket SSH in LISTEN sulla VM Ubuntu
Note personali#
Il concetto "tutto è un file" diventa concreto con i socket: puoi fare
echo "GET / HTTP/1.0" | nc google.com 80e funziona perchéncapre un socket, ci scrive sopra con write() come se fosse un file, e legge la risposta con read(). Unix domain socket li incontrerai di più quando installerai Docker e Wazuh — sono il modo in cui i componenti comunicano sulla stessa macchina senza esporre porte di rete.
Collegato a#
- system — categoria (Hub)
- lsof — comando principale per ispezionare socket aperti
- nc — crea socket client per connettersi a servizi
- systemctl — gestisce servizi che aprono socket
- standard-streams — i socket usano File Descriptor come stdin/stdout/stderr
- inode-anatomy — i socket sono "file" nel senso Unix del termine
- linux-services — i servizi (daemon) comunicano tramite socket
- ssh-protocol — SSH è un servizio che ascolta su un socket TCP


