Once, before the protection of a regular laboratory work, I was asked a question: which fields of the IP packet can be used for quilted? I did not know and only shrugged. But I soon decided to explore this issue.
Under the cat you will find the study of the headers of IP-packages, own ping utility in Python and several ways to transfer data without attracting attention.
Content
- IP packet structure
- Setting up the environment
- Ping: Easy version
- Ping: Difficult option
- Improvements?
IPv4 packet structure

Select the fields, the change of which does not greatly affect the package:
IHL can vary from 5 to 15.
The
ToS field is used to prioritize traffic and traffic congestion notifications without dropping packets. Most often, this field is 0. Theoretically, it can be used to transfer a whole byte of information.
The packet length is an excellent field for transmitting numbers from 20 to 65535.
TTL can transmit up to 7 bits of information. It is necessary to know the number of hopes before the receiver and take this into account.
Setting up the environment
To repeat the experiment, you need two machines with Python and the scapy framework.
You can install it following the
instructions in the documentation . In my case, these were two droplets on the DO with the local network turned on. To test the quilted performance, two routes were chosen: through a local network for 1 hop and through the Internet for 2 hop.
Ping: Easy option
First, we implement sender.py, which will send ICMP packets without hidden messages.
from scapy.all import *
Scapy, before sending, will fill in the remaining fields with default values and calculate the checksum.
On the side of the receiver, we write listener.py, which will listen and display all incoming ICMP packets.
from scapy.all import *
Approximate listener output ###[ Ethernet ]### dst = hh:hh:hh:hh:hh:hh src = gg:gg:gg:gg:gg:gg type = 0x800 ###[ IP ]### version = 4 ihl = 5 tos = 0x0 len = 28 id = 24923 flags = frag = 0 ttl = 64 proto = icmp chksum = 0x4364 src = 10.0.0.1 dst = 10.0.0.2 \options \ ###[ ICMP ]### type = echo-request code = 0 chksum = 0xf7ff id = 0x0 seq = 0x0 ###[ Padding ]### load = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
In the header of the IP packet there is an “ID” field. Fill it with the characters "A" and "B":
payload = ord("A") * 0x100 + ord("B") pkt = IP(src="10.0.0.1", dst="10.0.0.2", id = payload) / ICMP(type = 8)
Moreover, in the ICMP header there is exactly the same field in which you can also load two bytes.
Let's change the listener to display the received data:
from scapy.all import * import sys packets = sniff(filter="icmp", timeout = 10, count = 100, iface="eth0") for pkt in packets: if pkt[ICMP].type != 8: continue
In the image and likeness you can fill almost any field, previously noted as suitable for quilted.
Ping: Difficult option
The transfer of data from the previous paragraph was not the most obvious, but we can make it even more obscure. You can hide the data in the checksum field. According to
RFC1071 , the checksum is (all of a sudden!) The bitwise inversion of a slightly more complex arithmetic sum.
An example explanationSuppose we have a header for which we want to calculate a checksum. At the time of calculations, the checksum field is reset.
4500 003c 000a 0000 8001 [checksum] c0a8 000d c0a8 000d
1. Add up all 16-bit words, remembering the transfer from the high-order bit:
4500 + 003c + 000a + 0000 + 8001 + [checksum=0000] + c0a8 + 000d + c0a8 + 000e = = (2) 46b2
2. Add the result with the transfers:
46b2 + 2 = 46b4
3. Invert:
~(46b4) = b94b
b94b - the checksum we are
looking for . To check, you can substitute in the header and perform steps 1 and 2. If you get the FFFF, then the amount found is correct.
Check:
1. 4500 + 003c + 000a + 0000 + 8001 + [checksum=b94b] + c0a8 + 000d + c0a8 + 000e = = (2) FFFD 2. FFFD + 2 = FFFF
We know that the checksum of a packet changes as nodes pass through the network as TTL changes. Also, when passing NAT in the packet, the “source address” is replaced, which also affects the checksum. And how much the TTL decreases when our listener reaches ... The cherry on the cake is that the “identifier” is the same as the checksum. This fact allows us to influence the checksum and change it to any value from the domain of definition. Since the checksum (payload) will be calculated only when passing the last node in the route, it is important when calculating to take into account everything that can be changed in the package during the route.
The algorithm for finding the "identifier", which will give us the desired checksum:
- Configure the package as when passing the last node (IP, TTL, etc)
- In the "ID" write the payload
- Calculate the checksum
- The result must be recorded in the "ID" of the package being sent.
Let us write a function that, by the number of hops, IPs behind a NAT and two bytes of the payload, will form a packet.
Improvements?
- The chksum, seq, id fields in the ICMP protocol header can also be used to transfer data
- ToS can be used to identify packets "from one's own" and ignore other people's echo-request.