{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction\n", "\n", "This tutorial explains how to use Tranalyzer to extract the *bytes-per-burst* (BPB) feature from TLS encrypted YouTube video streams and recognize what video title is contained in a new test sample, or detect that it is a new video. This is an implementation of recent work by Dubin et al. [1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The bytes-per-burst feature\n", "\n", "A flow is viewed as a signal of packets over time. This signal is then transformed in a series of *bursts*. A burst here is defined as the set of packets that were recorded within a certain time window of each other. (Note that this is not a regular binning of the time dimension as a burst can be arbitrarily large in the time dimension as long as the next packet arrives within that window.) Each burst corresponds to the sum of bytes contained in all the packets aggregated into that burst. The total number of bytes in each burst for a given flow is then used to characterize this flow." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Extracting bursts from a flow\n", "\n", "Given the following series of packets:\n", "\n", "![Packet length](files/img/bpb/packets.png)\n", "\n", "Using a time window of 50ms in the `nFrstPkts` plugin, the following bursts are extracted:\n", "\n", "(Note the logarithmic scale on the y-axis and the changing y-limits between the two plots.)\n", "\n", "![Extracted bursts](files/img/bpb/bursts.png)\n", "\n", "Plots like this can also be generated for any given flow file using the `fpsGplt` and `t2plot` scripts. More information on this can be found in the documentation or in the traffic mining tutorial." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Identifying YouTube flows\n", "\n", "In order to identify YouTube flows in a larger PCAP traffic dump, the *Server Name Indication (SNI)* TLS extension is used. The *sslDecode* plugin for Tranalyzer makes the server name available in the `sslServerName` column in the flow file." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Prerequisites\n", "\n", "* Tranalyzer version 0.8.1lmw4 or higher,\n", "* A folder containing your training data:\n", " * Dubin et al [1] provide their data at: http://www.cse.bgu.ac.il/title_fingerprinting/dataset_chrome_100\n", " * The PCAP files used for training are expected to be at the following location: `DATA_PATH/{Class1,Class2,...,ClassN}/Train/*.pcap`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Implementation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are going to use a few modules:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "#!/usr/bin/env python3\n", "\n", "import argparse\n", "import matplotlib.pyplot as plt\n", "import tempfile\n", "import subprocess\n", "import threading\n", "import os\n", "from os.path import dirname, basename, isfile, splitext, normpath, expanduser\n", "import sys\n", "import glob\n", "import pickle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Configuration\n", "\n", "We need the path to the Tranalyzer directory, and to the PCAP files that we are going to be using to create our model." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Configuration: Adapt to your system.\n", "T2_ROOT = '{}/code/tranalyzer2-0.9.0'.format(expanduser('~'))\n", "DATA_PATH = '/mnt/{}/BPB/data_short'.format(os.environ['USER'])\n", "\n", "VERBOSE = False" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Automatically derived\n", "T2_PATH = '{}/tranalyzer2/src/tranalyzer'.format(T2_ROOT)\n", "T2CONF = '{}/scripts/t2conf/t2conf'.format(T2_ROOT)\n", "T2BUILD = '{}/autogen.sh'.format(T2_ROOT)\n", "BPBS_PATH = 'bpbs.data'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setting up Tranalyzer\n", "\n", "First, we need to set up Tranalyzer to include the required non-standard plugins:\n", "\n", "* `nFrstPkts` to get the signal for the first few packets of a flow, and\n", "* `sslDecode` to identify YouTube flows.\n", "\n", "We can use `t2conf` to configure the plugins to our liking. For this tutorial, we set the minimum time window that defines a burst to 50ms and the number of packets to analyze to 200.\n", "\n", "To build Tranalyzer and the plugins, we use the included `autogen` build script." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "REQUIRED_PLUGINS = ['nFrstPkts', 'sslDecode']\n", "\n", "def setup_t2():\n", " options = {\n", " 'NFRST_IAT': '0',\n", " 'NFRST_MINIATS': '0',\n", " 'NFRST_MINIATU': '50000',\n", " 'NFRST_PLAVE': '0',\n", " 'NFRST_PKTCNT': '200'\n", " }\n", "\n", " # Configure\n", " for opt, val in options.items():\n", " subprocess.call([T2CONF, 'nFrstPkts', '-D', f'{opt}={val}'])\n", "\n", " # Build\n", " subprocess.call([T2BUILD])\n", " for plugin in REQUIRED_PLUGINS:\n", " subprocess.call([T2BUILD, plugin])\n", "\n", "def definitely_need_setup_t2():\n", " active_plugins = subprocess.check_output([T2BUILD, '-l'], text=True).splitlines()\n", " return not all(plugin in active_plugins for plugin in REQUIRED_PLUGINS)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Running Tranalyzer\n", "\n", "We need a function that, given a PCAP file, runs Tranalyzer to determine the bursts, and returns the path to the resulting flow file for further processing:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def run_t2(infile, outdir):\n", " subprocess.call([\n", " T2_PATH,\n", " '-r', infile,\n", " '-w', outdir\n", " ], stdout=subprocess.DEVNULL)\n", " return '{}/{}_flows.txt'.format(outdir, splitext(basename(infile))[0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Extract the BPB features\n", "Given a flow file, we now need to extract a list of numbers that correspond to the total number of bytes in each burst. For this, we run a small (T)AWK script and some basic postprocessing that we execute using `tawk`." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def get_bpb(flowfile):\n", " EXTRACT_SCRIPT = '''\n", " source \"$1/scripts/t2utils.sh\"\n", " flowfile=\"$2\"\n", "\n", " cat $flowfile | $TAWK -t -I $flowfile \\\n", " '($dir == \"B\" && $sslServerName ~ /.googlevideo.com/) {\n", " print $numBytesSnt, $L2L3L4Pl_Iat_nP\n", " }' |\n", " sort -nr | # sort by byte count, descending\n", " head -n 1 | # get biggest flow\n", " cut -d$'\\t' -f2 | # get nFrstPkts output (burst)\n", " tr ';' '\\n' | # one burst per line\n", " cut -d'_' -f1 | # extract byte count from each burst\n", " cat\n", " '''\n", " proc = subprocess.Popen(['/bin/bash', '-c', EXTRACT_SCRIPT, '', T2_ROOT, flowfile], stdout=subprocess.PIPE)\n", " bpbs = [int(bpb) for bpb in proc.stdout] if proc.stdout else []\n", " return bpbs\n", "\n", "def extract_bpb(pcap):\n", " with tempfile.TemporaryDirectory() as tempdir:\n", " flowfile = run_t2(pcap, tempdir)\n", " bpb = get_bpb(flowfile)\n", " return bpb" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's check the output using a random PCAP in our training data path:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[434420, 1595848, 359344, 1811560, 351882, 1682472, 1665230, 329019, 2101566, 1793397, 356024, 1789932, 2101566, 338444, 2101566, 1377684, 341082, 1830971, 2084460, 337281, 2101566, 1792682, 328908, 2101566, 1957214, 445567, 1129815]\n" ] } ], "source": [ "try:\n", " print(extract_bpb(glob.glob(f'{DATA_PATH}/*/Train/*.pcap')[0]))\n", "except:\n", " print('Unable to find any PCAPs in your training data. Are you sure you set DATA_PATH correctly?')\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We extract the bursts for several PCAPS in parallel to speed up the process. Each thread is given a PCAP file, runs Tranalyzer, extracts the bursts and stores them into a unique location per thread." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def thread_t2_bpb(bpbs, index, pcap):\n", " bpb = extract_bpb(pcap)\n", " bpbs[index] = bpb\n", "\n", "def get_bpbs_for_class(c):\n", " pcaps_root = '{}/{}/Train'.format(DATA_PATH.rstrip('/'), c)\n", " pcaps = glob.glob('{}/*.pcap'.format(pcaps_root))\n", " bpbs = []\n", "\n", " max_pcaps = 6\n", " chunk_size = max_pcaps # `chunk_size` must divide `max_pcaps`.\n", " pcaps_chunks = zip(*[iter(pcaps[:max_pcaps])]*chunk_size)\n", " for chunk in map(list, pcaps_chunks):\n", " threads_results = [None] * chunk_size\n", " threads = [threading.Thread(target=thread_t2_bpb, args=(threads_results, i, pcap))\n", " for i, pcap in enumerate(chunk)]\n", " for t in threads:\n", " t.start()\n", " for t in threads:\n", " t.join()\n", " for bpbs_c in threads_results:\n", " bpbs.append(bpbs_c)\n", " return bpbs\n", "\n", "def load_bpbs(path):\n", " with open(path, 'rb') as fh:\n", " return pickle.load(fh)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's look at the BPBs for all samples of a class. For each sample of this class, we get a list of numbers representing the number of bytes in a burst:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[4110, 129278, 778713, 268405, 3728018, 4098388, 384251, 3486785, 379773, 3845582, 375686, 1108149], [4990, 66262, 7826406, 384251, 3486785, 379773, 3845582, 375686, 1108149], [4110, 66262, 4098388, 384251, 3486785, 379773, 3845582, 375686, 1108149], [4400, 129278, 149475, 627878, 268405, 402514, 1608766, 405435, 1203498, 4098388, 384251, 3486785, 379773, 1906720, 1938862, 375686, 1108149], [4110, 129278, 149475, 896283, 2011280, 405435, 4931516, 4098388, 384251, 3486785, 379773, 3845582, 375686, 1108149], [4110, 129278, 1287473, 670919, 1554910, 2094745, 8215957, 384251, 3335118, 379773, 3881665]]\n" ] } ], "source": [ "print(get_bpbs_for_class('Avengers'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Building the model\n", "\n", "Using these building blocks, we can now write a function `learn` that, given a list of classes, fetches the corresponding PCAP files from the user-defined path at the top in this script, extracts the bursts for all PCAPs of each class using Tranalyzer, and stores the resulting features in a dictionary.\n", "\n", "We persist this dictionary to disk so that we can call this program again in test mode, give it a new PCAP, and get the video title that most closely matches the unknown sample, given the model.\n", "\n", "Note that here, we are storing the bursts in a list rather than a set (as in the paper). This helps us understand the data better when exploring it visually later on. It does not have an impact on classification accuracy." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "def learn(classes, *, write_to=None):\n", " bpbs = { c: [] for c in classes }\n", " for c in classes:\n", " print('Extracting bursts for class {}'.format(c))\n", " c_bpb = get_bpbs_for_class(c)\n", " for b in c_bpb:\n", " # NOTE: Could be set() to improve performance in production, but we're using\n", " # list() to preserve the order of the bursts and produce more meaningful plots.\n", " bpbs[c].append(list(b))\n", " if write_to:\n", " with open(write_to, 'wb') as fh:\n", " pickle.dump(bpbs, fh)\n", " return bpbs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Classification\n", "\n", "To classify an unknown sample, a simple nearest neighbor approach is used.\n", "\n", "A video is represented as a set of integers, each representing one burst in the signal.\n", "\n", "The unknown sample is classified to the video title of the known sample that shares the most bursts in common with the unknown sample." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "def similarity(xs, ys):\n", " return sum(x in ys for x in xs)\n", "\n", "def nearest_neighbors(x, bpbs, n=3):\n", " top, sims = [None] * n, [0] * n\n", " for c in bpbs:\n", " best_in_class = 0\n", " j = -1\n", " for y in bpbs[c]:\n", " j += 1\n", " s = similarity(x, y)\n", " for i in range(n):\n", " if s > sims[i] and s > best_in_class:\n", " sims[i] = s\n", " best_in_class = s\n", " top[i] = c\n", " break\n", " return list(zip(top, sims))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Program options\n", "\n", "Parsing the options for our program:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "def parse_args(argv=sys.argv[1:]):\n", " parser = argparse.ArgumentParser(description='Classifier for YouTube video streams')\n", " group = parser.add_argument_group('Classifier options')\n", " modes = set(['learn', 'test'])\n", " parser.add_argument('mode', metavar='mode', choices=modes, nargs=1,\n", " help='Mode to run in. Available: ' + ', '.join(modes))\n", " group.add_argument('-t', '--test', default='', type=str, help='Path to pcap to classify')\n", " group.add_argument('-f', '--force', action='store_true', help='Overwrite existing files without prompting')\n", " group.add_argument('-s', '--setup', action='store_true', help='Perform initial setup of tranalyzer')\n", " return parser.parse_args(argv)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exploring the data\n", "\n", "To understand our data better, we can plot the extracted bursts for each sample and examine them side by side." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "def plot_bursts(bpbs, title=''):\n", " width = 10\n", " cols = 3\n", " rows = (len(bpbs) + cols - 1 ) // cols\n", " fig = plt.figure(figsize=(width, 3 * rows))\n", " ax1 = plt.subplot(rows, cols, 1)\n", " for i, bpb in enumerate(map(list, bpbs), 1):\n", " plt.subplot(rows, cols, i, sharey=ax1)\n", "\n", " # Optional: Filter out audio bursts <=500K.\n", " #bpb = [x for x in bpb if (x > 500*1024)]\n", "\n", " # Optional: Sort for alternative visualization perspective.\n", " #bpb.sort()\n", "\n", " plt.bar(x=range(len(bpb)), height=bpb)\n", " plt.title(title + ' ' + str(i), fontsize=12, fontweight=0)\n", "\n", " plt.tight_layout()\n", " plt.show(block=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Putting it all together\n", "\n", "We first train our model using the training data. Afterwards, the program can be run in test mode and will output the top matches for a new unknown encrypted video stream sample." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "def main(argv=None):\n", " args = parse_args(argv)\n", "\n", " if args.setup or definitely_need_setup_t2():\n", " print('Setting up tranalyzer ... ', end='')\n", " setup_t2()\n", " print('done')\n", "\n", " # run t2 on all PCAPs and extract BPB set for each class\n", " if args.mode[0] == 'learn':\n", " print('Storing learned BPB models to file: {}'.format(BPBS_PATH))\n", " if isfile(BPBS_PATH) and not args.force:\n", " print('WARNING: The model file {} already exists. Overwrite it? [yN] '.format(BPBS_PATH), end='')\n", " if input().lower() != 'y':\n", " print('Exiting.')\n", " sys.exit(1)\n", " classes = [basename(normpath(p)) for p in glob.glob('{}/**/'.format(DATA_PATH), recursive=False)]\n", " classes = classes[:10]\n", " print('Found {} classes, building model now ...'.format(len(classes)))\n", " bpbs = learn(classes, write_to=BPBS_PATH)\n", " print('Done building model, ready to test now.')\n", "\n", " # NOTE: Collected BPB features are stored in a file and are used when this program is\n", " # invoked in \"test\" mode.\n", "\n", " if VERBOSE:\n", " for c in classes[:3]:\n", " print('Bursts for class {}:'.format(c))\n", " plot_bursts(list(bpbs[c]), c) # Plot bursts of whichever class happens to be the first.\n", " else:\n", " if not args.test:\n", " print(\"Missing path to test PCAP file. See help page.\")\n", " sys.exit(1)\n", "\n", " pcap_test = args.test\n", " bpbs = load_bpbs(BPBS_PATH)\n", " bpb_test = extract_bpb(pcap_test)\n", "\n", " print('Bursts of test sample ({}):'.format(basename(pcap_test)))\n", " plot_bursts([bpb_test], 'Test sample')\n", "\n", " top = nearest_neighbors(bpb_test, bpbs, 3)\n", " result = top[0][0]\n", " print('Classification result: {}'.format(result))\n", " print('')\n", " print('(Top 3: {})'.format(top))\n", " plot_bursts(bpbs[result], result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the purposes of this tutorial, let's first train our model, then test it on a new PCAP:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Storing learned BPB models to file: bpbs.data\n", "WARNING: The model file bpbs.data already exists. Overwrite it? [yN] y\n", "Found 10 classes, building model now ...\n", "Extracting bursts for class Jennifer_Lopez_On_The_Floor\n", "Extracting bursts for class Democratic_Town_Hall\n", "Extracting bursts for class Fast_and_Furious_six\n", "Extracting bursts for class Coolio_Gangsters_Paradise\n", "Extracting bursts for class Lenny_Kravitz_American_Woman\n", "Extracting bursts for class Disconnect\n", "Extracting bursts for class Jungle_Book\n", "Extracting bursts for class Robbie_Williams_Supreme\n", "Extracting bursts for class Meghan_Trainor_All_About_That_Bass\n", "Extracting bursts for class fifty_Cent_In_Da_Club\n", "Done building model, ready to test now.\n", "----------------------------------------\n", "Bursts of test sample (Fast_and_Furious_six_Train00_40_30.pcap):\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Classification result: Fast_and_Furious_six\n", "\n", "(Top 3: [('Fast_and_Furious_six', 30), ('Disconnect', 1), ('Jungle_Book', 1)])\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "if __name__ == '__main__':\n", " main(['learn'])\n", "\n", " print('-' * 40)\n", "\n", " test_pcap = f'{DATA_PATH}/Fast_and_Furious_six/Test/Fast_and_Furious_six_Train00_40_30.pcap'\n", " main(['test', '-t', test_pcap])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion\n", "\n", "We wrote a short python program to build and train a nearest neighbor model to classify encrypted YouTube video streams using Tranalyzer.\n", "\n", "Download the jupyter notebook for this tutorial [here](/download/data/bpb-classifier.ipynb).\n", "\n", "If you have any questions or feedback, please do not hesitate to contact us!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# References\n", "\n", "[1] R. Dubin, A. Dvir, O. Pele and O. Hadar, \"I Know What You Saw Last Minute—Encrypted HTTP Adaptive Video Streaming Title Classification,\" in IEEE Transactions on Information Forensics and Security, vol. 12, no. 12, pp. 3039-3049, Dec. 2017. doi: 10.1109/TIFS.2017.2730819 https://ieeexplore.ieee.org/document/7987775" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.1" } }, "nbformat": 4, "nbformat_minor": 2 }