IP Spoofing

The goals of this assignment are to:

  1. Learn how to sniff and inject network traffic using raw sockets
  2. Bypass IP address-based network authentication

IP Address Authentication

IP addresses have been widely used as network principals for authentication. It was common in the early years of UNIX to control access to network services by adding IP addresses to an allow list. (For example, see man hosts.allow.) IP addresses still see wide use as network principals in firewalls and malware block lists. However, as discussed during lecture, IP addresses are in many contexts not a reliable principal for several reasons, including churn, weak binding to identity, and—not least—the ability to forge packets with arbitrary source addresses using raw sockets.

Service Protocol

In this assignment, you will exploit a vulnerable network service using IP address authentication. The service, which you can find in Canvas as ip_auth.${arch}.img.zst, implements a simple TCP protocol on port 1200:

\[ \begin{align*} C \rightarrow S &: \mathsf{u16}(|\mathsf{identifier}|) \cdot \mathsf{identifier} \cdot \mathsf{nonce} \\ S \rightarrow C &: \mathsf{u8}(\mathsf{authorized}) \cdot \mathsf{u16}(|\mathsf{secret}|) \cdot \mathsf{secret} \\ \end{align*} \]

where

  • \(\mathsf{identifier}\) is your hacker alias encoded as a UTF-8 string,
  • \(\mathsf{nonce}\) is an 8-byte nonce,
  • \(\mathsf{secret}\) is a UTF-8 encoded string,
  • \(|x|\) is the length of a byte array \(x\),
  • \(\mathsf{u8}(x)\) is a single byte \(x\),
  • \(\mathsf{u16}(x)\) encodes an integer \(x\) as two big-endian bytes, and
  • \(\cdot\) is concatenation.

An example of running the server in an isolated network namespace with two IPv4 ACL entries (127.0.0.0/8 and 10.47.21.0/26) is:

$ docker run -it --rm netsec_ip_auth server -a 127.0.0.0/8 -a 10.47.21.0/26

Keep in mind that you may need to adapt this example to your development environment.

After the client sends its ID, the server will check whether the sending IP address is authorized to use the service as well as the validity of the nonce. If both checks pass, the server will return a non-zero value for \(\mathsf{authorized}\) as well as a secret. Otherwise, it will return \(\mathsf{authorized}=0\) and no secret.

The only content your program should write to stdout is the following JSON object:

{
    "id": "{{identifier}}",
    "secret": "{{base64(secret)}}",
    "nonce": "{{base64(nonce)}}"
}

Whitespace formatting doesn’t matter, only that piping the output of your program validates as JSON of the above form. Feel free to adapt the output validator script that has already been provided in Canvas.

Bypassing Authentication

To bypass the service’s authentication procedure, you will need to forge packets with source addresses that are on the service’s allow list. To do so, you will need to construct and inject packets using raw sockets. You can do this with a multitude of libraries, though scapy is a good choice if you don’t have a reason to use another one.

Raw sockets allow user space programs to bypass the kernel network stack and directly read or write link-layer frames on specific interfaces connected to the local machine. Thus, when injecting packets you will need to build valid Ethernet frames that contain a valid IPv4 datagram, which in turn contains a valid TCP segment. Upon that foundation, you will need to create a TCP connection with the server, and participate in the application-layer protocol.

Keep in mind that you will also need to sniff responses to your injected frames!

Submission Instructions

Package your solution as a gzipped TAR archive. Your solution should expand to the following directory structure.

$ tree -F ip_spoofing
ip_spoofing
├── Dockerfile
└── src/

The source code to your solution should be contained in src/. Your Dockerfile should create a container image that runs your solution given an injection interface, your client’s addresses, the gateway or server’s MAC address, the server’s IPv4 addresses, and a variable list of CIDR blocks representing the allow list as arguments. For example:

$ docker run -it --rm --cap-add net_raw --network=host ${solution_image} \
    docker0 \                   # Injection/sniffing interface
    00:11:22:33:44:55 \         # Client MAC address
    10.0.0.101 \                # Client IPv4 address
    66:77:88:99:aa:bb \         # Server or gateway MAC address
    172.17.0.101 \              # Server IPv4 address
    10.0.1.0/24 10.0.2.0/24     # Allow list

Submit the solution archive to Canvas.


© 2023 wkr