November 2, 2026
Scapy 101: Craft, Send and Analyze Packets on Kali Linux
A beginner-friendly lab: install Scapy, build ICMP and TCP packets, send them and read the replies
By Muhammad Fahad
5 min read
Basic Linux terminal skills are enough to follow along.
Note:This lab is for educational purpose only.
What is Scapy?
Scapy is a free, open-source packet manipulation tool written in Python. It runs inside a Python interpreter, so you build packets as objects, edit any field, and automate with normal Python code. Unlike scanners that report only open, closed or filtered, Scapy gives you the whole reply packet.
Common uses:
• Crafting and sending custom packets (ICMP, TCP, UDP, DNS, ARP)
• Network discovery and port scanning
• Sniffing traffic and reading pcap files
• Traceroute and firewall/IDS testing (authorized only)
• Learning how protocol headers work
Authorization: Only send packets to systems you own or have permission to test. My lab target was a public website, so use your own VM or a host that allows testing, such as scanme.nmap.org.
Step 1: Install
sudo apt install python3-scapy -y
Step 2: Launch
Scapy needs root to use raw sockets.
sudo scapy
The >>> prompt is a Python session with Scapy loaded. The PyX warning is harmless; it only affects psdump() and pdfdump().
Step 3: Explore
lsc() lists Scapy's commands, including send(), sr(), sr1(), sniff() and rdpcap().
lsc()
help(sr) shows documentation. sr() sends and receives packets at layer 3. Press q to exit.
help(sr)
ls() lists all protocol layers. ls(layer) shows a layer's fields and defaults.
ls()
ls(IP)
ls(TCP)
ls(UDP)
ls(ICMP)
explore() opens an interactive menu in the terminal to browse layers. Pass a module path to open one, such as explore("scapy.layers.dns").
explore()
Step 4: Create packets
Scapy stacks layers with /, outermost first: IP()/ICMP() is an ICMP message inside an IP packet.
ICMP packet
packet_ICMP = IP(dst="juiceshop.com")/ICMP()/"payload_to_share"
packet_ICMP.show()
Scapy filled in our source IP. The ICMP type is echo-request (the same message ping sends). Fields shown as None, such as length and checksum, are calculated when the packet is sent.
packet_ICMP.summary() # one-line view
ls(packet_ICMP) # all fields and defaults
TCP SYN packet
packet_TCP = Ether()/IP(dst="juiceshop.com")/TCP(dport=443, flags='S')
packet_TCP.show()
packet_TCP.summary()
ls(packet_TCP)
This is a SYN (flags='S') to port 443, the first step of the TCP handshake. The default source port is 20, shown as ftp_data.
Note: Ether() makes a layer 2 frame (send with sendp() or srp()). sr() works at layer 3, so the packets we send below do not include Ether().
Step 5: Send and analyze
ICMP
pkt_ICMP = IP(dst="juiceshop.com")/ICMP()
sr(pkt_ICMP)
- marks a matched reply. Received 1 packets, got 1 answers means the target responded. Now view the result; _ holds the last output.
ans, unans = _
ans.show()
The echo-reply from 23.227.38.32 confirms the host is up.
TCP
pkt_TCP = IP(dst="juiceshop.com")/TCP(dport=[80,443], flags='S')
sr(pkt_TCP)
A list in dport creates one packet per port. sr() saw 12 packets on the interface, but only 2 were replies to our probes.
ans, unans = _
ans.show()
How to read the replies:
• SA (SYN-ACK): the port is open. Ports 80 and 443 here.
• RA or R (reset): the port is closed.
- No reply: filtered by a firewall, or the host is down. Add timeout=2 to avoid long waits.
Bonus: Why You May See a Reset (RST)
You may notice a reset (RST) after the SYN-ACK arrives. It usually comes from your own machine, not the target. Scapy builds and sends the SYN itself, so your operating system has no record of opening that connection. When the SYN-ACK comes back, the OS does not recognize it and replies with a RST. Without a capture, this is easy to mistake for the target refusing you.
Let's confirm it with tcpdump, which shows each packet and its direction.
1. Open a second terminal (a normal Kali terminal, not the Scapy window) and start the capture. Replace eth0 with your interface name if it differs (check with ip a).
sudo tcpdump -i eth0 -n "tcp and host juiceshop.com"
Wait for the line listening on eth0. Nothing else prints until traffic arrives.
2. In the Scapy window, send the probe again:
sr(pkt_TCP)
3. Back in the tcpdump window, press Ctrl+C to stop and read the output.
How to read it. The arrow > points from the sender to the receiver:
• Flags [S]: our machine (192.168.100.14) sends a SYN to port 80 and port 443. The .20 after our IP is the source port, Scapy's default.
• Flags [S.]: the target (23.227.38.32) answers from both ports with a SYN-ACK. The dot stands for ACK.
• Flags [R]: a reset sent from our own IP to the target.
Takeaway: The target accepted the connection by sending SYN-ACK. The reset is your own system closing a connection it never opened. This is normal for Scapy probes. Always check the direction in a capture before deciding who sent a reset.x
Conclusion
We installed Scapy, explored its commands and layers, built ICMP and TCP packets, and read the replies: an echo-reply showed the host was alive, and two SYN-ACKs showed ports 80 and 443 were open. Next, try sr1(), sniff(), and UDP or DNS packets on your own lab.
Resources: scapy.net, scapy.readthedocs.io, github.com/secdev/scapy
#Scapy #CyberSecurity #NetworkSecurity #PenetrationTesting #KaliLinux