Shebang and Python script

A shebang refers to two letters
#!
It is also called sha-bang, hashbang, pound-bang, or hash-pling. Consider the following simple python file hello.py:

# hello.py
print("hello")
print("world")
Let's make the file executable, and run it.
~$ chmod 755 hello.py
~$ ./hello.py
./hello.py: line 2: syntax error near unexpected token `"hello"'
./hello.py: line 2: `print("hello")'
We see an error because we didn't give a shebang line that specifies the interpreter to be used for this script file hello.py. So, let's add a shebang line. Obviously, the inpterpreter is python3 whose full path name is /usr/bin/python3.

#!/usr/bin/python3
# hello.py
print("hello")
print("world")
Now, let's run the file again:
~$ ./hello.py
hello
world

UDP Socket Spoofing

In this lecture, we learn how to spoof UDP packets. We would like to write udp_spoof.py that will send a spoofed UDP packet.

Our Goal

We would like to fake the source IP address in the UDP packet.

For example:
We will demonstrate our success by showing a UDP netcat chat. We will use serv_udp.py and check the the IP/port to see if the spoofing works.

Socket Creation

We first create a raw socket.

Recall that IP header contains the source IP address field. This implies that we need to craft IP header and IP payload. Fortuntately, since we don't fake the Ethernet frame part, we simply let the OS fill out that part honestly.

This means that the raw socket that we create can be on the IP level, not the Ethernet level.


#!/usr/bin/python3
# udp_spoof.py
from socket import *
raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)

Crafting a Packet and struct.pack

Now, we have to create a bytes object for the faked UDP packet. We will use struct.pack. Here's our strategy.
  1. We first create a bytes object udp_payload.
  2. We next construct a bytes object udp_header. Note we have
    ip_payload = udp_header + udp_payload
    Note: bytes objects can be concatenated using "+" operator. For example, b"hello" + b"world" will be evaluated to b"helloworld".
  3. Next construct ip_header
  4. Finally, we have ip_pkt = ip_header + ip_payload.

UDP payload

In our context, udp_payload corresponds to any chat text. Let's say:

udp_payload = b"Hello World\n"

Crafting a UDP header

See the header format on the right.
  • It's easy to fill the Source Port (random), Destination Port (9000).
  • The Lengh field should consider both the header and the payload (i.e., 8 + len(udp_payload)). Note that the UDP header is 8 bytes long (2 lines).
  • Filling out Checksum seems to be difficult. Fortunately, it's fine to set 0 for this field! Recall UDP doesn't provide error-correction, so this Checksum field is optional, but not usually used.
UDP Header Format
 0                   1                   2                   3   
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            Length             |           Checksum            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

So, we can write the following code:

import struct 
udp_header = struct.pack(">HHHH", 12345, 9000, 8 + len(udp_payload), 0)
ip_payload = udp_header + udp_payload 

Crafting a IP header

One way that helps is:
  • Capture a normal UDP packet using wireshark.
  • Mimick everything except the IP address and port number. You can see a packet capture on the right.

We will fill out the IP header according to the format shown on the right.

  • The first line:
    • Version: 4
    • IHL: 5 (lines for IP header)
    • Type of Service: 0 (No special service)
    • Total length: header length (20 bytes) + payload length.
    
    version = 4
    ihl = 5
    # version is 4 bits, IHL is 4 bits
    # version_ihl is 8 bits (1 byte)
    version_ihl = (version << 4) | ihl
    type_of_service = 0
    total_length = 20 + len(ip_payload)
    ip_header = struct.pack(">BBH", 
                  version_ihl, type_of_service, total_length)
    
  • The second line:
    • Identification: Any unique number would be fine. We will set 12345.
    • Fragment Offset: If you just set 0, the OS will take care of the rest.
    
    ip_header += struct.pack(">HH", 12345, 0)
    
  • The third line:
    • TTL: Let's just set 20.
    • Protocol: For UDP, you need to set 17.
    • Checksum: Just put 0, and then the OS will fill in a correct value.
    
    ttl = 20
    protocol = 17
    checksum = 0
    ip_header += struct.pack(">BBH", ttl, protocol, checksum)
    
  • The fourth line: source address. We spoof it to "123.123.123.123".
    
    ip_header += struct.pack(">BBBB", 123, 123, 123, 123)
    
  • The fifth line: destnation address. Here, we set 192.168.172.5. Change the address according to your need. It should be the IP address of the receiving host.
    
    ip_header += struct.pack(">BBBB", 192, 168, 172, 5)
    ip_pkt = ip_header + ip_payload
    
Internet Datagram Header
 0                   1                   2                   3   
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version|  IHL  |Type of Service|          Total Length         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|         Identification        |R D M|      Fragment Offset    |
|                               |S F F|                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Time to Live |    Protocol   |         Header Checksum       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       Source Address                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Destination Address                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


Sending a Packet

You can simply use sendto() function as usual.

addr = ("192.168.172.5", 9000)
raw_socket.sendto(ip_pkt, addr)

Acitivty

  1. Have a UDP netcat chat. Use nc for the listening party.
  2. Using the above code, send a spoof packet.
  3. Use wireshark to capture the udp packet. See how the actual packet contents look like.

Applications of Packet Spoofing