Scapy

Framework de manipulation de paquets interactif basé sur Python. Forgez, envoyez, sniffez et décodez des paquets à n'importe quelle couche protocolaire. Idéal pour les attaques sur mesure et le fuzzing de protocoles. Python-based interactive packet manipulation framework. Craft, send, sniff, and dissect packets at any protocol layer. Ideal for custom attacks and protocol fuzzing.

↗ https://scapy.net

Overview

Scapy is an interactive Python packet manipulation library. It lets you forge or decode packets at any layer (Ethernet, IP, TCP, DNS, etc.), send them, capture responses, and script complex network interactions. Ideal for building custom tools, PoCs, and protocol research.

Starting Scapy

Interactive mode

sudo scapy

Or import in Python script

from scapy.all import *

Packet Crafting

# Build a simple TCP SYN packet
pkt = IP(dst="10.10.10.1") / TCP(dport=80, flags="S")

# ICMP ping
pkt = IP(dst="10.10.10.1") / ICMP()

# UDP DNS query
pkt = IP(dst="8.8.8.8") / UDP(dport=53) / DNS(rd=1, qd=DNSQR(qname="target.com"))

# Custom Ethernet frame
pkt = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst="10.10.10.1")

# View packet structure
pkt.show()
pkt.show2()  # Computed fields

# Dump as hex
hexdump(pkt)

Sending Packets

# Layer 3 send (no response handling)
send(IP(dst="10.10.10.1") / ICMP())

# Layer 2 send
sendp(Ether() / IP(dst="10.10.10.1") / ICMP(), iface="eth0")

# Send and receive (Layer 3) — returns answered, unanswered
ans, unans = sr(IP(dst="10.10.10.1") / TCP(dport=80, flags="S"), timeout=2)

# Send and receive one packet
ans = sr1(IP(dst="8.8.8.8") / ICMP(), timeout=2)

# Layer 2 send/receive
ans, unans = srp(Ether() / IP(dst="10.10.10.1") / ICMP(), iface="eth0", timeout=2)

Sniffing

# Sniff 10 packets
pkts = sniff(count=10)

# Sniff with filter
pkts = sniff(filter="tcp port 80", count=20, iface="eth0")

# Sniff with callback
sniff(prn=lambda x: x.summary(), filter="icmp")

# Sniff and save to pcap
sniff(filter="tcp port 80", count=100, iface="eth0") \
    .nsummary()
wrpcap("capture.pcap", pkts)

# Read pcap
pkts = rdpcap("capture.pcap")

Common Use Cases

# ARP Scan (host discovery)
ans, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst="10.10.10.0/24"),
             timeout=2, iface="eth0")
for sent, recv in ans:
    print(recv.sprintf("%ARP.psrc% - %Ether.src%"))

# TCP SYN Scan
ans, unans = sr(IP(dst="10.10.10.1") / TCP(dport=[22,80,443,445], flags="S"),
                timeout=2)
for sent, recv in ans:
    if recv.haslayer(TCP) and recv[TCP].flags == 0x12:  # SYN-ACK
        print(f"Port {recv[TCP].sport} open")

# DNS Query
resp = sr1(IP(dst="8.8.8.8") / UDP(dport=53) /
           DNS(rd=1, qd=DNSQR(qname="target.com")), timeout=2)
print(resp[DNS].summary())

# ARP Spoofing (MITM)
def arp_spoof(target_ip, spoof_ip, iface="eth0"):
    target_mac = getmacbyip(target_ip)
    pkt = ARP(op=2, pdst=target_ip, hwdst=target_mac, psrc=spoof_ip)
    send(pkt, verbose=False)

Fuzzing

# Fuzz TCP flags
for flag in ["S", "SA", "F", "R", "P", "U", "A"]:
    sr1(IP(dst="10.10.10.1") / TCP(dport=80, flags=flag), timeout=1)

# Fuzz payload length
for size in range(0, 1000, 100):
    send(IP(dst="10.10.10.1") / UDP(dport=9999) / Raw(b"A" * size))

# Built-in fuzz function (randomizes fields)
send(IP(dst="10.10.10.1") / fuzz(TCP()))

Tips

  • Always run as root/sudo — raw sockets require elevated privileges
  • pkt.show() and ls(pkt) are invaluable for exploring packet fields
  • conf.iface = "eth0" sets the default interface
  • Scapy can read/write pcap files: rdpcap() and wrpcap()
Useful commands reference
ls()               # List all protocols
ls(IP)             # List IP fields
conf.iface         # Default interface
conf.verb = 0      # Suppress output

pkt.show()         # Pretty print packet
pkt.summary()      # One-line summary
hexdump(pkt)       # Hex dump

send()             # L3 send
sendp()            # L2 send
sr()               # L3 send+receive (multiple)
sr1()              # L3 send+receive (one)
srp()              # L2 send+receive
sniff()            # Capture packets
rdpcap()           # Read pcap
wrpcap()           # Write pcap
getmacbyip()       # ARP lookup

Vue d’ensemble

Scapy est une bibliothèque Python interactive de manipulation de paquets. Elle permet de forger ou décoder des paquets à n’importe quelle couche (Ethernet, IP, TCP, DNS, etc.), de les envoyer, de capturer les réponses, et de scripter des interactions réseau complexes. Idéal pour construire des outils sur mesure, des PoC et de la recherche protocolaire.

Démarrer Scapy

# Mode interactif
sudo scapy
# Ou importer dans un script Python
from scapy.all import *

Fabrication de paquets

# Construire un simple paquet TCP SYN
pkt = IP(dst="10.10.10.1") / TCP(dport=80, flags="S")

# Ping ICMP
pkt = IP(dst="10.10.10.1") / ICMP()

# Requête DNS UDP
pkt = IP(dst="8.8.8.8") / UDP(dport=53) / DNS(rd=1, qd=DNSQR(qname="target.com"))

# Trame Ethernet personnalisée
pkt = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst="10.10.10.1")

# Voir la structure du paquet
pkt.show()
pkt.show2()  # Champs calculés

# Dump en hexadécimal
hexdump(pkt)

Envoi de paquets

# Envoi couche 3 (pas de gestion des réponses)
send(IP(dst="10.10.10.1") / ICMP())

# Envoi couche 2
sendp(Ether() / IP(dst="10.10.10.1") / ICMP(), iface="eth0")

# Envoyer et recevoir (couche 3) : renvoie answered, unanswered
ans, unans = sr(IP(dst="10.10.10.1") / TCP(dport=80, flags="S"), timeout=2)

# Envoyer et recevoir un seul paquet
ans = sr1(IP(dst="8.8.8.8") / ICMP(), timeout=2)

# Envoi/réception couche 2
ans, unans = srp(Ether() / IP(dst="10.10.10.1") / ICMP(), iface="eth0", timeout=2)

Sniffing

# Sniffer 10 paquets
pkts = sniff(count=10)

# Sniffer avec filtre
pkts = sniff(filter="tcp port 80", count=20, iface="eth0")

# Sniffer avec callback
sniff(prn=lambda x: x.summary(), filter="icmp")

# Sniffer et sauvegarder en pcap
sniff(filter="tcp port 80", count=100, iface="eth0") \
    .nsummary()
wrpcap("capture.pcap", pkts)

# Lire un pcap
pkts = rdpcap("capture.pcap")

Cas d’usage courants

# Scan ARP (découverte d'hôtes)
ans, _ = srp(Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst="10.10.10.0/24"),
             timeout=2, iface="eth0")
for sent, recv in ans:
    print(recv.sprintf("%ARP.psrc% - %Ether.src%"))

# Scan TCP SYN
ans, unans = sr(IP(dst="10.10.10.1") / TCP(dport=[22,80,443,445], flags="S"),
                timeout=2)
for sent, recv in ans:
    if recv.haslayer(TCP) and recv[TCP].flags == 0x12:  # SYN-ACK
        print(f"Port {recv[TCP].sport} open")

# Requête DNS
resp = sr1(IP(dst="8.8.8.8") / UDP(dport=53) /
           DNS(rd=1, qd=DNSQR(qname="target.com")), timeout=2)
print(resp[DNS].summary())

# ARP Spoofing (MITM)
def arp_spoof(target_ip, spoof_ip, iface="eth0"):
    target_mac = getmacbyip(target_ip)
    pkt = ARP(op=2, pdst=target_ip, hwdst=target_mac, psrc=spoof_ip)
    send(pkt, verbose=False)

Fuzzing

# Fuzzer les flags TCP
for flag in ["S", "SA", "F", "R", "P", "U", "A"]:
    sr1(IP(dst="10.10.10.1") / TCP(dport=80, flags=flag), timeout=1)

# Fuzzer la longueur du payload
for size in range(0, 1000, 100):
    send(IP(dst="10.10.10.1") / UDP(dport=9999) / Raw(b"A" * size))

# Fonction de fuzz intégrée (randomise les champs)
send(IP(dst="10.10.10.1") / fuzz(TCP()))

Conseils

  • Toujours lancer en root/sudo : les sockets raw nécessitent des privilèges élevés
  • pkt.show() et ls(pkt) sont précieux pour explorer les champs d’un paquet
  • conf.iface = "eth0" définit l’interface par défaut
  • Scapy peut lire/écrire des fichiers pcap : rdpcap() et wrpcap()
Référence des commandes utiles
ls()               # List all protocols
ls(IP)             # List IP fields
conf.iface         # Default interface
conf.verb = 0      # Suppress output

pkt.show()         # Pretty print packet
pkt.summary()      # One-line summary
hexdump(pkt)       # Hex dump

send()             # L3 send
sendp()            # L2 send
sr()               # L3 send+receive (multiple)
sr1()              # L3 send+receive (one)
srp()              # L2 send+receive
sniff()            # Capture packets
rdpcap()           # Read pcap
wrpcap()           # Write pcap
getmacbyip()       # ARP lookup