Network anonymization and privacy techniques. Proxychains, Tor, VPN stacking, I2P, Whonix, MAC spoofing, metadata stripping, browser fingerprint hardening, and censorship circumvention.
Routes TCP connections through a chain of proxy servers, hiding the true source IP from the destination.
# /etc/proxychains4.conf
# Choose ONE chain mode:
dynamic_chain # Skip dead proxies, continue through chain
# strict_chain # All proxies must be up, in exact order
# round_robin_chain # Rotate proxies round-robin per connection
# random_chain # Random order each connection
proxy_dns # CRITICAL - prevents DNS leaks
remote_dns_subnet 224 # Subnet for internal DNS mapping
tcp_read_time_out 15000
tcp_connect_time_out 8000
# Proxy list (bottom of file)
[ProxyList]
socks5 127.0.0.1 9050 # Tor
socks5 10.10.10.1 1080 # Custom SOCKS5
http 192.168.1.100 8080 # HTTP proxy
Usage with common tools:
# Basic usage pattern
proxychains4 <command>
# Nmap (must use TCP connect scan - no SYN/UDP)
proxychains4 nmap -sT -Pn -n --top-ports 100 10.10.10.5
# curl
proxychains4 curl https://check.torproject.org
# Firefox
proxychains4 firefox &
# Metasploit
proxychains4 msfconsole
Chain modes compared:
| Mode | Behavior | Use Case |
|---|---|---|
dynamic_chain |
Skips dead proxies, continues | General use, fault-tolerant |
strict_chain |
All proxies must respond, in order | When exact path matters |
round_robin_chain |
Rotates through proxy list | Load balancing across proxies |
random_chain |
Random proxy per connection | Harder to fingerprint pattern |
Supported proxy types:
| Type | Keyword | Auth Support | Notes |
|---|---|---|---|
| SOCKS4 | socks4 |
No | TCP only, no DNS via proxy |
| SOCKS5 | socks5 |
Yes (user/pass) | TCP + UDP (limited), DNS via proxy |
| HTTP | http |
Yes (Basic/NTLM) | CONNECT method, HTTP tunneling |
Common pitfall: if proxy_dns is not enabled, DNS queries go directly to the system resolver, leaking your real IP to the DNS server. Always verify with tcpdump -i eth0 port 53 while proxychains is active.
The Onion Router - encrypts traffic through 3 relays (guard, middle, exit). Each relay only knows the previous and next hop.
# Install and start Tor
sudo apt install tor
sudo systemctl enable --now tor
# Default SOCKS5 proxy
# 127.0.0.1:9050 (Tor service)
# 127.0.0.1:9150 (Tor Browser)
# Verify Tor is running
ss -tlnp | grep 9050
Proxychains + Tor:
# /etc/proxychains4.conf
[ProxyList]
socks5 127.0.0.1 9050
torsocks for transparent routing:
# Wraps application to route through Tor
torsocks curl https://check.torproject.org
torsocks ssh user@host
torsocks wget http://target.com/file.zip
# Check current Tor exit IP
torsocks curl -s https://api.ipify.org && echo
Circuit rotation:
# Force new Tor circuit (new exit node)
sudo systemctl reload tor
# Or via control port (must enable in torrc)
# /etc/tor/torrc:
# ControlPort 9051
# HashedControlPassword <hash>
# Generate hashed password for torrc
tor --hash-password "mypassword"
# Request new circuit via control port
echo -e 'AUTHENTICATE "mypassword"\r\nSIGNAL NEWNYM\r\nQUIT' \
| nc 127.0.0.1 9051
# Using stem (Python library) for control
pip install stem
python3 -c "
from stem import Signal
from stem.control import Controller
with Controller.from_port(port=9051) as c:
c.authenticate('mypassword')
c.signal(Signal.NEWNYM)
print('New circuit requested')
"
Prevents different applications from sharing the same Tor circuit. Each IsolateSOCKSAuth port gets its own circuit.
# /etc/tor/torrc - Stream isolation ports
SocksPort 9050 # Default (shared circuits)
SocksPort 9052 IsolateDestAddr IsolateDestPort
SocksPort 9053 IsolateSOCKSAuth IsolateClientAddr
SocksPort 9054 IsolateDestAddr
# Each port gets separate circuits
# 9052: new circuit per unique destination
# 9053: new circuit per auth + client
# 9054: new circuit per destination address
Application-specific isolation:
# Browser on port 9050 (default)
# curl on port 9052 (isolated by destination)
curl --socks5-hostname 127.0.0.1:9052 https://target.com
# SSH on port 9053 (isolated by auth)
torsocks -P 9053 ssh user@host
# In proxychains, use different configs:
# proxychains4 -f /etc/proxychains-9052.conf curl ...
Bridges are unlisted Tor relays used to bypass censorship. Pluggable transports disguise Tor traffic.
# /etc/tor/torrc - obfs4 bridge
UseBridges 1
ClientTransportPlugin obfs4 exec /usr/bin/obfs4proxy
Bridge obfs4 IP:PORT FINGERPRINT cert=... iat-mode=0
# Get bridges: https://bridges.torproject.org
# /etc/tor/torrc - Snowflake (looks like WebRTC)
UseBridges 1
ClientTransportPlugin snowflake exec /usr/bin/snowflake-client
Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 \
fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 \
url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ \
fronts=cdn.sstatic.net,www.phpmyadmin.net \
ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478 \
utls-imitate=hellorandomizedalpn
# /etc/tor/torrc - meek-azure (looks like Azure traffic)
UseBridges 1
ClientTransportPlugin meek_lite exec /usr/bin/obfs4proxy
Bridge meek_lite 192.0.2.18:80 BE776A53492E1E044A26F17306E1BC46A55A1625 \
url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
Available transport types:
| Transport | Disguise | Speed | Detection Resistance |
|---|---|---|---|
| obfs4 | Randomized bytes | Good | High |
| Snowflake | WebRTC video call | Moderate | Very high |
| meek | Cloud CDN HTTPS | Slow | Very high |
| webtunnel | HTTPS website | Good | Very high |
Host services accessible only via .onion addresses. Traffic never leaves the Tor network.
# /etc/tor/torrc - Hidden service for C2
HiddenServiceDir /var/lib/tor/c2_service/
HiddenServicePort 443 127.0.0.1:8443
# Multi-port service
HiddenServiceDir /var/lib/tor/multi_service/
HiddenServicePort 80 127.0.0.1:8080
HiddenServicePort 22 127.0.0.1:22
# After restart, .onion address in:
# /var/lib/tor/c2_service/hostname
# Restart Tor, get .onion address
sudo systemctl restart tor
sudo cat /var/lib/tor/c2_service/hostname
OnionShare for anonymous file sharing:
# Install
sudo apt install onionshare-cli
# Share files (creates temporary .onion)
onionshare-cli --receive # Receive files anonymously
onionshare-cli file.zip # Share file via .onion link
onionshare-cli --chat # Anonymous chat room
# Persistent address (keeps same .onion)
onionshare-cli --persistent ~/.onionshare file.zip
Monitor Tor with Nyx (formerly arm):
# Install and run Tor monitor
sudo apt install nyx
nyx # Requires ControlPort enabled in torrc
# Shows bandwidth, circuits, connections in real-time
Encrypts all traffic between client and VPN server. Replaces your visible IP with the server's IP.
WireGuard quick setup:
# Install
sudo apt install wireguard
# Generate keys (server)
wg genkey | tee server_private.key | wg pubkey > server_public.key
# Generate keys (client)
wg genkey | tee client_private.key | wg pubkey > client_public.key
# Generate preshared key (additional encryption layer)
wg genpsk > preshared.key
# /etc/wireguard/wg0.conf (Server)
[Interface]
PrivateKey = <server_private_key>
Address = 10.0.0.1/24
ListenPort = 51820
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = <client_public_key>
PresharedKey = <preshared_key>
AllowedIPs = 10.0.0.2/32
# /etc/wireguard/wg0.conf (Client)
[Interface]
PrivateKey = <client_private_key>
Address = 10.0.0.2/24
DNS = 10.0.0.1
[Peer]
PublicKey = <server_public_key>
PresharedKey = <preshared_key>
Endpoint = <server_ip>:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
# Bring interface up/down
sudo wg-quick up wg0
sudo wg-quick down wg0
sudo wg show
VPN + Tor combinations:
| Setup | Flow | Pros | Cons |
|---|---|---|---|
| VPN then Tor | You > VPN > Tor > Dest | ISP sees VPN only, not Tor usage | VPN provider knows you use Tor |
| Tor then VPN | You > Tor > VPN > Dest | Exit node can't see traffic, VPN sees Tor exit IP | VPN sees traffic content (if not E2E encrypted) |
Kill switch (prevent leaks if VPN drops):
# iptables kill switch - only allow traffic through VPN
VPN_SERVER="<vpn_server_ip>"
VPN_IF="wg0" # or tun0 for OpenVPN
# Flush and set default DROP
sudo iptables -F
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT DROP
# Block ALL IPv6 (prevents IPv6 leaks)
sudo ip6tables -P INPUT DROP
sudo ip6tables -P FORWARD DROP
sudo ip6tables -P OUTPUT DROP
# Allow loopback
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -o lo -j ACCEPT
# Allow VPN establishment
sudo iptables -A OUTPUT -d $VPN_SERVER -j ACCEPT
sudo iptables -A INPUT -s $VPN_SERVER -j ACCEPT
# Allow all traffic through VPN interface
sudo iptables -A OUTPUT -o $VPN_IF -j ACCEPT
sudo iptables -A INPUT -i $VPN_IF -j ACCEPT
# Allow LAN (optional - remove for maximum isolation)
sudo iptables -A OUTPUT -d 192.168.0.0/16 -j ACCEPT
sudo iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT
# Save rules persistently
sudo apt install iptables-persistent
sudo netfilter-persistent save
DNS leak prevention:
# Force DNS through VPN only
echo "nameserver 10.0.0.1" | sudo tee /etc/resolv.conf
# Prevent resolv.conf overwrite by DHCP/NetworkManager
sudo chattr +i /etc/resolv.conf
# Verify: check for leaks
# These should show VPN IP, not real IP
curl -s https://api.ipify.org && echo
dig +short myip.opendns.com @resolver1.opendns.com
# Verify no plaintext DNS escaping
sudo tcpdump -i eth0 port 53 -c 5
# Should capture nothing if DNS goes through VPN
Garlic routing network - bundles multiple messages together. Designed for internal services (eepsites) rather than clearnet access.
# Install I2P
sudo apt install apt-transport-https
wget -q -O - https://geti2p.net/_static/i2p-archive-keyring.gpg \
| sudo tee /usr/share/keyrings/i2p-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/i2p-archive-keyring.gpg] \
https://deb.i2p2.de/ $(lsb_release -sc) main" \
| sudo tee /etc/apt/sources.list.d/i2p.list
sudo apt update && sudo apt install i2p i2p-keyring
# Start I2P router
i2prouter start
# Web console: http://127.0.0.1:7657
# Or as system service
sudo systemctl enable --now i2p
I2P proxy configuration:
# I2P provides several local proxies:
HTTP proxy: 127.0.0.1:4444 (for .i2p sites)
HTTPS proxy: 127.0.0.1:4445
SOCKS proxy: 127.0.0.1:4447 (must enable in console)
IRC: 127.0.0.1:6668
SMTP: 127.0.0.1:7659
POP3: 127.0.0.1:7660
# Access .i2p eepsites
curl --proxy http://127.0.0.1:4444 http://stats.i2p
# Configure browser: set HTTP proxy to 127.0.0.1:4444
# Only use for .i2p domains
Tor vs I2P:
| Feature | Tor | I2P |
|---|---|---|
| Primary use | Clearnet anonymity | Internal network services |
| Routing | Onion (circuit-based) | Garlic (packet-based) |
| Latency | Moderate | Higher |
| Hidden services | .onion | .i2p (eepsites) |
| Outproxy (clearnet) | Built-in (exit nodes) | Limited, unreliable |
| Threat model | Exit node surveillance | Peer enumeration |
Whonix - two-VM architecture for Tor isolation:
| Component | Role |
|---|---|
| Gateway VM | Runs Tor, all traffic forced through it |
| Workstation | Isolated VM, no direct network - routes via Gateway |
Workstation -> Gateway (Tor) -> Internet
No IP leaks possible - Workstation
has no knowledge of real IP or network
Setup: runs on VirtualBox, KVM, or Qubes OS. Pre-built OVA images available at whonix.org.
Tails - amnesic live OS:
When to use which:
| Scenario | Best Choice |
|---|---|
| Daily research from desktop | Whonix |
| Physical engagement, on-site | Tails USB |
| Maximum compartmentalization | Qubes OS + Whonix |
| Quick anonymous browsing | Tor Browser |
| Untrusted hardware | Tails USB (amnesic) |
Qubes OS + Whonix: each activity runs in a separate VM (disposable). Network VM handles Tor. Strongest desktop isolation model.
When Tor itself is blocked or detectable, use additional circumvention tools.
V2Ray / Xray:
# Install V2Ray
bash <(curl -L https://raw.githubusercontent.com/v2fly/fhs-install-v2ray/master/install-release.sh)
# V2Ray with VMess + WebSocket + TLS
# Looks like normal HTTPS traffic to a website
// /usr/local/etc/v2ray/config.json (client)
{
"inbounds": [{
"port": 1080,
"protocol": "socks",
"settings": { "auth": "noauth" }
}],
"outbounds": [{
"protocol": "vmess",
"settings": {
"vnext": [{
"address": "your-server.com",
"port": 443,
"users": [{
"id": "your-uuid-here",
"alterId": 0,
"security": "auto"
}]
}]
},
"streamSettings": {
"network": "ws",
"security": "tls",
"wsSettings": { "path": "/secret-path" },
"tlsSettings": { "serverName": "your-server.com" }
}
}]
}
# Start V2Ray
sudo systemctl enable --now v2ray
# Use via SOCKS5 proxy
curl --socks5-hostname 127.0.0.1:1080 https://check.torproject.org
Other circumvention tools:
| Tool | Technique | Notes |
|---|---|---|
| V2Ray/Xray | VMess/VLESS over WS+TLS | Looks like HTTPS, highly configurable |
| Shadowsocks | AEAD encrypted proxy | Lightweight, widely deployed |
| Psiphon | Multi-protocol (SSH, VPN, HTTP) | Easy to use, auto-selects best method |
| Lantern | P2P + CDN fronting | Good for casual use |
| Hysteria | QUIC-based proxy | Very fast, good for high-latency networks |
| REALITY (Xray) | TLS 1.3 mimicry | Steals TLS fingerprint of real sites |
Makes traffic appear to go to an allowed domain (e.g., CDN) while actually reaching a blocked destination. Relies on the CDN routing based on the Host header rather than the SNI.
# Domain fronting concept:
# TLS SNI: allowed-cdn.com (what censors see)
# HTTP Host: blocked-c2.com (where traffic actually goes)
# Example with curl
curl -H "Host: blocked-c2.com" \
https://allowed-cdn.com/path
# Many CDN providers have restricted this
# Still works with some cloud providers and specific configurations
Note: major CDN providers (CloudFront, Google, Azure) have largely blocked domain fronting. Research current options before relying on this technique.
Changes the hardware MAC address visible on the local network. Important for Wi-Fi engagements to avoid device tracking.
# Install
sudo apt install macchanger
# View current MAC
macchanger -s eth0
ip link show eth0
# Interface MUST be down before changing
sudo ip link set eth0 down
# Set random MAC
sudo macchanger -r eth0
# Set random vendor MAC (looks legitimate)
sudo macchanger -a eth0
# Set specific MAC
sudo macchanger -m XX:XX:XX:XX:XX:XX eth0
# Restore original (permanent) MAC
sudo macchanger -p eth0
# Bring back up
sudo ip link set eth0 up
Persistent MAC change via NetworkManager:
# /etc/NetworkManager/conf.d/mac-randomize.conf
[device]
wifi.scan-rand-mac-address=yes
[connection]
wifi.cloned-mac-address=random
ethernet.cloned-mac-address=random
connection.stable-id=${CONNECTION}/${BOOT}
sudo systemctl restart NetworkManager
Persistent via systemd service (runs before network starts):
# /etc/systemd/system/[email protected]
[Unit]
Description=MAC spoofing for %i
Before=network-pre.target
Wants=network-pre.target
BindsTo=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
[Service]
Type=oneshot
ExecStart=/usr/bin/ip link set dev %i down
ExecStart=/usr/bin/macchanger -r %i
ExecStart=/usr/bin/ip link set dev %i up
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
sudo systemctl enable [email protected]
Websites can identify you without cookies by combining browser characteristics into a unique fingerprint.
Fingerprint vectors:
| Vector | What It Reveals |
|---|---|
| User-Agent | Browser, OS, version |
| Canvas | GPU/rendering engine uniqueness |
| WebGL | Graphics hardware details |
| Fonts | Installed font list |
| Screen | Resolution, color depth |
| Timezone | Geographic region |
| Audio | AudioContext processing fingerprint |
| Navigator | CPU cores, memory, platform |
| TLS/JA3 | TLS client fingerprint (hard to spoof) |
Firefox hardening (about:config):
# Resist fingerprinting (master switch - changes many settings)
privacy.resistFingerprinting = true
# Sets: UTC timezone, standard fonts, rounded screen size,
# generic user-agent, disabled Canvas/WebGL readback
# Disable WebRTC (prevents IP leak via STUN)
media.peerconnection.enabled = false
# Disable WebGL entirely
webgl.disabled = true
# Enhanced Tracking Protection
privacy.trackingprotection.enabled = true
# Total Cookie Protection (dynamic First-Party Isolation)
# Replaces deprecated privacy.firstparty.isolate
network.cookie.cookieBehavior = 5
# Disable telemetry
datareporting.policy.dataSubmissionEnabled = false
datareporting.healthreport.uploadEnabled = false
# Disable Pocket, Normandy, studies
extensions.pocket.enabled = false
app.normandy.enabled = false
app.shield.optoutstudies.enabled = false
# HTTPS-Only Mode
dom.security.https_only_mode = true
Recommended extensions (2026-current):
Tor Browser is pre-hardened: all users share the same fingerprint. Best option when maximum anonymity is needed. Do not install additional extensions in Tor Browser - it makes you more unique.
VM-based browser isolation: run the browser in a disposable VM (Qubes, VirtualBox snapshot). Destroy after use - no persistent state.
Files contain hidden metadata (author name, GPS coordinates, timestamps, software version) that can deanonymize you.
View metadata:
# Install exiftool
sudo apt install libimage-exiftool-perl
# View all metadata
exiftool image.jpg
exiftool document.pdf
exiftool report.docx
# View specific fields
exiftool -GPSLatitude -GPSLongitude image.jpg
exiftool -Author -Creator -Producer document.pdf
# Recursive - check all files in directory
exiftool -r -GPSPosition *.jpg
Remove metadata:
# Strip ALL metadata from image
exiftool -all= image.jpg
# Strip metadata from all JPGs in directory
exiftool -all= -overwrite_original *.jpg
# Strip metadata but keep orientation
exiftool -all= -tagsfromfile @ -Orientation image.jpg
# Strip metadata from all supported types recursively
exiftool -all= -overwrite_original -r ./directory/
mat2 (Metadata Anonymisation Toolkit 2):
# Install
sudo apt install mat2
# Check file metadata
mat2 --show document.pdf
# Clean file (creates cleaned copy with .cleaned suffix)
mat2 document.pdf
# Output: document.cleaned.pdf
# Clean in place
mat2 --inplace document.pdf
# Lightweight mode (faster, less thorough)
mat2 --lightweight document.pdf
# List supported formats
mat2 --list
# Images (JPEG, PNG, TIFF, SVG), PDFs, Office docs
# (DOCX, XLSX, PPTX, ODP, ODS, ODT), audio (FLAC,
# OGG, MP3), video, archives (ZIP, TAR), torrents
PDF metadata cleaning:
# View PDF metadata
exiftool document.pdf
pdfinfo document.pdf
strings document.pdf | grep -i "author\|creator\|producer"
# Remove with exiftool
exiftool -all= document.pdf
# Remove with qpdf (strips and re-linearizes)
qpdf --empty --pages document.pdf -- cleaned.pdf
# Flatten PDF (removes editing history, comments, forms)
gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \
-sOutputFile=clean.pdf document.pdf
OPSEC fail examples:
| Failure | Consequence |
|---|---|
| GPS in photo EXIF | Physical location revealed |
| Author field in DOCX | Real name exposed |
| Software version in PDF | OS/tool identification |
| Unique font in screenshot | System fingerprinting |
| Camera serial in EXIF | Device attribution |
| Printer tracking dots | Specific printer identified |
| Timezone in document XML | Geographic region narrowed |
Fake identities for engagement work. Must be consistent and compartmentalized.
Creation principles:
Email:
ProtonMail or Tuta (formerly Tutanota)
- Register over Tor
- Use sock puppet name
- Never access from real IP
- Consider using a recovery email from another sock puppet
- Enable Tor onion access if available
Phone numbers:
| Service | Notes |
|---|---|
| MySudo | Virtual numbers, multiple lines |
| TextNow | Free US number, works for some verifications |
| Prepaid SIM | Buy with cash, activate away from home |
| Google Voice | Requires existing US number |
| JMP.chat | XMPP-based phone number, pay with crypto |
Social media:
Compartmentalization rules:
Per sock puppet, maintain SEPARATE:
- Browser profile or VM (ideally disposable)
- VPN/Tor circuit (stream isolation)
- Email address
- Phone number
- Writing style and vocabulary
- Login times (avoid patterns matching your timezone)
- Password manager vault
- Payment methods (crypto, prepaid cards)
| Platform | Type | E2E Default | Phone # required | Notes |
|---|---|---|---|---|
| Olvid | Decentralized | Yes | No server trust needed - crypto verification is fully client-side. No phone/email required. ANSSI (French NSA) certified. Theoretically the most secure: even if the server is compromised, messages remain confidential. | |
| Signal | Centralized | Yes | Gold standard for usability, minimal metadata. Phone number = identity linkage. | |
| Session | Decentralized | Yes | No phone number, onion routing via Oxen network. Weaker crypto review than Signal. | |
| Briar | P2P | Yes | Tor-based, no server, works offline via Bluetooth/Wi-Fi. Android only. | |
| SimpleX | P2P | Yes | No user identifiers at all - not even random IDs. Best metadata privacy. | |
| Matrix/Element | Federated | Optional | Self-hostable, cross-platform. Metadata visible to homeserver admin. | |
| Wire | Centralized | Yes | Business-friendly. Swiss jurisdiction. Some metadata concerns. |
Why Olvid stands out: Unlike Signal (which trusts Signal's server for key distribution), Olvid performs mutual authentication and key exchange entirely on-device via QR codes or numeric verification. The server is a "dumb relay" that never sees keys, contact lists, or metadata. Even a fully compromised Olvid server cannot decrypt messages or learn who talks to whom. ANSSI certification (France) validates this architecture.
PGP/GPG for email:
# Generate key pair (use ed25519 for modern, RSA 4096 for compat)
gpg --full-generate-key
# Choose: ECC (Curve 25519) or RSA 4096
# Set expiration (1-2 years recommended)
# Export public key
gpg --armor --export [email protected] > pubkey.asc
# Import someone's public key
gpg --import their_pubkey.asc
# Verify key fingerprint (out of band!)
gpg --fingerprint [email protected]
# Encrypt a message
gpg --encrypt --armor --recipient [email protected] message.txt
# Decrypt
gpg --decrypt message.txt.asc
# Sign a message
gpg --clearsign message.txt
# Verify signature
gpg --verify message.txt.asc
# Encrypt + sign
gpg --encrypt --sign --armor -r [email protected] message.txt
Signal best practices:
- Enable disappearing messages (short timer)
- Verify safety numbers in person
- Use registration lock PIN
- Disable link previews
- Use a dedicated number (not personal)
- Enable screen security (prevents screenshots)
- Use relay calls (hides IP from contact)
Separate devices per engagement: never use the same phone/laptop for two different operations. If one is compromised, the other stays clean.
Burner phones:
Digital dead drops:
# Encrypted paste with expiry
# Use PrivateBin (self-hosted) or onion-based paste sites
# Share via Tor hidden service paste
torsocks curl -X POST \
-d "content=<encrypted_message>&expiry=1hour&burn=true" \
http://paste_onion_address.onion/api
# One-time secret sharing
# https://onetimesecret.com (or self-host)
Steganography - hide data inside images:
# steghide - embed file in JPEG/BMP
sudo apt install steghide
# Embed secret.txt into image.jpg
steghide embed -cf image.jpg -ef secret.txt -p "passphrase"
# Extract hidden data
steghide extract -sf image.jpg -p "passphrase"
# Check if file has hidden data
steghide info image.jpg
# OpenStego - GUI and CLI, supports PNG
sudo apt install openstego
# Embed
openstego embed -mf secret.txt -cf cover.png \
-sf output.png -p "passphrase"
# Extract
openstego extract -sf output.png -p "passphrase"
Using public networks for anonymous operations requires discipline.
Wi-Fi operational security:
Before connecting:
1. Spoof MAC address (macchanger -r wlan0)
2. Disable Bluetooth and NFC
3. Cover or disable webcam
4. Use a privacy screen filter
5. Boot from Tails USB
Location selection:
- Choose busy locations (library, cafe, airport)
- Rotate locations - never use the same one twice
- Ensure no direct camera angle on your screen
- Sit with your back to a wall
- Stay within 30 minutes to limit exposure
After operation:
- Disconnect and disable Wi-Fi
- Do not visit the location with your real devices
- Change MAC address again before next use
Device compartmentalization:
| Device | Purpose | Network |
|---|---|---|
| Daily laptop | Normal activities | Home/work network |
| Op laptop | Engagement work | Public Wi-Fi + Tor |
| Burner phone | Sock puppet accounts | Prepaid data + VPN |
| Tails USB | High-risk operations | Public Wi-Fi + Tor |
Basic rules for anonymous payments:
Privacy coins:
- Monero (XMR): private by default, ring signatures
- Zcash (shielded): optional privacy, zk-SNARKs
Bitcoin privacy (harder):
- Use CoinJoin / Wasabi Wallet
- Never reuse addresses
- Use Tor for all transactions
- Do not link KYC exchanges to anonymous wallets
Payment flow for services:
Cash -> Bitcoin ATM (no KYC) -> Monero swap -> Payment
OR
Cash -> P2P exchange (Bisq) -> Monero -> Payment
For detailed cryptocurrency OPSEC, see the dedicated Crypto OPSEC sheet.
Standard DNS sends queries in plaintext - your ISP (and anyone on the network) can see every domain you resolve. DoH encrypts DNS inside HTTPS.
Firefox DoH configuration:
Settings > Privacy & Security > DNS over HTTPS
- Enable "Max Protection"
- Provider: Cloudflare or Custom
# Or via about:config:
network.trr.mode = 3 # 3 = DoH only (no fallback)
network.trr.uri = https://mozilla.cloudflare-dns.com/dns-query
System-wide DoH with cloudflared:
# Install cloudflared
# Download from: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
sudo dpkg -i cloudflared-linux-amd64.deb
# Run as DNS-over-HTTPS proxy
sudo cloudflared proxy-dns \
--address 127.0.0.1 \
--port 5053 \
--upstream https://1.1.1.1/dns-query \
--upstream https://1.0.0.1/dns-query
# Point system DNS to local proxy
echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf
# Note: if using port 5053, configure local resolver
# to forward to 127.0.0.1:5053
Common DoH providers:
| Provider | Address |
|---|---|
| Cloudflare | https://1.1.1.1/dns-query |
| Quad9 | https://dns.quad9.net/dns-query |
| Mullvad | https://dns.mullvad.net/dns-query |
| NextDNS | https://dns.nextdns.io/<config-id> |
| Wikimedia | https://wikimedia-dns.org/dns-query |
Encrypts DNS queries over TLS on port 853. More transparent than DoH (network admins can see it is DNS traffic, but not the content).
systemd-resolved configuration:
# /etc/systemd/resolved.conf
[Resolve]
DNS=1.1.1.1#cloudflare-dns.com 9.9.9.9#dns.quad9.net
DNSOverTLS=yes
DNSSEC=yes
FallbackDNS=
Domains=~.
# Restart and verify
sudo systemctl restart systemd-resolved
# Check status
resolvectl status
resolvectl query example.com
Stubby client:
# Install
sudo apt install stubby
# Default config: /etc/stubby/stubby.yml
# Pre-configured with Cloudflare and Quad9 over TLS
# /etc/stubby/stubby.yml (key sections)
resolution_type: GETDNS_RESOLUTION_STUB
dns_transport_list:
- GETDNS_TRANSPORT_TLS
listen_addresses:
- 127.0.0.1@53
upstream_recursive_servers:
- address_data: 1.1.1.1
tls_auth_name: "cloudflare-dns.com"
- address_data: 9.9.9.9
tls_auth_name: "dns.quad9.net"
# Start stubby
sudo systemctl enable --now stubby
# Point resolv.conf to stubby
echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf
sudo chattr +i /etc/resolv.conf
# Verify TLS is working
sudo tcpdump -i eth0 port 853 -c 5
# Should see traffic to 1.1.1.1:853 or 9.9.9.9:853
# Verify NO plaintext DNS
sudo tcpdump -i eth0 port 53 -c 5
# Should see NO traffic if DoT is working
Verify DNS privacy:
# Check which DNS server is answering
dig +short whoami.akamai.net
dig +short txt ch whoami.cloudflare @1.1.1.1
# Check for DNS leaks
curl -s https://ipleak.net/json/ | python3 -m json.tool
# tcpdump - confirm no plaintext DNS
sudo tcpdump -i eth0 port 53 -c 10 &
# Generate DNS traffic, then check tcpdump output
dig example.com
# If DoH/DoT is working, tcpdump captures nothing
Use the advisor below to determine the recommended anonymization setup based on your threat model and operational requirements.