dsniff  2.4b2
About: A collection of tools for network auditing
  Fossies Dox: dsniff-2.4b2.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

sshow.c
Go to the documentation of this file.
1 /*
2  * sshow.c
3  *
4  * Passive SSH traffic analysis.
5  *
6  * http://www.openwall.com/advisories/OW-003-ssh-traffic-analysis.txt
7  *
8  * Copyright (c) 2000-2001 Solar Designer <solar@openwall.com>
9  * Copyright (c) 2000 Dug Song <dugsong@monkey.org>
10  *
11  * $Id: sshow.c,v 1.2 2001/03/19 06:52:15 dugsong Exp $
12  */
13 
14 #include "config.h"
15 
16 #include <sys/types.h>
17 #include <sys/times.h>
18 
19 #include <netinet/in_systm.h>
20 #include <netinet/in.h>
21 #include <netinet/ip.h>
22 #include <netinet/tcp.h>
23 #include <arpa/inet.h>
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <err.h>
33 #include <nids.h>
34 #include <pcap.h>
35 
36 #include "pcaputil.h"
37 
38 #if !defined(NIDS_MAJOR) || (NIDS_MAJOR == 1 && NIDS_MINOR < 15)
39 #error This program requires libnids 1.15+
40 #endif
41 
42 #define HISTORY_SIZE 16
43 #define CLK_TCK 1
44 typedef struct {
45  u_int min, max;
46 } range;
47 
48 typedef struct {
49  int direction; /* 0 for client to server */
50  clock_t timestamp; /* timestamp of this packet */
51  u_int cipher_size; /* ciphertext size */
52  range plain_range; /* possible plaintext sizes */
53 } record;
54 
55 struct history {
56  record packets[HISTORY_SIZE]; /* recent packets (circular list) */
57  int index; /* next (free) index into packets[] */
58  u_int directions; /* recent directions (bitmask) */
59  clock_t timestamps[2]; /* last timestamps in each direction */
60 };
61 
62 struct line {
63  int input_count; /* input packets (client to server) */
64  int input_size; /* input size (estimated) */
65  int input_last; /* last input packet size */
66  int echo_count; /* echo packets (server to client) */
67 };
68 
69 struct session {
70  int protocol; /* -1 not SSH, 0 unknown, 1 or 2 once known */
71  int state; /* 1 after username, 2 after authentication */
72  int compressed; /* whether compression is known to be used */
73  struct history history; /* session history */
74  struct line line; /* current command line */
75 };
76 
77 static int debug = 0;
78 
79 static clock_t now;
80 
81 static void
82 usage(void)
83 {
84  fprintf(stderr, "Usage: sshow [-d] [-i interface]\n");
85  exit(1);
86 }
87 
88 static clock_t
89 add_history(struct session *session, int direction,
90  u_int cipher_size, range *plain_range)
91 {
92  record *current;
93  clock_t delay;
94 
95  current = &session->history.packets[session->history.index++];
97 
98  current->direction = direction;
99  current->timestamp = now;
100  current->cipher_size = cipher_size;
101  current->plain_range = *plain_range;
102 
103  session->history.directions <<= 1;
104  session->history.directions |= direction;
105 
106  delay = now - session->history.timestamps[direction];
107  session->history.timestamps[direction] = now;
108 
109  return (delay);
110 }
111 
112 static record *
113 get_history(struct session *session, int age)
114 {
115  int index;
116 
117  index = session->history.index + (HISTORY_SIZE - 1) - age;
118  index %= HISTORY_SIZE;
119 
120  return (&session->history.packets[index]);
121 }
122 
123 static char *
124 s_saddr(struct tcp_stream *ts)
125 {
126  static char output[32];
127 
128  snprintf(output, sizeof(output), "%s:%u",
129  inet_ntoa(*((struct in_addr *)&ts->addr.saddr)),
130  ts->addr.source);
131  return (output);
132 }
133 
134 static char *
135 s_daddr(struct tcp_stream *ts)
136 {
137  static char output[32];
138 
139  snprintf(output, sizeof(output), "%s:%u",
140  inet_ntoa(*((struct in_addr *)&ts->addr.daddr)),
141  ts->addr.dest);
142  return (output);
143 }
144 
145 static char *
147 {
148  static char output[32];
149 
150  snprintf(output, sizeof(output),
151  range->min == range->max ? "%u" : "%u to %u",
152  range->min, range->max);
153  return (output);
154 }
155 
156 static void
157 print_data(struct half_stream *stream, u_int count)
158 {
159  u_int i;
160  int printable;
161 
162  printable = 1;
163  for (i = 0; i < count; i++) {
164  printf("%02x%c", (int)(u_char)stream->data[i],
165  i < count - 1 && i % 24 != 23
166  ? ' ' : '\n');
167  printable &=
168  isprint(stream->data[i]) ||
169  stream->data[i] == '\n';
170  }
171  if (printable && count >= 4 && !memcmp(stream->data, "SSH-", 4))
172  fwrite(stream->data, count, 1, stdout);
173 }
174 
175 static u_int
176 ssh1_plain_size(struct half_stream *stream)
177 {
178  if (stream->count_new < 4) return (0);
179 
180  return (u_int)(u_char)stream->data[3] |
181  ((u_int)(u_char)stream->data[2] << 8) |
182  ((u_int)(u_char)stream->data[1] << 16) |
183  ((u_int)(u_char)stream->data[0] << 24);
184 }
185 
186 static u_int
187 ssh1_cipher_size(struct half_stream *stream)
188 {
189  return (4 + ((ssh1_plain_size(stream) + 8) & ~7));
190 }
191 
192 static range *
193 ssh1_plain_range(struct half_stream *stream)
194 {
195  static range output;
196 
197  output.min = output.max = ssh1_plain_size(stream) - 5;
198  return (&output);
199 }
200 
201 static range *
202 ssh2_plain_range(struct half_stream *stream)
203 {
204  static range output;
205 
206  output.max = stream->count_new - 16;
207  /* Assume min padding + 8-byte cipher blocksize */
208  output.min = output.max - 7;
209  if ((int)output.min < 0) output.min = 0;
210  return (&output);
211 }
212 
213 static void
214 client_to_server(struct tcp_stream *ts, struct session *session,
215  u_int cipher_size, range *plain_range)
216 {
217  clock_t delay;
218  int payload;
219 
220  delay = add_history(session, 0, cipher_size, plain_range);
221 
222  if (debug)
223  printf("- %s -> %s: DATA (%s bytes, %.2f seconds)\n",
224  s_saddr(ts), s_daddr(ts), s_range(plain_range),
225  (float)delay / CLK_TCK);
226  if (debug > 1)
227  print_data(&ts->server, cipher_size);
228 
229  payload = plain_range->min;
230  if (session->state == 2 && payload > 0) {
232  session->line.input_last = payload;
233  if (session->protocol == 1)
234  payload -= 4;
235  else {
236  payload -= 20 + 1;
237  /* Assume several SSH-2 packets in this IP packet */
238  if (payload % 44 == 0) {
239  session->line.input_count += payload / 44;
240  /* One character per SSH-2 packet (typical) */
241  payload += payload / 44;
242  payload %= 44;
243  }
244  payload++;
245  }
246  if (payload <= 0) {
247  if (payload < 0 && !session->compressed &&
248  session->protocol == 1) {
249  session->compressed = 1;
250  printf("+ %s -> %s: Compression detected, "
251  "guesses will be much less reliable\n",
252  s_saddr(ts), s_daddr(ts));
253  }
254  payload = 1;
255  }
256  session->line.input_size += payload;
257  }
258 }
259 
260 static void
261 server_to_client(struct tcp_stream *ts, struct session *session,
262  u_int cipher_size, range *plain_range)
263 {
264  clock_t delay;
265  int skip;
266  range string_range;
267 
268  delay = add_history(session, 1, cipher_size, plain_range);
269 
270  if (debug)
271  printf("- %s <- %s: DATA (%s bytes, %.2f seconds)\n",
272  s_saddr(ts), s_daddr(ts), s_range(plain_range),
273  (float)delay / CLK_TCK);
274  if (debug > 1)
275  print_data(&ts->client, cipher_size);
276 
277 /*
278  * Some of the checks may want to skip over multiple server responses.
279  * For example, there's a debugging packet sent for every option found
280  * in authorized_keys, but we can't use those packets in our pattern.
281  */
282  skip = 0;
283  while (((session->history.directions >> skip) & 3) == 3)
284  if (++skip > HISTORY_SIZE - 5) break;
285 
286  if (session->state == 0 &&
287  session->protocol == 1 &&
288  ((session->history.directions >> skip) & 7) == 5 &&
289  plain_range->min == 0 &&
290  get_history(session, skip + 1)->plain_range.min > 4 &&
291  get_history(session, skip + 2)->plain_range.min == 0) {
292  session->state = 1;
293  string_range = get_history(session, skip + 1)->plain_range;
294  string_range.min -= 4; string_range.max -= 4;
295  printf("+ %s -> %s: GUESS: Username length is %s\n",
296  s_saddr(ts), s_daddr(ts), s_range(&string_range));
297  return;
298  }
299 
300  if (session->state == 1 &&
301 #ifdef USE_TIMING
303 #endif
304  session->protocol == 1 &&
305  (session->history.directions & 7) == 5 &&
306  plain_range->min == 0 &&
307  get_history(session, 1)->plain_range.min > 4 &&
308  get_history(session, 2)->plain_range.min == 0) {
309  session->state = 2;
310  string_range = get_history(session, 1)->plain_range;
311  string_range.min -= 4; string_range.max -= 4;
312  printf("+ %s -> %s: GUESS: Password authentication, "
313  "password length %s %s%s\n",
314  s_saddr(ts), s_daddr(ts),
315  string_range.min == 32 ? "appears to be" : "is",
316  s_range(&string_range),
317  string_range.min == 32 ? " (padded?)" : "");
318  }
319 
320  if (session->state == 0 &&
321  session->protocol == 2 &&
322  (session->history.directions & 7) == 5) {
323  if (plain_range->min == 4 + 9) {
324  string_range = get_history(session, 1)->plain_range;
325 
326  if (string_range.min > 500 && string_range.min < 600) {
327  session->state = 2;
328  printf("+ %s -> %s: GUESS: DSA "
329  "authentication accepted\n",
330  s_saddr(ts), s_daddr(ts));
331  } else
332  if (string_range.min > 42 + 9) {
333  session->state = 2;
334  printf("+ %s -> %s: GUESS: Password "
335  "authentication accepted\n",
336  s_saddr(ts), s_daddr(ts));
337  }
338  } else if (plain_range->min > 12 + 9 &&
339  plain_range->min < 56 + 9) {
340  string_range = get_history(session, 1)->plain_range;
341 
342  if (string_range.min > 500 && string_range.min < 600)
343  printf("+ %s -> %s: GUESS: DSA "
344  "authentication failed\n",
345  s_saddr(ts), s_daddr(ts));
346  else if (string_range.min > 42 + 9)
347  printf("+ %s -> %s: GUESS: Password "
348  "authentication failed\n",
349  s_saddr(ts), s_daddr(ts));
350  }
351  }
352 
353  if (session->state == 1 &&
354  session->protocol == 1 &&
355  (session->history.directions & 3) == 1 &&
356  plain_range->min == 0 &&
357  get_history(session, 1)->plain_range.min == 130) {
358  printf("+ %s -> %s: GUESS: RSA authentication refused\n",
359  s_saddr(ts), s_daddr(ts));
360  }
361 
362  if (session->state == 1 &&
363  session->protocol == 1 &&
364  skip >= 1 &&
365  ((session->history.directions >> (skip - 1)) & 037) == 013 &&
366  plain_range->min == 0 &&
367  get_history(session, skip - 1 + 2)->plain_range.min == 16 &&
368  get_history(session, skip - 1 + 3)->plain_range.min == 130 &&
369  get_history(session, skip - 1 + 4)->plain_range.min == 130) {
370  char *what;
371 
372  switch (get_history(session, 1)->plain_range.min - 4) {
373  case 28:
374  /* "RSA authentication accepted." */
375  session->state = 2;
376  if (skip > 1 && (what = alloca(64))) {
377  snprintf(what, 64, "accepted "
378  "(%d+ authorized_keys option%s)",
379  skip - 1, skip - 1 == 1 ? "" : "s");
380  break;
381  }
382  what = "accepted";
383  break;
384 
385  case 47:
386  /* "Wrong response to RSA authentication challenge." */
387  what = "failed";
388  break;
389 
390  default:
391  what = "???";
392  }
393  printf("+ %s -> %s: GUESS: RSA authentication %s\n",
394  s_saddr(ts), s_daddr(ts), what);
395  }
396 
397  if (session->state == 2) {
399 
400  /* Check for backspace */
401  if (session->protocol == 1 && !session->compressed &&
402  plain_range->min == 4 + 3 &&
403  session->line.input_size >= 2)
404  session->line.input_size -= 2;
405 
406  if (plain_range->min > 4 + session->line.input_last &&
407  session->line.input_count >= 2 &&
408  session->line.input_size >= 2) {
409  int size;
410  char *what;
411 
412  size = session->line.input_size;
413  if (session->line.echo_count + 1 >=
415  size <= (session->line.input_count << 2) &&
416  size < 0x100) {
417  what = "(command) line";
418  }
419  else {
420  if (session->line.echo_count <= 2 &&
421  size <= (session->line.input_count << 1) &&
422  size >= 2 + 1 && size <= 40 + 1) {
423  what = "password";
424  }
425  else what = NULL;
426  }
427  if (debug) {
428  printf("- %s -> %s: sent %d packets "
429  "(%d characters), seen %d replies\n",
430  s_saddr(ts), s_daddr(ts),
431  session->line.input_count, size,
433  }
434  if (what) {
435  printf("+ %s -> %s: GUESS: "
436  "a %s of %d character%s\n",
437  s_saddr(ts), s_daddr(ts),
438  what, size - 1, size == 2 ? "" : "s");
439  }
440  }
441  if (plain_range->min <= 0 ||
442  plain_range->min > 4 + session->line.input_last) {
443  session->line.input_count = 0;
444  session->line.input_size = 0;
445  session->line.echo_count = 0;
446  }
447  }
448 }
449 
450 static void
451 process_data(struct tcp_stream *ts, struct session *session)
452 {
453  u_int have, need;
454  char *lf;
455 
456  if (session->protocol < 0) return;
457 
458  if (ts->client.count_new &&
459  (have = ts->client.count - ts->client.offset)) {
460  switch (session->protocol) {
461  case 1:
462  if (have < (need = ssh1_cipher_size(&ts->client))) {
463  if (debug) {
464  printf("- %s <- %s: got %u of "
465  "%u bytes\n", s_saddr(ts),
466  s_daddr(ts), have, need);
467  }
468  nids_discard(ts, 0);
469  return;
470  }
471  if (have != need && debug) {
472  printf("- %s <- %s: left %u bytes\n",
473  s_saddr(ts), s_daddr(ts),
474  have - need);
475  }
476  nids_discard(ts, need);
477  server_to_client(ts, session, need,
478  ssh1_plain_range(&ts->client));
479  return;
480 
481  case 2:
482  server_to_client(ts, session, have,
483  ssh2_plain_range(&ts->client));
484  return;
485 
486  default:
487  break;
488  }
489  }
490  if (ts->server.count_new &&
491  (have = ts->server.count - ts->server.offset)) {
492  if (!session->protocol) {
493  lf = (char *)memchr(ts->server.data, '\n', have);
494  if (have < 7 || (!lf && have < 0x100)) {
495  nids_discard(ts, 0);
496  return;
497  }
498  if (lf && !memcmp(ts->server.data, "SSH-", 4))
499  session->protocol = ts->server.data[4] - '0';
500  /* some clients announce SSH-1.99 instead of SSH-2.0 */
501  if (session->protocol == 1 &&
502  ts->server.data[5] == '.' &&
503  ts->server.data[6] == '9') {
504  session->protocol = 2;
505  }
506  if (session->protocol != 1 && session->protocol != 2) {
507  session->protocol = -1;
508  if (debug) {
509  printf("- %s -> %s: not SSH\n",
510  s_saddr(ts), s_daddr(ts));
511  }
512  return;
513  }
514  need = lf - ts->server.data + 1;
515  nids_discard(ts, need);
516  printf("+ %s -> %s: SSH protocol %d\n",
517  s_saddr(ts), s_daddr(ts), session->protocol);
518  if (debug)
519  print_data(&ts->server, have);
520  return;
521  }
522 
523  switch (session->protocol) {
524  case 1:
525  if (have < (need = ssh1_cipher_size(&ts->server))) {
526  if (debug) {
527  printf("- %s -> %s: got %u of "
528  "%u bytes\n", s_saddr(ts),
529  s_daddr(ts), have, need);
530  }
531  nids_discard(ts, 0);
532  return;
533  }
534  if (have != need && debug) {
535  printf("- %s -> %s: left %u bytes\n",
536  s_saddr(ts), s_daddr(ts),
537  have - need);
538  }
539  nids_discard(ts, need);
540  client_to_server(ts, session, need,
541  ssh1_plain_range(&ts->server));
542  return;
543 
544  case 2:
545  client_to_server(ts, session, have,
546  ssh2_plain_range(&ts->server));
547  }
548  }
549 }
550 
551 static void
552 process_event(struct tcp_stream *ts, struct session **session)
553 {
554  struct tms buf;
555  char *what;
556 
557  now = times(&buf);
558  what = NULL;
559 
560  switch (ts->nids_state) {
561  case NIDS_JUST_EST:
562  ts->client.collect = 1;
563  ts->server.collect = 1;
564  if (debug) {
565  printf("- %s -> %s: ESTABLISHED\n",
566  s_saddr(ts), s_daddr(ts));
567  }
568  if (!(*session = calloc(1, sizeof(**session)))) {
569  err(1, "calloc");
570  }
571  (*session)->history.timestamps[0] = now;
572  (*session)->history.timestamps[1] = now;
573  return;
574 
575  case NIDS_CLOSE:
576  what = "CLOSED";
577 
578  case NIDS_RESET:
579  if (!what) what = "RESET";
580 
581  case NIDS_TIMED_OUT:
582  if (!what) what = "TIMED OUT";
583  if ((*session)->protocol > 0) {
584  printf("+ %s -- %s: %s\n",
585  s_saddr(ts), s_daddr(ts), what);
586  }
587  else if (debug) {
588  printf("- %s -- %s: %s\n",
589  s_saddr(ts), s_daddr(ts), what);
590  }
591  free(*session);
592  return;
593 
594  case NIDS_DATA:
595  process_data(ts, *session);
596  return;
597  }
598 }
599 
600 static void
601 null_syslog(int type, int errnum, struct ip *iph, void *data)
602 {
603 }
604 
605 static void
606 cleanup(int signum)
607 {
608  exit(0); /* Just so that atexit(3) jobs are called */
609 }
610 
611 int
612 main(int argc, char *argv[])
613 {
614  extern char *optarg;
615  extern int optind;
616  int c;
617 
618  while ((c = getopt(argc, argv, "di:h?")) != -1) {
619  switch (c) {
620  case 'd':
621  debug++;
622  break;
623  case 'i':
624  nids_params.device = optarg;
625  break;
626  default:
627  usage();
628  break;
629  }
630  }
631  argc -= optind;
632  argv += optind;
633 
634  signal(SIGTERM, cleanup);
635  signal(SIGINT, cleanup);
636  signal(SIGHUP, cleanup);
637 
638  setlinebuf(stdout);
639 
640  if (argc > 0) {
641  nids_params.pcap_filter = copy_argv(argv);
642  }
643  else nids_params.pcap_filter = "tcp";
644 
645  nids_params.syslog = null_syslog;
646  nids_params.scan_num_hosts = 0;
647  nids_params.one_loop_less = 1;
648 
649  if (!nids_init())
650  errx(1, "nids_init: %s", nids_errbuf);
651 
652  nids_register_tcp(process_event);
653 
654  if (nids_params.pcap_filter != NULL) {
655  warnx("listening on %s [%s]", nids_params.device,
656  nids_params.pcap_filter);
657  }
658  else warnx("listening on %s", nids_params.device);
659 
660  nids_run();
661 
662  return (0);
663 }
line::input_last
int input_last
Definition: sshow.c:65
session::line
struct line line
Definition: sshow.c:74
range::max
u_int max
Definition: sshow.c:45
warnx
void warnx(const char *fmt,...)
Definition: err.c:89
record::plain_range
range plain_range
Definition: sshow.c:52
ssh1_cipher_size
static u_int ssh1_cipher_size(struct half_stream *stream)
Definition: sshow.c:187
record
Definition: sshow.c:48
CLK_TCK
#define CLK_TCK
Definition: sshow.c:43
now
static clock_t now
Definition: sshow.c:79
debug
static int debug
Definition: sshow.c:77
s_saddr
static char * s_saddr(struct tcp_stream *ts)
Definition: sshow.c:124
pcaputil.h
history::directions
u_int directions
Definition: sshow.c:58
history::packets
record packets[16]
Definition: sshow.c:56
history
Definition: sshow.c:55
s_range
static char * s_range(range *range)
Definition: sshow.c:146
timestamp
static char * timestamp(void)
Definition: msgsnarf.c:52
history::timestamps
clock_t timestamps[2]
Definition: sshow.c:59
line::input_count
int input_count
Definition: sshow.c:63
cleanup
static void cleanup(int signum)
Definition: sshow.c:606
session::state
int state
Definition: sshow.c:71
session::history
struct history history
Definition: sshow.c:73
history::index
int index
Definition: sshow.c:57
ssh1_plain_range
static range * ssh1_plain_range(struct half_stream *stream)
Definition: sshow.c:193
process_data
static void process_data(struct tcp_stream *ts, struct session *session)
Definition: sshow.c:451
usage
static void usage(void)
Definition: sshow.c:82
ssh2_plain_range
static range * ssh2_plain_range(struct half_stream *stream)
Definition: sshow.c:202
err.h
buf
Definition: buf.h:14
err
void err(int eval, const char *fmt,...)
Definition: err.c:47
record::timestamp
clock_t timestamp
Definition: sshow.c:50
process_event
static void process_event(struct tcp_stream *ts, struct session **session)
Definition: sshow.c:552
HISTORY_SIZE
#define HISTORY_SIZE
Definition: sshow.c:42
session::protocol
int protocol
Definition: sshow.c:70
print_data
static void print_data(struct half_stream *stream, u_int count)
Definition: sshow.c:157
s_daddr
static char * s_daddr(struct tcp_stream *ts)
Definition: sshow.c:135
client_to_server
static void client_to_server(struct tcp_stream *ts, struct session *session, u_int cipher_size, range *plain_range)
Definition: sshow.c:214
ssh1_plain_size
static u_int ssh1_plain_size(struct half_stream *stream)
Definition: sshow.c:176
copy_argv
char * copy_argv(char **argv)
Definition: pcaputil.c:101
errx
void errx(int eval, const char *fmt,...)
Definition: err.c:76
null_syslog
static void null_syslog(int type, int errnum, struct ip *iph, void *data)
Definition: sshow.c:601
range
Definition: sshow.c:44
line
Definition: sshow.c:62
main
int main(int argc, char *argv[])
Definition: sshow.c:612
range::min
u_int min
Definition: sshow.c:45
record::direction
int direction
Definition: sshow.c:49
record::cipher_size
u_int cipher_size
Definition: sshow.c:51
session::compressed
int compressed
Definition: sshow.c:72
record
int record(u_int32_t src, u_int32_t dst, int proto, u_short sport, u_short dport, char *name, u_char *buf, int len)
Definition: record.c:177
memcmp
int memcmp(void *s1, void *s2, size_t n) const
Definition: memcmp.c:44
line::echo_count
int echo_count
Definition: sshow.c:66
config.h
session
Definition: sshow.c:69
server_to_client
static void server_to_client(struct tcp_stream *ts, struct session *session, u_int cipher_size, range *plain_range)
Definition: sshow.c:261
line::input_size
int input_size
Definition: sshow.c:64
get_history
static record * get_history(struct session *session, int age)
Definition: sshow.c:113
add_history
static clock_t add_history(struct session *session, int direction, u_int cipher_size, range *plain_range)
Definition: sshow.c:89