Browse Source

Created utility specifically to find a matching pcap file for a keylog

It will not match packets, leaving that up to a seperate utility.
Thomas Flucke 6 năm trước cách đây
mục cha
commit
5e60adbeb2

+ 4 - 0
.gitignore

@@ -8,3 +8,7 @@ data/*/
 *.a
 *.so
 *.pdf
+src/flow-seperator/flow-seperator
+src/pcap-matcher/pcap-matcher
+src/packet-matcher/packet-matcher
+src/common/*.a

+ 7 - 1
src/common/Makefile

@@ -1,4 +1,4 @@
-TARGETS=libtimecap.a
+TARGETS=liblinkedlist.a libtimecap.a
 CC=gcc
 AR=ar
 CCFLAGS=-Wall -g
@@ -14,6 +14,12 @@ libtimecap.a: $(ODIR)/timecap.o
 $(ODIR)/timecap.o: timecap.c timecap.h $(ODIR)
 	$(CC) -c $(CCFLAGS) $< -o $@
 
+liblinkedlist.a: $(ODIR)/linkedList.o
+	$(AR) $(ARFLAGS) $@ $<
+
+$(ODIR)/linkedList.o: linkedList.c linkedList.h $(ODIR)
+	$(CC) -c $(CCFLAGS) $< -o $@
+
 $(ODIR):
 	mkdir $(ODIR)
 

+ 0 - 0
src/packet-matcher/linkedList.c → src/common/linkedList.c


+ 0 - 0
src/packet-matcher/linkedList.h → src/common/linkedList.h


+ 42 - 3
src/common/timecap.c

@@ -1,26 +1,58 @@
 #include"timecap.h"
 #include<stdlib.h>
+#include<stdbool.h>
+
+static int set_filter(TimeCap* self) {
+  int err = pcap_compile(self->pcap, &self->_filter, FILTER_KEY_PKT, true,
+                         PCAP_NETMASK_UNKNOWN);
+  if (-1 == err) {
+    return -1;
+  }
+  pcap_setfilter(self->pcap, &self->_filter);
+  return 0;
+}
+
+static ssize_t count_packets(TimeCap* self) {
+  struct pcap_pkthdr* header;
+  const u_char* buf;
+  size_t i = 0;
+  if (-1 == set_filter(self)) {
+    return -1;
+  }
+  while (0 < pcap_next_ex(self->pcap, &header, &buf)) {
+    ++i;
+  }
+  pcap_freecode(&self->_filter);
+  return i;
+}
 
 TimeCap* new_timecap(char* filename, char* errBuf) {
   struct pcap_pkthdr* header;
   const u_char* buf;
   TimeCap* cap = malloc(sizeof(TimeCap));
-  if (cap == NULL) {
+  if (NULL == cap) {
     perror("malloc");
     exit(1);
   }
   cap->pcap = pcap_open_offline(filename, errBuf);
-  if (cap->pcap == NULL) {
+  if (NULL == cap->pcap) {
     free(cap);
     return NULL;
   }
-  if (pcap_next_ex(cap->pcap, &header, &buf) == -1) {
+  if (-1 == pcap_next_ex(cap->pcap, &header, &buf)) {
     sprintf(errBuf, NO_PACKET_ERR, filename, pcap_geterr(cap->pcap));
     pcap_close(cap->pcap);
     free(cap);
     return NULL;
   }
   cap->time = header->ts;
+  cap->pack_count = count_packets(cap);
+  if (-1 == cap->pack_count) {
+    pcap_perror(cap->pcap, NULL);
+    pcap_close(cap->pcap);
+    free(cap);
+    return NULL;
+  }
   pcap_close(cap->pcap);
   cap->pcap = pcap_open_offline(filename, errBuf);
   if (cap->pcap == NULL) {
@@ -28,6 +60,12 @@ TimeCap* new_timecap(char* filename, char* errBuf) {
     return NULL;
   }
   cap->name = filename;
+  if (-1 == set_filter(cap)) {
+    pcap_perror(cap->pcap, NULL);
+    pcap_close(cap->pcap);
+    free(cap);
+    return NULL;
+  }
   return cap;
 }
 
@@ -47,6 +85,7 @@ int cmp_timecap(const void* a, const void* b) {
 
 void delete_timecap(void* val) {
   TimeCap* cap = val;
+  pcap_freecode(&cap->_filter);
   pcap_close(cap->pcap);
   free(cap);
 }

+ 12 - 0
src/common/timecap.h

@@ -4,12 +4,24 @@
 #include<pcap/pcap.h>
 #include <sys/time.h>
 
+/* Filter for packets (see pcap-filter(7))
+ * 
+ * tcp[13] & 8 == 8: PSH tcp flag is set
+ * dst port 22: Going to an SSH port
+ * len % 8 == 6: Packet length is in the 8 byte intervals offset by 6.
+ * For some reason (likely for block ciphers) the length of key the packets are
+ * always this length.
+ */
+#define FILTER_KEY_PKT "tcp[13] & 8 == 8 and dst port 22 and len % 8 == 6"
+
 #define NO_PACKET_ERR "Failed to read any packets from file, \"%s\".\n\t%s"
 
 typedef struct {
+  struct bpf_program _filter;
   struct timeval time;
   pcap_t* pcap;
   char* name;
+  size_t pack_count;
 } TimeCap;
 
 TimeCap* new_timecap(char* filename, char* errBuf);

BIN
src/flow-seperator/flow-seperator


+ 1 - 1
src/packet-matcher/Makefile

@@ -2,7 +2,7 @@ TARGET=packet-matcher
 CC=gcc
 LIBS=../common
 CCFLAGS=-Wall -g -I$(LIBS)
-LDFLAGS=-lpcap -lm -ltimecap -L$(LIBS)
+LDFLAGS=-lpcap -lm -ltimecap -llinkedlist -L$(LIBS)
 
 ODIR=obj
 

BIN
src/packet-matcher/packet-matcher


+ 19 - 0
src/pcap-matcher/Makefile

@@ -0,0 +1,19 @@
+TARGET=pcap-matcher
+CC=gcc
+LIBS=../common
+CCFLAGS=-Wall -g -I$(LIBS)
+LDFLAGS=-lm -ltimecap -lpcap -llinkedlist -L$(LIBS)
+
+ODIR=obj
+
+SOURCES = $(wildcard *.c)
+OBJECTS = $(patsubst %.c, $(ODIR)/%.o, $(SOURCES))
+HEADERS = $(wildcard *.h)
+
+default: $(TARGET)
+
+$(TARGET): $(SOURCES)
+	$(CC) $(CCFLAGS) $^ $(LDFLAGS) -o $@ -D"PROG_NAME=\"$@\""
+
+clean:
+	rm -f $(TARGET) $(ODIR)/*.o *~

+ 223 - 0
src/pcap-matcher/main.c

@@ -0,0 +1,223 @@
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+#include<pcap/pcap.h>
+#include<stdbool.h>
+#include<inttypes.h>
+#include<netinet/ether.h>
+#include<arpa/inet.h>
+#include"linkedList.h"
+#include"timecap.h"
+
+#ifndef PROG_NAME
+#define PROG_NAME "a.out"
+#endif
+
+// Seconds
+#define KEY_EPS 1
+
+#define SSH_SETUP_PACKET_COUNT 4
+
+#define FLAG_SHORT_HELP "-h"
+#define FLAG_LONG_HELP  "--help"
+
+#define FLAG_SHORT_DIFF "-t"
+#define FLAG_LONG_DIFF  "--time-diff"
+
+#define FLAG_SHORT_LENG "-l"
+#define FLAG_LONG_LENG  "--len-diff"
+
+/* Format of keylog lines in log file.
+ * 4 = the fd SSH reads user input from (don't know why it isn't 0 but it isn't)
+ * 16384 = How many bytes SSH reads as a time
+ * The last %c is there to check that the entire string matched correctly.
+ * This does not read the entire line.  Call read_line after using this.
+ */
+#define KEYLOG_FORMAT "%lf read(%*d, \"%m[^\"]\", 16384) %c"
+
+#define S_TO_NS 1000000000
+#define S_TO_US 1000000
+
+#define DEFAULT_SYN_E {3, 0}
+
+#define DEFAULT_TIM_E 20
+
+typedef struct {
+  FILE* keylog;
+  char* keylogName;
+  struct timeval keylogStart;
+  List* timecaps;
+  struct timeval synEpsiolon;
+  size_t keyEpsilon;
+} Args;
+
+void check_error(int cond, char* str) {
+  if (cond) {
+    perror(str);
+    exit(1);
+  }
+}
+
+void print_usage(int exitCode) {
+  printf("Usage: %s [options] keylog pcap [pcap ...]\n"
+         "Pair a keylog to a SSH TCP stream.\n\n"
+         "Options:\n"
+         "%-20s Show this help message and exit\n"
+         "%-20s The allowable difference in time (in seconds) between keylog \n"
+         "%-20s start and flow start\n"
+         "%-20s The allowable difference in estimated keys between keylog and\n"
+         "%-20s flow \n",
+         PROG_NAME,
+         "  "FLAG_SHORT_HELP", "FLAG_LONG_HELP,
+         "  "FLAG_SHORT_DIFF", "FLAG_LONG_DIFF, "",
+         "  "FLAG_SHORT_LENG", "FLAG_LONG_LENG, "");
+  exit(exitCode);
+}
+
+struct timeval double_to_timeval(double in) {
+  struct timeval time;
+  time.tv_sec = in;
+  time.tv_usec = (in - time.tv_sec) * S_TO_US;
+  return time;
+}
+
+void parse_flag(char** argv, Args* res, size_t* i) {
+  double d;
+  char* arg = argv[*i];
+  if (strcmp(arg, FLAG_SHORT_HELP) == 0 || strcmp(arg, FLAG_LONG_HELP) == 0) {
+    print_usage(0);
+  }
+  else if (strcmp(arg, FLAG_SHORT_DIFF) == 0 ||
+           strcmp(arg, FLAG_LONG_DIFF) == 0) {
+    if (sscanf(argv[++*i], " %lf", &d) == 1 && d > 0) {
+      res->synEpsiolon = double_to_timeval(d);
+    }
+    else {
+      print_usage(1);
+    }
+  }
+  else if (0 == strcmp(arg, FLAG_SHORT_LENG) ||
+           0 == strcmp(arg, FLAG_LONG_LENG)) {
+    if (1 != sscanf(argv[++*i], " %zu", &res->keyEpsilon)) {
+      print_usage(1);
+    }
+  }
+}
+
+/* Assumes each pcap has at least one packet and each pcap doesn't overlap.
+ */
+static void add_timecap(List* pcaps, char* filename) {
+  char* errBuf  = alloca(PCAP_ERRBUF_SIZE + strlen(filename) +
+                         sizeof(NO_PACKET_ERR));
+  TimeCap* cap = new_timecap(filename, errBuf);
+  if (cap == NULL) {
+    fprintf(stderr, "%s: %s\n", filename, errBuf);
+  }
+  else {
+    list_add_head(pcaps, cap);
+  }
+}
+
+static int read_line(FILE* keylog) {
+  int c;
+  while ('\n' != (c = fgetc(keylog)) && EOF != c) {
+    continue;
+  }
+  return EOF == c? EOF : 0;
+}
+
+static struct timeval get_keylog_start(FILE* keylog) {
+  double seconds;
+  if (fscanf(keylog, " %lf", &seconds) != 1) {
+    fprintf(stderr, "Failed to read timestame from keylog file.\n");
+    exit(1);
+  }
+  read_line(keylog);
+  return double_to_timeval(seconds);
+}
+
+static Args get_args(int argc, char** argv) {
+  Args res = {NULL, NULL, {0, 0}, new_list(NULL), DEFAULT_SYN_E, DEFAULT_TIM_E};
+  size_t i = 1;
+  while (argv[i]) {
+    if (argv[i][0] == '-') {
+      parse_flag(argv, &res, &i);
+    }
+    else if (res.keylog) {
+      add_timecap(res.timecaps, argv[i]);
+    }
+    else {
+      res.keylog = fopen(argv[i], "r");
+      res.keylogName = argv[i];
+      check_error(res.keylog == NULL, argv[i]);
+      res.keylogStart = get_keylog_start(res.keylog);
+    }
+    ++i;
+  }
+  if (res.keylog == NULL || res.timecaps == NULL) {
+    print_usage(1);
+  }
+  return res;
+}
+
+void timeval_subtract(struct timeval *result,
+                      struct timeval *x,
+                      struct timeval *y) {
+  *result = *x;
+  if (result->tv_usec < y->tv_usec) {
+    result->tv_sec -= 1;
+    result->tv_usec += 1*S_TO_US;
+  }
+  result->tv_sec -= y->tv_sec;
+  result->tv_usec -= y->tv_usec;
+  result->tv_sec = abs(result->tv_sec);
+  result->tv_usec = abs(result->tv_usec);
+}
+
+int timercmp_eps(struct timeval* a, struct timeval* b, struct timeval eps) {
+  struct timeval diff;
+  timeval_subtract(&diff, a, b);
+  return timercmp(&diff, &eps, <);
+}
+
+size_t count_lines(FILE* file) {
+  size_t res = 1;
+  while (EOF != read_line(file)) {
+    ++res;
+  }
+  return res;
+}
+
+int is_match(void* tc, void* a) {
+  TimeCap* cap = (TimeCap*) tc;
+  Args* args = (Args*) a;
+  size_t line_count;
+  if (timercmp_eps(&args->keylogStart, &cap->time, args->synEpsiolon)) {
+    line_count = count_lines(args->keylog);
+    return line_count <= cap->pack_count + args->keyEpsilon &&
+      line_count >= cap->pack_count - args->keyEpsilon;
+  }
+  else {
+    return 0;
+  }
+}
+
+void cleanup(Args args) {
+  delete_list(args.timecaps, delete_timecap);
+  fclose(args.keylog);
+}
+
+int main(int argc, char** argv) {
+  Args args = get_args(argc, argv);
+  TimeCap* cap = (TimeCap*) list_find(args.timecaps, &args, is_match);
+  int exitStatus = 0;
+  if (cap != NULL) {
+    printf("%s\n", cap->name);
+  }
+  else {
+    fprintf(stderr, "Failed to find matching flow file for %s.\n",
+            args.keylogName);
+  }
+  cleanup(args);
+  return exitStatus;
+}

+ 57 - 0
systems/setup.md

@@ -0,0 +1,57 @@
+# Participant Setup Procedures
+
+## Set up VM
+
+1. Open VMWare Player app
+2. Open existing Virtual Machine
+3. Navigate to and select `/opt/Thesis-mini.ova`
+4a. *14-30{1,2}* Rename directory from `/home/...` to `/tmp/...`
+4b. *14-303* Rename directory from `/home/...` to `/vm/...`
+5. Hit Retry when prompted
+6. Once the program finishes importing, Boot the Thesis-Mini
+7. Hit Continue when prompted
+
+## Using VM
+
+*Note: All SSH connections to outside the VM will take up 2 minutes to establish
+for unknown reasons.*
+
+### Direct Usage
+
+There is a terminal available as the green icon in the bottom-right corner.
+From there you can SSH into another computer as per normal.
+
+### Indirect Usage
+
+Each VM will have an IP with is accessible from the host O.S.  Usually this will be
+`172.16.98.128`.
+To be sure, from the host O.S. open a terminal and type, `ip addr` and look for
+`vmnet1`.  Replace the last octal of that interface's IP with `128` and that is
+the address of the VM.
+
+Using this IP address, you can connect to the VM with:
+```Shell
+ssh volenteer@172.16.98.128
+```
+And the password, `password123`.  Once connected participants can SSH out to other
+machines as normal.
+
+## SSHing from the VM
+
+Each SSH session will start with a prompt asking if this is a guided session.  If
+participants are not following the provided guide, they should type `n`.  If they
+are, they should type `y`.
+
+Next the shell will warn them not to enter passwords into the session.  The first
+password they enter to establish the connection will not be captured.  Importantly,
+afterwards we cannot filter out passwords afterwards.  Thus far avoiding entering
+any has not been a problem.
+
+Finally, participants must properly close the SSH session in order to complete the
+connection __(They should not hit the X to exit)__.  They can do this by typing
+`exit` or `C-d`.
+
+Once they have exited the VM will show them the log of their recorded keys.  Once
+they review the logs and feel satisfied, they can type `q` to continue.  The VM
+will then confirm that they want to submit the logs which they can respond with
+either `y` or `n` to continue or withdraw respectively.