Lab 07: TCP Sessoion Hijacking

Table of contents
  1. Lab 07: TCP Sessoion Hijacking
  2. Introduction
    1. Logistics
    2. Learning objectives
    3. Getting the configuration
    4. Generating your .env file
    5. Network topology
  3. Code introduction
    1. Compilation
  4. Step 1: Inject any command
    1. Setup
    2. The exploit
    3. Implementation
      1. 1.1 Complete the stubs
      2. 1.2 Implement and test the trigger function
        1. Helper setup
      3. 1.3 Hijack the session
      4. Testing
  5. Step 2: Establish a reverse shell
    1. Testing
  6. Demo
    1. Submission

Introduction

This lab is the final conclusion to the path we started all the way in the first lab. It builds on Lab 07 and our TCP discussion to finally implement a true Man-In-The-Middle (MITM) exploit: one that employs TCP session hijacking.

Logistics

We will continue with the same set of tools from Lab01, these are namely:

  1. Wireshark to visually see packets and protocols.
    • Install this on your local machine, so you can see things visually.
  2. If you are comfortable with command line, you can also use tshark to observe the same packets and protocols, directly on the server machine.

  3. scp or rsync will prove to be useful to obtain packet captures from the server and download them on your local machine. They should be available by default on your Linux distribution that you are running.

Learning objectives

After completing this lab, you should be able to:

  • Conduct an Man-In-The-Middle (MITM) attack using Address Resolution Protocol (ARP) cache poisoning.

  • Modify TCP packets on the fly to violate the integrity of communication packets.

Getting the configuration

You can find the starter setup for this lab under the 09_Lab09 directory of the csse341-labs repository. If you have set up your private repository correctly, you can fetch the latest version of the labs using the following sequence of commands:

  1. Synchronize with the labs remote using git fetch upstream.

  2. Pull the latest changes from the main branch of the class repository:

    $ git pull upstream main
    
  3. Push the starter setup to your repository so you can start modifying it:

    $ git add 09_Lab09
    $ git push origin main
    

Generating your .env file

Before we spin up our containers, there are some configuration variables that we must generate. To do so, please run the gen_env_file.sh script from the lab repository directory as follows:

$ ./gen_env_file.sh

If run correctly, you will find the following new files:

  1. .env (hidden file - use ls -al to see it) contains your UID and GID variables.

  2. connect_*.sh a utility script to connect to each container in this lab.

  3. run_*.sh is another utility script that allows you to run commands on a container without logging into it.

Network topology

In this lab, we will be working with three machines connected to the same local network. They will live on the same subnet and all can access each other directly. The machines are:

  1. hostA with IP address 10.10.0.4
  2. hostB with IP address 10.10.0.5
  3. attacker with IP address 10.10.0.10

Code introduction

We will continue with a similar structure to the previous but we’ll extend it with a few more utilities. Since we will be observing and modifying TCP packets, we will add some utility functions to parse TCP packets.

First, copy over your solution to previous lab so we can reuse the implementation from there. To do so, from the top level directory of the current lab (i.e., 09_Lab09), do the following:

$ cp ../07_Lab07/volumes/code/src/arp_util.c volumes/code/src/arp_util.c
$ cp ../07_Lab07/volumes/code/src/poison.c volumes/code/src/poison.c

Please do not copy over the tcp_util.c file from the previous lab, it contains new functions now. You can copy over the parts of it that are command but need to update that file with new functions.

If you modified any other files, please be careful when copying them. Make sure to not interfere with any functionality that I already implement for you. It is better to ask for clarification before copying over more code int this lab.

As in the previous lab, we structure the code as follows:

  • The include directory contains definitions for the functions and utility headers.

  • The src directory contains the implementation files. Of particular interest to us are:

    1. arp_util.c: This is the file you should copy over from the previous lab.
    2. poison.c: This file contains code that would run a dual ARP cache poisoning to make the attacker act the MITM between the two hosts. It relies on the send_arp_packets function we implement in the previous lab.
    3. sniff.c: This file contains code to sniff TCP packets coming form either hostA or hostB.
    4. tcp_util.c: This is where we’ll implement our session hijacking exploit.

If you completed Lab 07, then the only file you need to modify is tcp_util.c. You can copy the rest over from the previous lab.

Compilation

As in the previous lab, we will use cmake as our build system to facilitate the resolution of dependencies. To build your code, you should first generate the appropriate makefiles as follows.

On your server (not a container), navigate to the volumes/code directory and then do the following:

$ mkdir build
$ cd build
$ cmake ..

Once we have generated the build files (you only need to do that once), you can compiled your code on any change using make in the build directory.

Step 1: Inject any command

Our main task in this lab is to hijack an active telnet connection to run arbitrary commands on our target machine (hostB in this case). Our exploit’s goal is to hijack an established connection, so we are not interested in the TCP handshake, we only want to look for active telnet packets between the host and the designation (Recall the TCP worksheet for a refresher on tetlnet).

Therefore, we will assume that all packets we see on the wire occur after the handshake (Practically, this means we only care about packets that contain data).

Setup

We will need to poison both ARP caches at hostA and hostB so that the attacker acts as a MITM. We will use the same approach in Lab 07. However, I have hard-coded the MAC address for all machines (you can see them in docker-compose.yml) so you can always run the poisoning using the following command:

$ ./run_attacker.sh 'sudo /volumes/code/build/bin/poison -s 3e:a6:0b:fe:d8:95 -v 10.10.0.4 -t 10.10.0.5 -a request'

Make sure you compile your code first. Feel free to shove this command into a script if you’d like.

The exploit

For this part of the lab, your goal is to have hostB execute the command touch /volumes/pwnd.txt by hijacking the telnet session coming from hostA. Your exploit is successful is the file pwnd.txt appears on hostB’s disk after you inject your packets.

For simplicity, we will assume that our exploit will only trigger after the user has entered a specific command on the telnet session. For me, I chose to trigger my exploit when the user enters ls, feel free to choose whichever trigger command you find suitable. Your will implement your own trigger in the function is_triggered at the top of the tcp_util.c file.

Implementation

First, look over the implementation of parse_tcp in the tcp_util.c. This function serves the same purpose as in Lab 07, however, we’d like it to act differently by hijacking the telnet session when the trigger fires.

You can see that in the snippet below:

// 2. Check if the packet contains the trigger.
//    NOTE: is_triggered might need to work across packets.
if(is_triggered(ip, tcp)) {
  nlen = len;
  npkt = hijack_tcp_connect(pkt, ip, tcp, "Command for your choice", &nlen);
  int nrc = pcap_inject(handle, npkt, nlen);
  free(npkt);
  if(nrc == PCAP_ERROR_NOT_ACTIVATED) {
    print_err("Pcap was not actived!\n");
    exit(EXIT_FAILURE);
  } else if(nrc == PCAP_ERROR) {
    print_err("Pcap error: %s\n", pcap_geterr(handle));
    exit(EXIT_FAILURE);
  }
}

This updated code snippet checks is the packet triggers the exploit. If so, it calls the function hijack_tcp_connect to hijack the session and inject the desired command. It will then send the packet on the wire before continuing with forwarding the original packet it received.

1.1 Complete the stubs

Before starting with implementing your trigger function, copy over the code from Lab 07 that handles the TODO items 1 and 3 in parse_tcp. If your implementation worked in Lab 07, it will work correctly here.

To test this out, copy over your code and compile it. Then run the following:

  1. The poison exploit on the attacker in one window/pane.

  2. The sniff binary on the attacker in one window/pane (same as Lab 07).

  3. Open a terminal on hostA and run telnet hostB (or run ./run_hostA 'telnet hostB')

    You should be able to login to hostB using the username root and the password netsec. The connection should be stable and you should be able to run commands on the telnet session.

1.2 Implement and test the trigger function

Next, decide on your trigger function and implement it in is_triggered. You can choose which trigger you want as long as it consists of at least two characters.

For example, in my case, my exploit waits until the user types the command ls into the telnet session. You can use the same one or decide on something different, as long as it has at least two letters in it.

Hint: Recall how telnet sends commands, you might need to track things across packets to know when you trigger condition should fire.

To test your implementation so far, start with the same setup as in the previous step. Then on hostA, type your trigger word. This will cause the function hijack_tcp_connect to run and print Running the TCP session hijacking code.... This function does nothing yet, we will complete it in the next step.

Helper setup

To help you out in your setup, I have added the file ./launch_experiment.sh to your Lab 09 root directory. This script will launch a new tmux window with four panes:

  1. The top-left pane will run the poison exploit on the attacker.

  2. The bottom-left pane will run the sniff binary on the attacker.

  3. The top-right pane will run a netcat server on the attacker. You can ignore that one until Step 2.

  4. The bottom-right pane will run a telnet session from hostA to hostB. I will demo this script in class for your convenience.

You do not have to use this script, but I hope that it will make this lab easier to manage.

You will have to run this script from an existing tmux session.

1.3 Hijack the session

Finally, implement the hijack_tcp_connect function to craft a new packet that will hijack the session and execute the command touch /volumes/pwnd.txt on hostB.

There are couple of things to be mindful of in here:

  1. hijack_tcp_connect needs to send a new packet, so you must not modify the input argument pkt in this case. You can however copy its content over into a new packet.

  2. Your new packet should be longer then the original packet (watch out when using memcpy).

  3. The argument len is an in/out argument. It contains the original length of the received packet (i.e., *len is the full length of pkt). We also use it to return the length of the new packet to the caller of hijack_tcp_connect. Therefore, your code should set *len = //length of new packet before you return.

The comments in the function contain hints and steps that you can use to break down your implementation. Please read them carefully.

Based on our TCP discussion, you will need to think about the content of the command you’d want to send (i.e., is touch /volumes/pwnd.txt enough?). Also, carefully consider sequence number in the inject TCP packet. Please ask questions about your thought process if you are unsure how to proceed.

Testing

To test your exploit, launch your experiment and then type your trigger word into hostA’s telnet session. Once the trigger is active, your code should inject a new packet in the connection’s stream. Depending on your sequence number choice, either momentarily or a few keystrokes later, the file pwnd.txt should show up in your volumes directory.

Step 2: Establish a reverse shell

If you have done step 1 carefully, this should be a simple modification on it. Based on the experiments you have done in the reverse shell lab, modify the call to hijack_tcp_connect in the parse_tcp to establish a reverse shell on the attacker container.

Please note that this only requires a change to the command you use when hijacking the session. You do not need to change anything in hijack_tcp_connect, just the arguments with which you call it.

Your exploit in this experiment would be successful if you can have a hostB shell running on the attacker machine, from which you can execute any arbitrary command.

Testing

Launch the experiment script ./launch_experiment.sh from the root of Lab09 and then type your trigger in the hostA session. Depending on your implementation, either directly or a few keystrokes later, a shell should show up in the netcat server running on the attacker machine (top-right).

Demo

Once your are confident with your implementation, please demo it to your instructor for grading.

Submission

Please submit your modified .c and .h to the appropriate Gradescope dropbox.