Security Handbook 🌐 VI

Chapter 1 — Computer Networking (TCP/IP, OSI)

Overview

Open Wireshark without a working model of each layer and it is just numbers scrolling past: the packet got dropped, but where and why is anyone’s guess. This chapter follows that exact question: how does a byte travel from an application process out onto the wire, and at every hop along the way, where can an attacker step in — by eavesdropping, spoofing, intercepting, or just flooding it.

The chapter opens with the two reference models, OSI and TCP/IP — the vocabulary for naming each layer — then moves to encapsulation/decapsulation, where every layer tacks on a header that both analysis tools and attackers can read. From there it works its way down the stack: Layer 2 with Ethernet, MAC, VLAN, ARP, and switches; Layer 3 with IP, ICMP, routing, and NAT; then Layer 4 with TCP/UDP and service ports — each layer opening up its own slice of attack surface. The last stretch comes back to what you touch every day: application-layer protocols like DNS, DHCP, HTTP, and TLS, plus the defensive and observability side — firewalls, DMZs, and tcpdump/Wireshark.

Each section moves from concept to bit-level mechanics to a runnable example — written for anyone doing Blue Team, AppSec, or DevSecOps who needs to actually read a packet, not just recite its definition.


1.1. Two reference models: the 7-layer OSI and TCP/IP

1.1.1. Why we need a layered model

Moving data across a network involves a lot of moving parts: representing bits on copper/fiber, addressing machines, routing through dozens of routers, ensuring reliability, encryption, and representing application data. Lump it all into a single monolithic block and it becomes nearly impossible to maintain — and impossible to swap out one piece of technology at a time (copper to fiber, IPv4 to IPv6) without breaking everything.

The solution is layering: each layer provides services to the layer above and uses the services of the layer below, through a fixed interface. The core principles:

  • Encapsulation: the upper layer's data is treated by the lower layer as an opaque payload, wrapped with the lower layer's header (and sometimes trailer).
  • Separation of concerns: TCP does not need to know whether the packet travels over Ethernet or Wi-Fi; IP does not need to know whether the payload is TCP or UDP.
  • Interchangeability: you can replace the physical layer without changing the IP layer.

1.1.2. The OSI model (ISO/IEC 7498-1)

Layer Name PDU Core function Example protocols/devices
7 Application Data Interface for applications HTTP, DNS, SMTP, FTP
6 Presentation Data Encoding, compression, data representation TLS, ASCII/UTF-8, JPEG
5 Session Data Establishing/maintaining/terminating sessions RPC, NetBIOS, SOCKS
4 Transport Segment (TCP) / Datagram (UDP) Ports, reliability, flow control TCP, UDP, SCTP, QUIC
3 Network Packet Logical addressing, routing IPv4, IPv6, ICMP, IPsec
2 Data Link Frame Physical addressing (MAC), media access Ethernet, 802.11, ARP, VLAN
1 Physical Bit/Symbol Representing bits as signals Copper, fiber, RF, NIC PHY

Note on PDUs — at Layer 4, TCP is called a segment and UDP a datagram; Layer 3 is a packet; Layer 2 is a frame; Layer 1 is a bit/symbol.

1.1.3. The TCP/IP model (RFC 1122)

TCP/IP is the de facto model of the Internet. RFC 1122 divides it into 4 layers:

TCP/IP layer OSI mapping Representative protocols
Application 7 + 6 + 5 HTTP, DNS, TLS (usually grouped here), SMTP
Transport 4 TCP, UDP
Internet 3 IPv4, IPv6, ICMP, ARP (blurry boundary with L2)
Link / Network Access 2 + 1 Ethernet, Wi-Fi, PPP

Why two models? OSI is a theoretical model (the 1984 specification) that is complete and used for teaching and standardizing terminology. TCP/IP came out of ARPANET, is simpler, and is what actually runs on the Internet. A security engineer must be fluent in both because tools and documentation mix the two conventions (for example "L7 firewall", "L2 attack", "L3/L4 ACL").


1.2. Encapsulation / Decapsulation byte-by-byte

1.2.1. The wrapping process when sending

Suppose a browser sends an HTTP GET / request to a web server on the same LAN over HTTP (without TLS, for simplicity).

Layer 7 (HTTP)  : [ "GET / HTTP/1.1\r\nHost: x\r\n\r\n" ]               <- App data (D)
Layer 4 (TCP)   : [ TCP header 20B | D ]                                <- Segment
Layer 3 (IP)    : [ IPv4 header 20B | TCP header 20B | D ]              <- Packet
Layer 2 (Eth)   : [ Eth header 14B | IPv4 20B | TCP 20B | D | FCS 4B ]  <- Frame
Layer 1 (PHY)   : [ Preamble 7B | SFD 1B | <Frame on the wire> ]        <- Bit stream

Each layer adds only its own header and treats everything received from the layer above as an opaque payload. This is why each header must have a "protocol/next header" field so the receiver knows which protocol the payload belongs to (see EtherType, the IPv4 Protocol field, and TCP ports).

1.2.2. The unwrapping process when receiving (decapsulation)

The receiver does the reverse: each layer reads its own header, checks it (checksum/FCS), strips it off, then pushes the payload up to the layer above based on the "next protocol" field:

PHY  : receive bits -> detect Preamble/SFD to sync clock -> obtain frame
L2   : check FCS (CRC32). EtherType=0x0800 -> push up to IPv4
L3   : check IP checksum, verify Dst IP is ours, Protocol=6 -> TCP
L4   : check TCP checksum, use Dst Port=80 -> push up to the process listening on port 80
L7   : web server parses "GET / HTTP/1.1"

1.2.3. Real-world example: viewing each layer with tcpdump

# -X prints hex+ASCII, -e prints the Ethernet header too, -nn skips name/port resolution
sudo tcpdump -i eth0 -e -nn -X 'tcp port 80 and host 192.0.2.10' -c 1

Sample output (abbreviated and annotated):

14:02:11.123456 aa:bb:cc:11:22:33 > de:ad:be:ef:00:01, ethertype IPv4 (0x0800), length 74:
    10.0.0.5.54321 > 192.0.2.10.80: Flags [S], seq 1001, win 64240,
    options [mss 1460,sackOK,TS val 1 ecr 0,nop,wscale 7], length 0
    0x0000:  dead beef 0001 aabb cc11 2233 0800 4500   <- Eth(14) + IP starts (45=Ver4,IHL5)
    0x0010:  003c 1c46 4000 4006 ...                    <- TotalLen 0x3c=60, Flags+FragOff, TTL=0x40=64, Proto=0x06=TCP
    ...
  • dead beef 0001 = Dst MAC; aabb cc11 2233 = Src MAC; 0800 = EtherType IPv4.
  • 45 = 0100 0101 -> Version 4, IHL 5 (5×4=20 bytes).
  • 40 (at the TTL offset) = TTL 64; 06 = Protocol 6 = TCP.

1.3. Layer 2 — Ethernet, MAC, VLAN, ARP, switch

1.3.1. The Ethernet II frame — field-by-field layout

Ethernet II (DIX) is the most common format in use today (unlike IEEE 802.3, which uses a Length field instead of EtherType).

Field Size Offset (in the frame on the wire) Meaning Example
Preamble 7 bytes 0 7 bytes of 0xAA (10101010…) to sync the clock AA AA AA AA AA AA AA
SFD (Start Frame Delimiter) 1 byte 7 0xAB (10101011), final bit =1 signals the start of the frame AB
Destination MAC 6 bytes 8 Destination MAC de:ad:be:ef:00:01
Source MAC 6 bytes 14 Source MAC aa:bb:cc:11:22:33
EtherType / Length 2 bytes 20 ≥0x0600 = EtherType; <0x0600 = Length (802.3) 0x0800 (IPv4)
Payload 46–1500 bytes 22 Upper-layer data (IP packet) …
FCS (Frame Check Sequence) 4 bytes end CRC-32 over Dst..Payload 0x1c2d3e4f

The Preamble + SFD (8 bytes) are not counted as part of the frame when captured with tcpdump/Wireshark (the NIC strips them first). They are also not part of the MTU.

Common EtherTypes:

EtherType Protocol
0x0800 IPv4
0x0806 ARP
0x86DD IPv6
0x8100 VLAN tag 802.1Q
0x8847 MPLS unicast

MTU and sizes: - Default Ethernet MTU = 1500 bytes (maximum payload). Maximum frame (excluding preamble/SFD) = 14 (header) + 1500 + 4 (FCS) = 1518 bytes. With a VLAN tag, add 4 bytes → 1522. - Minimum payload 46 bytes: if smaller, it must be padded with zeros to reach the minimum. Why? So the minimum frame reaches 64 bytes (to ensure collision detection in old half-duplex Ethernet — the slot time). - Jumbo frame: MTU 9000 (not an IEEE standard but common in datacenters/SANs).

Structure of a MAC address (48 bits):

AA:BB:CC:DD:EE:FF
└── OUI (24 bits) ──┘└── NIC-specific (24 bits) ──┘
First byte (AA): bit 0 (I/G) = 1 -> multicast; =0 -> unicast
                 bit 1 (U/L) = 1 -> locally administered; =0 -> universal (real OUI)
Broadcast = FF:FF:FF:FF:FF:FF

1.3.2. VLAN — IEEE 802.1Q tag (4 bytes)

When you need to logically separate multiple networks over the same physical infrastructure, the switch inserts a 4-byte 802.1Q tag into the frame, immediately after the Source MAC.

[ Dst MAC 6B | Src MAC 6B | 802.1Q TAG 4B | EtherType 2B | Payload | FCS 4B ]
                                   │
                                   ▼
              ┌──────────────────────────────────────────┐
              │ TPID 16b | PCP 3b | DEI 1b | VID 12b       │
              └──────────────────────────────────────────┘
Field Size Meaning Example
TPID (Tag Protocol ID) 16 bits 0x8100 signals this is 802.1Q 0x8100
PCP (Priority Code Point) 3 bits QoS priority (0–7) 5 (voice)
DEI (Drop Eligible Indicator) 1 bit Marks the frame as droppable under congestion 0
VID (VLAN ID) 12 bits VLAN ID (0–4095; 0 and 4095 are reserved) 100
  • A 12-bit VID → up to 4094 usable VLANs. Native VLAN: on a trunk port, the untagged VLAN.
  • Note: VLAN hopping.
  • Double tagging: the attacker attaches 2 tags; the switch strips the outer tag (native VLAN) and forwards across the trunk still carrying the inner tag → the frame jumps to another VLAN. Mitigation: set the native VLAN to a "dead" unused VLAN, or tag the native VLAN as well (vlan dot1q tag native).
  • Switch spoofing: the attacker forges DTP to turn an access port into a trunk. Mitigation: disable DTP (switchport mode access, switchport nonegotiate).

1.3.3. ARP — Address Resolution Protocol (RFC 826)

ARP maps an IP address (L3) to a MAC address (L2) within the same broadcast domain. Before sending an IP packet to a host on the same subnet, a machine must know that host's MAC.

ARP packet layout (28 bytes for IPv4-over-Ethernet), placed in the Ethernet payload with EtherType 0x0806:

Field Size Meaning Example
Hardware Type (HTYPE) 2 bytes L2 type; Ethernet = 1 0x0001
Protocol Type (PTYPE) 2 bytes L3 type; IPv4 = 0x0800 0x0800
Hardware Addr Len (HLEN) 1 byte MAC length = 6 0x06
Protocol Addr Len (PLEN) 1 byte IP length = 4 0x04
Operation (OPER) 2 bytes 1=request, 2=reply 0x0001
Sender Hardware Addr (SHA) 6 bytes Sender's MAC aa:bb:cc:11:22:33
Sender Protocol Addr (SPA) 4 bytes Sender's IP 10.0.0.5
Target Hardware Addr (THA) 6 bytes Target MAC (0 in a request) 00:00:00:00:00:00
Target Protocol Addr (TPA) 4 bytes The IP being queried 10.0.0.1

Request/reply process: 1. Host A (10.0.0.5) wants to send to 10.0.0.1, which is not in its ARP cache. 2. A broadcasts an ARP request: Eth Dst = ff:ff:ff:ff:ff:ff, OPER=1, TPA=10.0.0.1, THA=0. 3. Every host in the broadcast domain receives it; only 10.0.0.1 replies. 4. B unicasts an ARP reply back to A: OPER=2, SHA = B's MAC, SPA=10.0.0.1. 5. A stores the pair 10.0.0.1 → MAC_B in its ARP cache (timeout of tens of seconds to a few minutes).

Real-world example:

ip neigh show                 # View the ARP cache (modern Linux)
# 10.0.0.1 dev eth0 lladdr de:ad:be:ef:00:01 REACHABLE
arping -I eth0 10.0.0.1        # Send an ARP request manually
sudo tcpdump -i eth0 -nn arp  # Capture ARP packets

ARP spoofing/poisoning. Note: ARP has no authentication. The attacker continuously sends fake ARP replies (gratuitous ARP): "the gateway IP 10.0.0.1 has MAC = attacker's MAC". The victim overwrites its cache → all traffic destined for the gateway passes through the attacker's machine (Man-in-the-Middle).

# Demonstration (only in an authorized lab):
sudo arpspoof -i eth0 -t 10.0.0.5 10.0.0.1   # tell .5 that .1 is me
# Combine with enabling ip_forward so traffic is not interrupted:
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward

Defense: Dynamic ARP Inspection (DAI) on the switch (validating ARP against the DHCP snooping binding), static ARP for important gateways, port security, and monitoring mapping changes (arpwatch).

1.3.4. Switch CAM table (MAC address table)

A switch learns MACs by looking at the Source MAC of frames arriving on each port and storing them in the CAM table: MAC → port. When forwarding, it looks up the Dst MAC: - Present in the CAM → send to the correct port (unicast). - Absent (unknown unicast) or broadcast/multicast → flood out all ports except the ingress port.

Security — CAM table overflow / MAC flooding: the attacker pumps thousands of frames with random Source MACs (macof) to fill the CAM table. Once full, the switch floods all traffic like a hub → the attacker can sniff. Mitigation: port security limits the number of MACs per port.

Switch(config-if)# switchport port-security
Switch(config-if)# switchport port-security maximum 2
Switch(config-if)# switchport port-security violation shutdown
Switch(config-if)# switchport port-security mac-address sticky

1.4. Layer 3 — IPv4, IPv6, ICMP, routing, NAT

1.4.1. IPv4 header — 20-byte layout (without Options)

 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  |DSCP   |ECN|         Total Length              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Identification        |Flags|    Fragment Offset      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  TTL          |   Protocol    |        Header Checksum        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Source IP Address                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination IP Address                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                  Options (if IHL>5)             |   Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Field Size Offset (byte) Meaning Example
Version 4 bits 0 IP version = 4 4
IHL (Internet Header Length) 4 bits 0 Header length in units of 4 bytes; min 5 (=20B), max 15 (=60B) 5
DSCP 6 bits 1 Differentiated Services (QoS) 0
ECN 2 bits 1 Explicit Congestion Notification 0
Total Length 16 bits 2 Total length of header+payload (bytes); max 65535 60
Identification 16 bits 4 Datagram ID, used for reassembly 0x1c46
Flags 3 bits 6 bit0=reserved(0), bit1=DF (Don't Fragment), bit2=MF (More Fragments) 010 (DF)
Fragment Offset 13 bits 6 Fragment position (in units of 8 bytes) 0
TTL (Time To Live) 8 bits 8 Remaining hop count; each router −1; =0 means drop + ICMP 64
Protocol 8 bits 9 Payload protocol: 1=ICMP,6=TCP,17=UDP 6
Header Checksum 16 bits 10 Checksum of the header only 0xb1e6
Source IP 32 bits 12 Source IP 10.0.0.5
Destination IP 32 bits 16 Destination IP 192.0.2.10
Options 0–40 bytes 20 Options (record route, timestamp…) —

Why TTL? It prevents a packet from circling forever during a routing loop. traceroute exploits the TTL: it sends packets with TTL=1,2,3…; each router that drops the packet at TTL=0 returns an ICMP Time Exceeded, revealing its IP.

Header Checksum uses the one's complement sum of the 16-bit half-words of the header. Because the TTL changes at every hop, each router must recompute the checksum at every hop (the reason IPv6 dropped this checksum to reduce load).

1.4.2. Fragmentation (IPv4 fragmentation)

When a packet is larger than the MTU of the next link and the DF flag is not set, the router splits it into multiple fragments.

Example: a datagram with a 4000-byte payload over a link with MTU 1500 (maximum IP payload = 1500−20 = 1480, which must be a multiple of 8 → use 1480).

Fragment Data bytes Fragment Offset (units of 8B) MF Total Length
1 0–1479 0 1 1500
2 1480–2959 185 (=1480/8) 1 1500
3 2960–3999 370 (=2960/8) 0 1060

All fragments use the same Identification. The receiver reassembles them by offset. Security: overlapping fragment attacks (Teardrop), tiny fragments bypassing firewalls, fragment-based IDS evasion. Mitigation: have the firewall reassemble fragments before inspecting (virtual reassembly).

1.4.3. IPv6 header — fixed 40-byte layout

 0                   1                   2                   3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| Traffic Class |           Flow Label                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Payload Length        |  Next Header  |   Hop Limit   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                 Source Address (128 bits)                     +
|                      ... (16 bytes) ...                       |
+                                                               +
|                                                               |
+               Destination Address (128 bits)                  +
|                      ... (16 bytes) ...                       |
+                                                               +
Field Size Meaning Example
Version 4 bits = 6 6
Traffic Class 8 bits DSCP+ECN, similar to IPv4 0
Flow Label 20 bits Marks a flow for QoS 0x12345
Payload Length 16 bits Payload length (excluding the 40B header) 1280
Next Header 8 bits Like the IPv4 Protocol field; also points to extension headers 6 (TCP)
Hop Limit 8 bits Like TTL 64
Source Address 128 bits Source IPv6 2001:db8::1
Destination Address 128 bits Destination IPv6 2001:db8::2

Important design differences: a fixed 40B header (no IHL/Options in the main header — uses chained extension headers via Next Header); no checksum (delegated to the L2 FCS and the L4 checksum); routers do not fragment (the host uses Path MTU Discovery instead); minimum MTU of 1280B. IPv6 does not use ARP but uses NDP (Neighbor Discovery Protocol) over ICMPv6.

1.4.4. ICMP (RFC 792)

Placed in the IP payload with Protocol=1. Common 8-byte header:

Field Size Meaning Example
Type 1 byte Message type 8 (Echo Request)
Code 1 byte Sub-code 0
Checksum 2 bytes ICMP checksum 0xf7ff
Rest of Header 4 bytes Depends on Type (Echo: Identifier 2B + Sequence 2B) id=0x1234 seq=1

Important Type/Code values:

Type Code Meaning
0 0 Echo Reply (ping reply)
3 0–15 Destination Unreachable (3=port unreachable, 4=frag needed+DF)
5 0–3 Redirect
8 0 Echo Request (ping)
11 0 Time Exceeded (TTL=0, used for traceroute)
ping -c 3 8.8.8.8
sudo tcpdump -i eth0 -nn icmp
# ICMP type 8 = request; type 0 = reply; type 11 = traceroute hops

Security: ICMP tunneling (smuggling data inside the Echo payload to bypass firewalls — ptunnel), ICMP redirect spoofing, smurf attack (broadcast amplification). Many places rate-limit/filter ICMP but you should NOT block Type 3 Code 4, as that would break PMTUD.

1.4.5. RFC 1918, CIDR, and manual subnetting

RFC 1918 — private IP ranges:

Range CIDR Number of addresses
10.0.0.0 – 10.255.255.255 10.0.0.0/8 16,777,216
172.16.0.0 – 172.31.255.255 172.16.0.0/12 1,048,576
192.168.0.0 – 192.168.255.255 192.168.0.0/16 65,536

Others: 127.0.0.0/8 loopback; 169.254.0.0/16 link-local (APIPA); 100.64.0.0/10 CGNAT (RFC 6598).

CIDR (Classless Inter-Domain Routing): the /n notation = the first n bits are the network portion (netmask). Example: /24 → mask 255.255.255.0, 8 host bits → 2^8 = 256 addresses, 256 − 2 = 254 usable hosts (minus the network address and the broadcast address).

Manual subnetting — a step-by-step example: divide 192.168.1.0/24 into 4 equal subnets.

  1. We need 4 subnets → we need to borrow log2(4) = 2 bits from the host portion. New prefix: /24 + 2 = /26.
  2. The /26 mask = 255.255.255.192 (last byte 11000000 = 192).
  3. Block size (step) = 256 − 192 = 64.
  4. Enumerate:
Subnet Network Usable host range Broadcast
1 192.168.1.0/26 .1 – .62 192.168.1.63
2 192.168.1.64/26 .65 – .126 192.168.1.127
3 192.168.1.128/26 .129 – .190 192.168.1.191
4 192.168.1.192/26 .193 – .254 192.168.1.255

Each subnet: 2^(32−26) = 64 addresses, with 64 − 2 = 62 usable hosts.

Determining whether an IP belongs to a subnet (bitwise AND): IP 192.168.1.130 with the /26 mask:

IP:    11000000.10101000.00000001.10000010
Mask:  11111111.11111111.11111111.11000000
AND:   11000000.10101000.00000001.10000000  = 192.168.1.128  -> Subnet 3
ipcalc 192.168.1.130/26     # quick calculation tool
sipcalc 192.168.1.0/24 -s 26

1.4.6. NAT / PAT / SNAT / DNAT

NAT (Network Address Translation): translates the IP (and port) when a packet passes through the border router. Reason for existence: IPv4 exhaustion; many private hosts share a single public IP.

Type What it translates Used for
Static NAT (1:1) 1 private ↔ 1 fixed public Servers needing a stable public address
Dynamic NAT private ↔ public from a pool Many hosts, a public pool
PAT / NAPT / Overload many private → 1 public, distinguished by port The most common case at home/office
SNAT (Source NAT) changes the Source IP (outbound) LAN → Internet
DNAT (Destination NAT) changes the Dst IP (inbound) Port forwarding to an internal server

PAT translation table:

Inside Local Inside Global Outside Protocol
10.0.0.5:54321 203.0.113.10:40001 192.0.2.10:80 TCP
10.0.0.6:51000 203.0.113.10:40002 1.1.1.1:443 TCP

The router replaces the Source 10.0.0.5:54321 → 203.0.113.10:40001 on the way out, and does the reverse for the return packet based on port 40001.

Real-world example — iptables/nftables:

# SNAT/MASQUERADE: LAN 10.0.0.0/24 going out via eth0
sudo iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

# DNAT: forward public:8080 -> internal server 10.0.0.50:80
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 \
  -j DNAT --to-destination 10.0.0.50:80
sudo iptables -A FORWARD -p tcp -d 10.0.0.50 --dport 80 -j ACCEPT

# View the active NAT table
sudo conntrack -L

Security: NAT is not a firewall (a common misconception). NAT slipstreaming, hairpin NAT, and exposing ports to the Internet via DNAT are a large attack surface.

1.4.7. Basic routing

A router decides the next-hop using longest prefix match in the routing table.

ip route show
# default via 10.0.0.1 dev eth0
# 10.0.0.0/24 dev eth0 proto kernel scope link src 10.0.0.5
ip route get 8.8.8.8     # see which route a packet will take

The default route 0.0.0.0/0 is "match every destination" with the shortest prefix (used only when there is no more specific route).


1.5. Layer 4 — TCP and UDP

1.5.1. UDP header — 8-byte layout (RFC 768)

Field Size Offset Meaning Example
Source Port 16 bits 0 Source port 54321
Destination Port 16 bits 2 Destination port 53
Length 16 bits 4 Length of header+data (≥8) 40
Checksum 16 bits 6 Checksum (optional in IPv4, mandatory in IPv6) 0x1a2b

UDP has no handshake, no guarantee of ordering/reliability, and no flow control. Used for DNS, DHCP, VoIP, QUIC. The UDP/TCP checksum is computed over a pseudo-header consisting of the Src IP, Dst IP, Protocol, and Length from the IP layer — to detect packets delivered to the wrong address.

1.5.2. TCP header — 20-byte layout (without Options), RFC 9293

 0                   1                   2                   3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| DOff  |Rsv|N|C|E|U|A|P|R|S|F|         Window Size              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |        Urgent Pointer         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options (if DOff>5)          |   Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Field Size Offset Meaning Example
Source Port 16 bits 0 Source port 54321
Destination Port 16 bits 2 Destination port 443
Sequence Number 32 bits 4 Sequence number of the first byte of the segment (or the ISN when SYN) 1001
Acknowledgment Number 32 bits 8 Next byte expected to receive (valid when ACK=1) 2001
Data Offset (DOff) 4 bits 12 TCP header length in units of 4 bytes (min 5=20B) 5
Reserved 3 bits 12 =0 0
Flags (9 bits) 9 bits 12–13 See below —
Window Size 16 bits 14 Number of bytes in the receive window 64240
Checksum 16 bits 16 Checksum (including the pseudo-header) 0x4a8c
Urgent Pointer 16 bits 18 Offset of urgent data (when URG=1) 0

The 9 TCP flags (from high bit to low bit):

Flag Name Meaning
NS Nonce Sum ECN nonce (RFC 3540, rarely used)
CWR Congestion Window Reduced Signals that cwnd was reduced due to congestion
ECE ECN-Echo Signals that a congestion indication was received
URG Urgent The Urgent Pointer is valid
ACK Acknowledgment The Ack Number is valid
PSH Push Push data up to the app immediately, do not buffer
RST Reset Abruptly tear down the connection
SYN Synchronize Initiate a connection, synchronize the seq
FIN Finish End data transmission in one direction

Common TCP Options (in the Options region):

Option Kind Length Meaning
End of Option List 0 1B End of the options
No-Operation (NOP) 1 1B Padding to align to 4 bytes
MSS (Maximum Segment Size) 2 4B Maximum segment size, usually 1460 (1500−40)
Window Scale 3 3B Window scaling factor (left shift), up to ×2^14
SACK Permitted 4 2B Permits Selective ACK
SACK 5 variable Reports discontiguous received blocks
Timestamps 8 10B TSval/TSecr to measure RTT, protect against wrap (PAWS)

1.5.3. The 3-way handshake — seq/ack values at each step

Client (10.0.0.5)                                  Server (1.2.3.4:443)
        |                                                  |
        |  1. SYN  seq=x (ISN_c=1000), ack=0               |
        |------------------------------------------------->|   Flags=[S]
        |                                                  |
        |  2. SYN-ACK  seq=y (ISN_s=5000), ack=x+1=1001    |
        |<-------------------------------------------------|   Flags=[S.]
        |                                                  |
        |  3. ACK  seq=x+1=1001, ack=y+1=5001              |
        |------------------------------------------------->|   Flags=[.]
        |                                                  |
   (connection ESTABLISHED, data begins at seq=1001/5001)

Detailed explanation: 1. The client chooses a random ISN (Initial Sequence Number) (=1000) and sends a SYN with seq=1000. A SYN consumes 1 sequence number (even though it carries no data). 2. The server chooses its own ISN (=5000) and sends a SYN-ACK: seq=5000, ack=1001 (acknowledging receipt of seq 1000, expecting the next byte to be 1001). 3. The client sends an ACK: seq=1001, ack=5001. The connection becomes ESTABLISHED.

Why a random ISN? To defend against the TCP sequence prediction attack (an outsider guessing the seq to inject/spoof). RFC 6528 requires the ISN to be a secret hash function.

Security — SYN flood: the attacker sends a burst of SYNs with spoofed Source IPs and never sends the step-3 ACK. The server holds many half-open connections in its backlog queue → resource exhaustion. Mitigation: SYN cookies (the server encodes the state into the ISN and does not store anything in the queue until it receives a valid ACK).

sysctl net.ipv4.tcp_syncookies=1
sysctl net.ipv4.tcp_max_syn_backlog=4096

1.5.4. The 4-way connection close

Client                                Server
  |  FIN seq=u                          |   (Client has no more data to send)
  |------------------------------------>|
  |  ACK ack=u+1                        |
  |<------------------------------------|
  |              ... (server may still send data) ...
  |  FIN seq=v                          |
  |<------------------------------------|
  |  ACK ack=v+1                        |
  |------------------------------------>|
  |  (Client enters TIME_WAIT ~2*MSL)   |

TCP is full-duplex, so each direction closes independently (half-close). TIME_WAIT (2×MSL, with MSL usually 30–120s depending on the OS) ensures: (1) the final ACK reaches the server, and (2) old delayed segments in the network expire before the port pair is reused. Too many TIME_WAIT entries on a heavily loaded server is a real-world problem (tune tcp_tw_reuse).

1.5.5. The complete TCP state machine (RFC 9293)

                              CLOSED
                                | (passive open: LISTEN) | (active open: send SYN)
                                v                         v
                             LISTEN                  SYN_SENT
                          recv SYN |                   | recv SYN-ACK / send ACK
                          send SYN-ACK                 v
                                v                  ESTABLISHED
                           SYN_RCVD --recv ACK------>  |
                                                       | (close: send FIN)
                        ┌──────────────────────────────┤
       recv FIN/send ACK│                              │ send FIN
                        v                              v
                   CLOSE_WAIT                       FIN_WAIT_1
                        | app close/send FIN     recv ACK | recv FIN+ACK
                        v                              v        \
                   LAST_ACK                       FIN_WAIT_2     CLOSING
                        | recv ACK                  | recv FIN       | recv ACK
                        v                           v send ACK       v
                     CLOSED                     TIME_WAIT  <----------
                                                    | (2*MSL timeout)
                                                    v
                                                 CLOSED
State Meaning
LISTEN Server waiting for connections
SYN_SENT Client has sent the SYN
SYN_RCVD Received the SYN, sent the SYN-ACK
ESTABLISHED Active connection, transferring data
FIN_WAIT_1/2 The active-close side has sent its FIN
CLOSE_WAIT The passive side received the FIN, the app has not closed yet
LAST_ACK The passive side has sent its FIN, waiting for the final ACK
TIME_WAIT Waiting 2×MSL before fully closing
ss -tan          # view TCP states (ESTAB, TIME-WAIT, CLOSE-WAIT...)
ss -tanp state syn-recv   # detect a SYN flood

Port-scanning security (nmap) relies on the state machine: - SYN scan (-sS): send a SYN; receiving a SYN-ACK → open (then send an RST to abort); receiving an RST → closed. - NULL/FIN/Xmas scan: send unusual flag combinations; per the RFC, a closed port replies with RST while an open port stays silent → infer the state (bypassing some stateless firewalls).

1.5.6. Sliding window, flow control, congestion control

Flow control: the receiver advertises a Window Size (combined with Window Scale) = the number of bytes still free in its buffer. The sender does not send more than the Window. If the buffer is full → it advertises window=0 → the sender stops and periodically sends a window probe.

Sliding window: the byte stream is divided into 4 regions: (1) sent & acked, (2) sent but not yet acked, (3) not yet sent but within the window, (4) outside the window. When an ack arrives, the "window" slides to the right.

Congestion control — the classic algorithms: - Slow start: cwnd starts at 1–10 MSS and doubles every RTT until ssthresh. - Congestion avoidance: after ssthresh, it grows linearly (+1 MSS/RTT). - Fast retransmit: receiving 3 duplicate ACKs → retransmit immediately without waiting for a timeout. - Fast recovery: halve the cwnd instead of dropping back to 1. - Modern: CUBIC (the Linux default), BBR (Google, measures bandwidth×RTT).

sysctl net.ipv4.tcp_congestion_control   # view the algorithm
ss -ti                                    # view cwnd, rtt, retrans per-socket

Retransmission: based on the RTO (Retransmission Timeout) computed from the measured RTT (Jacobson/Karels: RTO = SRTT + 4*RTTVAR). When the RTO expires without an ack → retransmit, and the RTO doubles (exponential backoff).


1.6. Common ports

Port Protocol Service Security notes
20/21 TCP FTP data/control Plaintext, use FTPS/SFTP
22 TCP SSH Encrypted remote administration
23 TCP Telnet Plaintext — do not use
25 TCP SMTP Mail, check for open relay
53 TCP/UDP DNS UDP for queries, TCP for zone transfers/large responses
67/68 UDP DHCP server/client DORA
80 TCP HTTP Plaintext
110/143 TCP POP3/IMAP Mail client
123 UDP NTP Amplification DDoS
161/162 UDP SNMP Default community strings are dangerous
389/636 TCP LDAP/LDAPS Directory
443 TCP/UDP HTTPS / QUIC(UDP) TLS
445 TCP SMB EternalBlue, do not expose to the Internet
3306 TCP MySQL DB should not be public
3389 TCP RDP Brute-force, BlueKeep
5432 TCP PostgreSQL DB
6379 TCP Redis No auth by default — dangerous

Port ranges: 0–1023 well-known (requires root privileges to bind on Linux), 1024–49151 registered, 49152–65535 ephemeral/dynamic (assigned by the client).


1.7. Layer 7 — DNS, DHCP, HTTP, TLS

1.7.1. DNS — Domain Name System (RFC 1035)

DNS message header — 12 bytes:

Field Size Meaning Example
Transaction ID 16 bits Matches query/response 0x1a2b
Flags 16 bits QR(1b),Opcode(4b),AA,TC,RD,RA,Z,RCODE(4b) see below
QDCOUNT 16 bits Number of questions 1
ANCOUNT 16 bits Number of answer records 2
NSCOUNT 16 bits Number of authority records 0
ARCOUNT 16 bits Number of additional records 0

The Flags field (16 bits) in detail:

Bit Name Meaning
QR (1) Query/Response 0=query, 1=response
Opcode (4) 0=standard query
AA (1) Authoritative Answer the server is authoritative
TC (1) Truncated the response was truncated (switch to TCP)
RD (1) Recursion Desired the client wants recursion
RA (1) Recursion Available the server supports recursion
Z (3) reserved/AD/CD in DNSSEC: AD=Authenticated Data, CD=Checking Disabled
RCODE (4) Response code 0=NOERROR,2=SERVFAIL,3=NXDOMAIN

Question section: QNAME (a string of labels in the form [len][label]...0, e.g. 3www7example3com0), QTYPE (2B, A=1, AAAA=28, MX=15, CNAME=5, NS=2, TXT=16, SOA=6, PTR=12), QCLASS (2B, IN=1).

Resource Record (answer): NAME (usually a 2B compression pointer 0xc00c), TYPE (2B), CLASS (2B), TTL (4B), RDLENGTH (2B), RDATA (variable — 4 bytes of IPv4 for an A record).

Recursive vs iterative: - Recursive resolver (8.8.8.8): the client asks once, and the resolver does the lookups on its behalf and returns the final result. - Iterative: the resolver queries root → TLD (.com) → authoritative, receiving a referral to the next server at each step.

dig +trace example.com A      # view the entire iterative chain from root
dig @1.1.1.1 example.com MX
dig -x 192.0.2.10          # reverse (PTR)
sudo tcpdump -i eth0 -nn 'udp port 53'

Sample dig output (annotated):

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6789
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
example.com.    3600   IN   A   192.0.2.10

DNSSEC: adds the RRSIG record (signature), DNSKEY (public key), DS (delegation signer), and NSEC/NSEC3 (proof of non-existence). The AD bit (Authenticated Data) in the flags indicates the resolver has validated the signature. Goal: defend against cache poisoning/spoofing with digital signatures (it does not encrypt the content).

Security — DNS tunneling: encodes exfiltration data into the QNAME (base64data.attacker.com) or TXT records → bypasses firewalls because DNS is rarely blocked. Detection: unusually long QNAMEs with high entropy, a large volume of TXT queries.

Cache poisoning (Kaminsky): guessing the Transaction ID (16 bits) + source port to inject a fake answer before the real one. Mitigation: random source ports (more entropy), DNSSEC, DNS-over-TLS/HTTPS.

1.7.2. DHCP — DORA (RFC 2131)

The four-step dynamic IP allocation (UDP, server 67, client 68):

Client (0.0.0.0)                         DHCP Server
   | 1. DISCOVER (broadcast ff:ff..)        |   "Are there any servers out there?"
   |--------------------------------------->|
   | 2. OFFER (proposes IP, lease, gateway) |
   |<---------------------------------------|
   | 3. REQUEST (request the offered IP)     |
   |--------------------------------------->|
   | 4. ACK (confirm, finalize the lease)    |
   |<---------------------------------------|
Step Type Src IP Dst IP Purpose
D DISCOVER 0.0.0.0 255.255.255.255 Client looks for a server (no IP yet)
O OFFER server IP broadcast/unicast Server proposes an IP
R REQUEST 0.0.0.0 255.255.255.255 Client accepts (broadcast so other servers know)
A ACK server IP client Confirm, grant the lease + options (DNS, gateway, mask)

Important options (DHCP options in the BOOTP packet): Option 53 (message type), 51 (lease time), 1 (subnet mask), 3 (router/gateway), 6 (DNS), 50 (requested IP).

sudo dhclient -v eth0
sudo tcpdump -i eth0 -nn 'udp port 67 or udp port 68'

Security: a rogue DHCP server hands out a fake gateway/DNS → MITM. DHCP starvation (yersinia) floods DISCOVERs to exhaust the pool. Mitigation: DHCP snooping on the switch (only trust the port connected to the legitimate server).

1.7.3. HTTP — raw request/response

Request (HTTP/1.1):

GET /index.html HTTP/1.1\r\n
Host: example.com\r\n
User-Agent: curl/8.0\r\n
Accept: */*\r\n
\r\n

Structure: Method SP Request-URI SP HTTP-Version CRLF, then the headers Name: Value CRLF, an empty line CRLF, then the body (if present). CRLF = \r\n (0x0D 0x0A).

Response:

HTTP/1.1 200 OK\r\n
Content-Type: text/html; charset=UTF-8\r\n
Content-Length: 138\r\n
Connection: keep-alive\r\n
\r\n
<html>...</html>

Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase. Codes: 1xx info, 2xx success, 3xx redirect, 4xx client error, 5xx server error.

# Send a raw request, hiding none of the details:
printf 'GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n' \
  | ncat example.com 80
curl -v http://example.com/      # -v prints both request and response headers

Security: security headers — Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options: nosniff. Attacks: request smuggling (a conflict between Content-Length and Transfer-Encoding), host header injection.

1.7.4. TLS — 1.2 and 1.3

The cryptographic foundations behind TLS/PKI (key exchange, AEAD, X.509 certificates) are covered in Chapter 4 — Cryptography & Security Foundations.

TLS Record layer — 5-byte header (wraps every TLS message):

Field Size Meaning Example
Content Type 1 byte 20=ChangeCipherSpec,21=Alert,22=Handshake,23=Application Data 22
Version 2 bytes 0x0303=TLS1.2 (1.3 pretends to be 1.2 at the record layer for compatibility) 0x0303
Length 2 bytes Payload length (≤16384) 0x0200

TLS 1.2 handshake — message by message:

Client                                       Server
  | 1. ClientHello (random_c, cipher list, SNI, sessionID)
  |------------------------------------------------->|
  | 2. ServerHello (random_s, cipher chosen)         |
  |    Certificate (X.509 chain)                     |
  |    ServerKeyExchange (ECDHE params)              |
  |    ServerHelloDone                               |
  |<-------------------------------------------------|
  | 3. ClientKeyExchange (ECDHE public)              |
  |    ChangeCipherSpec                              |
  |    Finished (encrypted)                          |
  |------------------------------------------------->|
  | 4. ChangeCipherSpec                              |
  |    Finished (encrypted)                          |
  |<-------------------------------------------------|
  (2 RTT before sending application data)

TLS 1.3 handshake — reduced to 1 RTT (RFC 8446):

Client                                       Server
  | ClientHello (+ key_share, supported_versions, SNI)
  |------------------------------------------------->|
  |          ServerHello (+ key_share)               |
  |          {EncryptedExtensions}                   |
  |          {Certificate}{CertificateVerify}        |
  |          {Finished}                              |
  |<-------------------------------------------------|
  | {Finished}                                       |
  |------------------------------------------------->|
  (1 RTT; 0-RTT if using PSK/resumption)

Differences in TLS 1.3: weak ciphersuites removed (static RSA key exchange, RC4, CBC, SHA-1), mandatory forward secrecy (ECDHE), most of the handshake encrypted (the Certificate is encrypted), and only AEAD ciphersuites remain (e.g. TLS_AES_128_GCM_SHA256).

X.509 certificate — the main fields:

Field Meaning Example
Version v3 2 (=v3)
Serial Number a unique identifier from the CA 0x0a1b...
Signature Algorithm the signing algorithm sha256WithRSAEncryption
Issuer the DN of the issuing CA CN=R11, O=Let's Encrypt
Validity notBefore / notAfter 2026-01-01 .. 2026-04-01
Subject the subject's DN CN=example.com
Subject Public Key Info the algorithm + public key RSA 2048 / EC P-256
Extensions SAN, Key Usage, EKU, Basic Constraints, AIA, CRL/OCSP SAN: example.com, www.example.com
Signature the CA's signature over the TBS …
# View the real handshake and certificate:
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null
echo | openssl s_client -connect example.com:443 2>/dev/null \
  | openssl x509 -noout -text -dates -subject -issuer -ext subjectAltName

# Wireshark filters for the handshake:
#   tls.handshake.type == 1   (ClientHello)
#   tls.handshake.type == 2   (ServerHello)
#   tls.handshake.extensions_server_name == "example.com"

Security: SNI (Server Name Indication) leaks the domain name despite encryption → ECH (Encrypted Client Hello) addresses this. Verify the chain of trust, revocation (OCSP stapling), cipher downgrade, Heartbleed (CVE-2014-0160 — reading memory via the heartbeat).


1.8. Firewalls, DMZ, and packet analysis tools

1.8.1. Stateless vs stateful firewalls

Criterion Stateless (packet filter) Stateful
Decision based on Each packet individually (IP/port/flags) Connection state (conntrack)
Allowing return packets Must open rules for both directions manually Automatic (RELATED, ESTABLISHED)
Resistance to flag spoofing Weak Good (only accepts packets consistent with state)
Memory load Low High (maintains a connection table)

Stateful example with iptables:

# Default DROP, allow only inbound SSH and all established outbound
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
iptables -P OUTPUT ACCEPT

-m conntrack --ctstate ESTABLISHED,RELATED is precisely the "stateful" point: the kernel tracks connections, so return packets (e.g. an SSH response) are automatically accepted without needing a separate rule.

1.8.2. DMZ (Demilitarized Zone)

A DMZ is a buffer network zone between the Internet and the internal LAN, containing servers that need to be reachable from outside (web, mail). The two-firewall architecture:

Internet --[outer FW]-- DMZ (web, mail) --[inner FW]-- internal LAN

Principle: Internet → DMZ is permitted (specific ports); DMZ → LAN is restricted as much as possible; if a DMZ server is compromised, the attacker still finds it hard to pivot into the LAN.

1.8.3. tcpdump / Wireshark — practical filters

tcpdump (BPF filter — capture filter):

# Capture SYN-only (detect scans): SYN flag set, ACK off
sudo tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn'

# Capture HTTP GET (first payload bytes = 'G','E','T',' ')
sudo tcpdump -i eth0 'tcp port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'

# DNS query to a domain, write to a pcap file to open in Wireshark
sudo tcpdump -i eth0 -nn -w /tmp/dns.pcap 'udp port 53'

# One host, no name resolution, print hex/ascii payload, 100 packets
sudo tcpdump -i eth0 -nn -X host 10.0.0.50 -c 100

Parameters: -i interface, -nn no name/port resolution, -e print the L2 header, -X hex+ascii, -w write pcap, -c packet count, -s 0 capture the full packet.

Wireshark (display filter — different syntax from BPF):

ip.addr == 10.0.0.50
tcp.flags.syn == 1 && tcp.flags.ack == 0      # SYN scan
tcp.analysis.retransmission                   # detect packet loss
http.request.method == "POST"
dns.qry.name contains "example"
tls.handshake.type == 1                       # ClientHello
tcp.port == 443 && tls.record.content_type == 23   # encrypted app data
ip.ttl < 5                                    # traceroute / unusually low hop

A typical investigation workflow: capture with tcpdump on a headless server (-w file.pcap), transfer it to an analysis machine, open Wireshark, use "Follow TCP Stream" to reassemble the session, and "Statistics → Conversations" to see the top talkers.


1.9. Key numbers to memorize

Item Value
Ethernet header / FCS / MTU / max frame 14B / 4B / 1500B / 1518B (1522 with VLAN)
Ethernet payload min/max 46B / 1500B
802.1Q tag 4B (TPID 16b + PCP 3b + DEI 1b + VID 12b)
ARP packet (IPv4) 28B
IPv4 header min/max 20B / 60B
IPv6 header 40B fixed, min MTU 1280B
TCP header min / UDP header 20B / 8B
ICMP header 8B
DNS header 12B
TLS record header 5B
IPv4 Total Length max 65535B
Port ranges 0–1023 / 1024–49151 / 49152–65535

The RFC numbers above are stable standards. For implementation-dependent details (MSL, backlog size, the default congestion algorithm), verify them on the specific system using sysctl/ss, as the values vary by OS and kernel version.


My notes

Personal notes: points I previously misunderstood, areas I'm still exploring, or lessons from hands-on practice — updated over time.

  • Reading the access log is the fastest way to spot a scanner. Just skimming the access.log of any internet-facing web box paints the portrait of an automated scanner: hundreds of requests over a few minutes from one IP, paths all like /.env, /.git/config, /wp-login.php, /.ssh/id_rsa, plus path-traversal variants ../, ....//, and even %252e%252e (double-encoding — it encodes the . twice to dodge naive filters). The user-agent rotates constantly, occasionally faking a Bing/Google referer to look like a legit bot. Real human traffic almost never has this shape.
  • Look at the status code to know "breached or not". A practical trick I use: filter the log for the suspect IP's requests that the server answered with 2xx — if it's all 301/400/404, it was scanned but got nothing; a 200 on a sensitive path is when to raise the alarm. A high request count by itself isn't an incident; the outcome is.
  • Something I used to get wrong: I once thought blocking the ../ pattern at the reverse proxy was enough to stop traversal. In reality nginx normalizes the URI before matching a location, so blocking on the ..//%2e%2e string is unreliable; blocking by sensitive filename (\.env, id_rsa, \.git) catches most scans, while thorough traversal defense needs a WAF (ModSecurity + OWASP CRS). I keep the operational details of this in the infrastructure/hardening chapter.