Crafting Packets with Scapy

CybersecurityBeginner
Practice Now

Introduction

Scapy is a powerful interactive packet manipulation program and library written in Python. It enables you to forge or decode packets of a wide number of protocols, send them on the wire, capture them, and match requests and replies.

What makes Scapy special is that it lets you work at the packet-construction layer instead of only the socket or application layer. That means you can explicitly control headers, flags, fields, and protocol combinations in a way that ordinary client libraries usually hide.

In this lab, you will learn the basics of packet crafting using Scapy. You will start by importing the necessary modules, then move on to forging ICMP (ping) and TCP packets layer by layer. Finally, you will send these custom packets and analyze the network responses. This hands-on experience will give you a deeper understanding of how network protocols work under the hood and how security professionals test network defenses.

Import Scapy Modules

In this step, you will set up your Python environment and import the necessary Scapy modules. While Scapy can be used as an interactive console, writing scripts allows you to save, modify, and reuse your packet crafting logic easily.

This is a shift from using protocols to constructing them. Instead of only asking an existing application to send traffic for you, you are defining what the traffic should look like yourself.

First, ensure you are in your default working directory. You can use the pwd command to verify this, but you should already be in ~/project.

Create a new Python file named packet_crafter.py using the nano text editor.

nano packet_crafter.py

Inside this file, you need to import the Scapy library. The standard way to use Scapy in scripts is to import all functions from the scapy.all module. Add the following lines to your script:

from scapy.all import *

print("Scapy modules imported successfully.")

Save the file with Ctrl+O and Enter. If you want to keep building the script before testing it, you can stay in nano. Use Ctrl+X when you are ready to return to the terminal.

Now, run your script using Python to ensure there are no syntax errors and that the Scapy library loads correctly. Because crafting raw network packets requires elevated privileges, you must run the script with sudo.

sudo python3 packet_crafter.py

You should see output similar to the following indicating success:

Scapy modules imported successfully.

In some lab environments, Scapy may print an additional warning from optional Python packages such as matplotlib before the success message. If you still see Scapy modules imported successfully., you can continue to the next step.

Forge an ICMP Echo Request

In this step, you will forge an ICMP Echo Request packet, commonly known as a "ping". Network packets are built in layers, following the OSI model. In Scapy, you can stack these layers together using the / operator.

Layering is one of the most important ideas to absorb here. Each layer adds its own fields and responsibilities. Scapy mirrors that model directly, which is why its syntax makes packet structure easier to reason about.

An ICMP ping requires an IP layer (Network layer) to specify the source and destination addresses, and an ICMP layer to define the type of message.

If packet_crafter.py is still open in nano, continue editing it. Otherwise, reopen it:

nano packet_crafter.py

Append the following code to the end of your script. This code creates an IP object targeting Google's public DNS server (8.8.8.8), creates an ICMP object, and stacks them together. The show() method will print a detailed breakdown of the packet's structure.

## Create an IP layer targeting Google's DNS
ip_layer = IP(dst="8.8.8.8")

## Create an ICMP layer (Echo Request)
icmp_layer = ICMP()

## Stack the layers to form the final packet
icmp_packet = ip_layer / icmp_layer

print("\n--- ICMP Packet Structure ---")
icmp_packet.show()

Save the file with Ctrl+O and Enter. You can stay in nano and continue to the next step if you want to finish more of the script before running it.

Run your updated script to view the structure of the packet you just forged.

sudo python3 packet_crafter.py

You will see a detailed output showing the default values Scapy automatically filled in for you, such as the protocol version, TTL (Time To Live), and the ICMP type.

--- ICMP Packet Structure ---
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     =
  frag      = 0
  ttl       = 64
  proto     = icmp
  chksum    = None
  src       = 10.0.2.15
  dst       = 8.8.8.8
  \options   \
###[ ICMP ]###
     type      = echo-request
     code      = 0
     chksum    = None
     id        = 0x0
     seq       = 0x0
     unused    = ''

Craft a Custom TCP SYN Packet

In this step, you will craft a TCP SYN packet. This specific type of packet is frequently used in network scanning (such as Nmap's stealth scan) to check if a specific port is open on a target machine without fully establishing a connection.

This is a good example of how small header changes produce very different network behavior. You are not changing the destination host or port, only the flags and protocol fields, yet those details define the meaning of the packet and how the target will interpret it.

Similar to the ICMP packet, a TCP packet requires an underlying IP layer. On top of that, you will add a TCP layer where you can specify the destination port and the TCP flags. The S flag stands for SYN (Synchronize).

If you are still in the same nano session, continue editing packet_crafter.py. Otherwise, reopen it:

nano packet_crafter.py

Append the following code to your script. We will reuse the ip_layer you created in the previous step and stack a new TCP layer on top of it, targeting port 80 (HTTP).

## Create a TCP layer targeting port 80 with the SYN flag
tcp_layer = TCP(dport=80, flags="S")

## Stack the IP and TCP layers
tcp_packet = ip_layer / tcp_layer

print("\n--- TCP SYN Packet Structure ---")
tcp_packet.show()

Save the file with Ctrl+O and Enter. Exit with Ctrl+X only when you want to run the script from the terminal, or keep editing and continue to the final step.

Run the script again to inspect the structure of your new TCP SYN packet.

sudo python3 packet_crafter.py

The output will now include the TCP layer details, showing the destination port set to http (port 80) and the flags set to S.

--- TCP SYN Packet Structure ---
###[ IP ]###
  version   = 4
  ihl       = None
  tos       = 0x0
  len       = None
  id        = 1
  flags     =
  frag      = 0
  ttl       = 64
  proto     = tcp
  chksum    = None
  src       = 10.0.2.15
  dst       = 8.8.8.8
  \options   \
###[ TCP ]###
     sport     = ftp_data
     dport     = http
     seq       = 0
     ack       = 0
     dataofs   = None
     reserved  = 0
     flags     = S
     window    = 8192
     chksum    = None
     urgptr    = 0
     options   = []

Send and Analyze the Network Response

In this final step, you will actually send the ICMP packet you crafted over the network and capture the response.

Sending the packet completes the mental model: define layers, build the packet, transmit it, then interpret the reply. That is the same high-level loop behind many custom network testing tools.

Scapy provides several functions for sending packets. The sr1() function is particularly useful because it sends a packet and waits to return the very first response it receives. The "s" stands for send, "r" for receive, and "1" for one packet.

If you kept the file open, continue editing it. Otherwise, reopen packet_crafter.py:

nano packet_crafter.py

Append the following code to send the icmp_packet and analyze the reply. We include a timeout parameter so the script doesn't hang indefinitely if the packet is dropped or blocked.

print("\n--- Sending ICMP Packet ---")
## Send the packet and wait for 1 response, with a timeout of 2 seconds
## verbose=0 hides the default Scapy sending output for cleaner results
response = sr1(icmp_packet, timeout=2, verbose=0)

if response:
    print(f"Response received from: {response[IP].src}")
    print("\n--- Response Packet Structure ---")
    response.show()
else:
    print("No response received. The packet might have been dropped.")

Save the file and exit the editor.

Run your script to send the packet out to the internet and wait for Google's DNS server to reply.

sudo python3 packet_crafter.py

If the network allows the ICMP request, you will see the structure of the response packet. Notice that the ICMP type in the response is echo-reply.

--- Sending ICMP Packet ---
Response received from: 8.8.8.8

--- Response Packet Structure ---
###[ IP ]###
  version   = 4
  ihl       = 5
  tos       = 0x0
  len       = 28
  id        = 0
  flags     =
  frag      = 0
  ttl       = 114
  proto     = icmp
  chksum    = 0x67d8
  src       = 8.8.8.8
  dst       = 10.0.2.15
  \options   \
###[ ICMP ]###
     type      = echo-reply
     code      = 0
     chksum    = 0xffff
     id        = 0x0
     seq       = 0x0
     unused    = ''
###[ Padding ]###
        load      = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

Summary

You learned how to set up a Python script with Scapy, manually forge ICMP and TCP packets layer by layer using the / operator, and send these packets over the network to analyze the raw responses. This foundational knowledge is crucial for understanding how network protocols operate, performing security assessments, and developing custom network analysis tools.