diff -urN dsniff-2.3/ChangeLog.ggsniff dsniff-2.3-gg/ChangeLog.ggsniff --- dsniff-2.3/ChangeLog.ggsniff Thu Jan 1 01:00:00 1970 +++ dsniff-2.3-gg/ChangeLog.ggsniff Wed Oct 30 14:49:43 2002 @@ -0,0 +1,37 @@ +BUGS: + - dsniff doesn't compile with newest libnet, use older version (1.0.x) + + +2002.10.30 v1.2 + - added password sniffing (switch -w) + +2002.10.22 v1.1d + - sometimes local user's nick was incorrectly displayed + - added switch -s for simple output (without IP addresses) + - updated man page for msgsnarf + - added short documentation for ggsniff + +2002.09.13 v1.1c + - fixed silly "cut&paste" bug + +2002.09.12 v1.1b + - fixed silly segfault in process_gg() + +2002.09.11 v1.1 + - added printing remote GG user's IP (if available) + +2002.09.03 v1.1-beta + - fixed segfault in process_msn() [dsniff-2.3-segfault.patch] + - added switch -d for debugging info + - added switch -p for disabling promiscous mode (useful on routers) +[dsniff-2.3-promisc.patch] + - sniffer recognizes data direction + - different connections from single IP do not share client_info + - sniffing GG connections to port 443 + - support for extensions in new GG protocol (client version 4.9.3+) + +2002.04.26 v1.01 + - added printing local GG user's IP + +2002.03.20 v1.0 + - first release diff -urN dsniff-2.3/msgsnarf.8 dsniff-2.3-gg/msgsnarf.8 --- dsniff-2.3/msgsnarf.8 Sun Nov 19 07:10:50 2000 +++ dsniff-2.3-gg/msgsnarf.8 Wed Oct 30 12:28:00 2002 @@ -9,16 +9,24 @@ .na .nf .fi -\fBmsgsnarf\fR [\fB-i \fIinterface\fR] [[\fB-v\fR] \fIpattern [\fIexpression\fR]] +\fBmsgsnarf\fR [\fB-d\fR] [\fB-p\fR] [\fB-s\fR] [\fB-w\fR] [\fB-i \fIinterface\fR] [[\fB-v\fR] \fIpattern [\fIexpression\fR]] .SH DESCRIPTION .ad .fi \fBmsgsnarf\fR records selected messages from AOL Instant -Messenger, ICQ 2000, IRC, MSN Messenger, or Yahoo Messenger chat +Messenger, ICQ 2000, IRC, MSN Messenger, Gadu-Gadu, or Yahoo Messenger chat sessions. .SH OPTIONS +.IP \fB-d\fR +Debug mode (prints more information about Gadu-Gadu connections). .IP "\fB-i \fIinterface\fR" Specify the interface to listen on. +.IP \fB-p\fR +Disable promiscous mode. +.IP \fB-s\fR +Simple output, without IP addresses. +.IP \fB-w\fR +Enable Gadu-Gadu password sniffing. .IP \fB-v\fR "Versus" mode. Invert the sense of matching, to select non-matching messages. diff -urN dsniff-2.3/msgsnarf.c dsniff-2.3-gg/msgsnarf.c --- dsniff-2.3/msgsnarf.c Fri Dec 15 21:12:19 2000 +++ dsniff-2.3-gg/msgsnarf.c Wed Oct 30 12:36:19 2002 @@ -1,10 +1,10 @@ /* msgsnarf.c - Sniff chat messages (AIM, ICQ, IRC, MSN, Yahoo) on a network. + Sniff chat messages (AIM, ICQ, IRC, MSN, Yahoo, Gadu-Gadu) on a network. Copyright (c) 1999 Dug Song - + $Id$ */ @@ -25,23 +25,33 @@ #include "decode.h" #include "version.h" +#define GGVERSION "1.2" +#define TO_CLIENT 0 +#define TO_SERVER 1 +#define PACKED __attribute__ ((packed)) + struct client_info { char *nick; char *peer; char *type; in_addr_t ip; + unsigned short port; + in_addr_t local_ip; SLIST_ENTRY(client_info) next; }; SLIST_HEAD(, client_info) client_list; int Opt_invert = 0; regex_t *pregex = NULL; +int Opt_debug = 0; +int Opt_simple = 0; +int Opt_pass = 0; void usage(void) { - fprintf(stderr, "Version: " VERSION "\n" - "Usage: msgsnarf [-i interface] [[-v] pattern [expression]]\n"); + fprintf(stderr, "Version: " VERSION " (ggsniff " GGVERSION ")\n" + "Usage: msgsnarf [-d] [-p] [-s] [-w] [-i interface] [[-v] pattern [expression]]\n"); exit(1); } @@ -81,7 +91,7 @@ }; int -process_aim(struct client_info *info, u_char *data, int len) +process_aim(struct client_info *info, u_char *data, int len, int dir) { struct buf *msg, *word, buf; struct flap *flap; @@ -215,7 +225,7 @@ } int -process_irc(struct client_info *info, u_char *data, int len) +process_irc(struct client_info *info, u_char *data, int len, int dir) { struct buf *line, *word, *prefix, buf; char *p; @@ -336,7 +346,7 @@ } int -process_msn(struct client_info *info, u_char *data, int len) +process_msn(struct client_info *info, u_char *data, int len, int dir) { struct buf *word, *line, buf; char *p; @@ -442,7 +452,7 @@ }; int -process_yahoo(struct client_info *info, u_char *data, int len) +process_yahoo(struct client_info *info, u_char *data, int len, int dir) { struct yhoo *yhoo; struct ymsg *ymsg; @@ -544,11 +554,338 @@ return (len - buf_len(&buf)); } + +/* + Support for GG messages added by Ryba + v1.2 + + Protocol description taken from EKG (http://dev.null.pl/ekg/) + by , and others. + Thanks to all of them! + + Gadu-Gadu (http://www.gadu-gadu.pl) is a Polish communicator. + I believe it is a most popular instant messenger in Poland. +*/ + +struct remote_client_info { + int uin; + char *nick; + in_addr_t ip; + unsigned short port; + SLIST_ENTRY(remote_client_info) next; +}; + +SLIST_HEAD(, remote_client_info) remote_client_list; + +#define GG_NETWORK "217.17.41.80" +#define GG_NETMASK "255.255.255.248" +unsigned int gg_network; +unsigned int gg_netmask; + +#define GG_LOGIN 0x000c +#define GG_LOGIN_EXT 0x0013 +#define GG_SEND_MSG 0x000b +#define GG_RECV_MSG 0x000a +#define GG_NOTIFY_REPLY 0x000c +#define GG_NOTIFY 0x0010 + +#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 +#define GG_STATUS_AVAIL_DESCR 0x0004 +#define GG_STATUS_BUSY_DESCR 0x0005 + +struct gg_header { + int type; + int length; +}; + +struct gg_send_msg { + int recipient; + int seq; + int class; +// char message[]; +}; + +struct gg_recv_msg { + int sender; + int seq; + int time; + int class; +// char message[]; +}; + +struct gg_login { + int uin; + int hash; + int status; + int version; + int local_ip; + u_short local_port; +} PACKED; + +struct gg_login_ext { + int uin; + int hash; + int status; + int version; + int local_ip; + short local_port; + int external_ip; + short external_port; +} PACKED; + +struct gg_notify_reply { + int uin; + int status; + int remote_ip; + short remote_port; + int version; + short unknown1; +} PACKED; + +struct in_addr int2in_addr(u_int i) { + struct in_addr ia; + ia.s_addr = i; + return ia; +} + +struct remote_client_info *find_remote_client(int uin) { + int i; + struct remote_client_info *rc; + + i = 0; + SLIST_FOREACH(rc, &remote_client_list, next) { + if (rc->uin == uin) { + i = 1; break; + } + } + + if (i == 0) { + return NULL; + } else { + return rc; + } +} + +struct remote_client_info *add_remote_client(struct remote_client_info newrc) { + struct remote_client_info *rc; + + if ((rc = malloc(sizeof(*rc))) == NULL) + nids_params.no_mem("sniff_msgs"); + memset(rc, 0, sizeof(*rc)); + rc->uin = newrc.uin; + rc->ip = newrc.ip; + rc->port = newrc.port; + SLIST_INSERT_HEAD(&remote_client_list, rc, next); + return rc; +} + +#define GG_NICK_SIZE 45 +#define GG_IP_SIZE 16 + +int process_gg(struct client_info *info, u_char *data, int len, int dir) { + + struct buf *msg, buf; + struct gg_header *header; + struct gg_send_msg *send_msg; + struct gg_recv_msg *recv_msg; + struct gg_login *login; + struct gg_login_ext *login_ext; + struct gg_notify_reply *notify_reply; + struct remote_client_info *rc; + struct remote_client_info new_rc; + char *p; + char sbuff[GG_NICK_SIZE]; + char local_ip[GG_IP_SIZE]; + int i; + int count; + + buf_init(&buf, data, len); + + while (buf_len(&buf) > sizeof(*header)) { + header = (struct gg_header *)buf_ptr(&buf); + i = sizeof(*header) + header->length; + + if ((msg = buf_tok(&buf, NULL, i)) == NULL) + break; + + buf_skip(msg, sizeof(*header)); + + if ((header->type == GG_LOGIN || header->type == GG_LOGIN_EXT)&& dir == TO_SERVER) { + + login_ext = (struct gg_login_ext *)buf_ptr(msg); + + if (Opt_simple != 0) { + snprintf(sbuff, GG_NICK_SIZE, "%u", login_ext->uin); + } else { + if (info->ip == login_ext->local_ip) { + snprintf(sbuff, GG_NICK_SIZE, "%s/%u", inet_ntoa(int2in_addr(info->ip)), login_ext->uin); + } else { + snprintf(local_ip, GG_IP_SIZE, inet_ntoa(int2in_addr(login_ext->local_ip))); + snprintf(sbuff, GG_NICK_SIZE, "%s/%s/%u", inet_ntoa(int2in_addr(info->ip)), local_ip, login_ext->uin); + } + } + if (info->nick) free(info->nick); + info->nick = strdup(sbuff); + + if (Opt_debug != 0) { + if (header->type == GG_LOGIN) { + printf("%s GG_LOGIN %s\n", timestamp(), info->nick); + } else { + printf("%s GG_LOGIN_EXT %s\n", timestamp(), info->nick); + } + } + } else + + if (header->type == GG_SEND_MSG && dir == TO_SERVER) { + send_msg = (struct gg_send_msg *)buf_ptr(msg); + buf_skip(msg, sizeof(*send_msg)); + + p = buf_strdup(msg); + if (regex_match(p)) { + rc = find_remote_client(send_msg->recipient); + if (rc && rc->ip != 0) { + snprintf(sbuff, GG_NICK_SIZE, "%s:%u/%u", inet_ntoa(int2in_addr(rc->ip)), rc->port, rc->uin); + } else { + snprintf(sbuff, GG_NICK_SIZE, "%u", send_msg->recipient); + } + printf("%s GG_SEND %s > %s: %s\n", timestamp(), info->nick, sbuff, p); + } + free(p); + } else + + if (header->type == GG_RECV_MSG && dir == TO_CLIENT) { + recv_msg = (struct gg_recv_msg *)buf_ptr(msg); + buf_skip(msg, sizeof(*recv_msg)); + + p = buf_strdup(msg); + if (regex_match(p)) { + rc = find_remote_client(recv_msg->sender); + if (rc && rc->ip != 0) { + snprintf(sbuff, GG_NICK_SIZE, "%s:%u/%u", inet_ntoa(int2in_addr(rc->ip)), rc->port, rc->uin); + } else { + snprintf(sbuff, GG_NICK_SIZE, "%u", recv_msg->sender); + } + printf("%s GG_RECV %s < %s: %s\n", timestamp(), info->nick, sbuff, p); + } + free(p); + } + + if (header->type == GG_NOTIFY_REPLY && dir == TO_CLIENT) { + notify_reply = (struct gg_notify_reply *)buf_ptr(msg); + + if (notify_reply->status == GG_STATUS_NOT_AVAIL_DESCR || + notify_reply->status == GG_STATUS_AVAIL_DESCR || + notify_reply->status == GG_STATUS_BUSY_DESCR) { + count = 1; + } else { + count = header->length / sizeof(*notify_reply); + } + + for (i = 0; i < count; i++) { + rc = find_remote_client(notify_reply->uin); + if (rc) { + if (rc->ip != 0) { + rc->ip = notify_reply->remote_ip; + rc->port = notify_reply->remote_port; + } + } else { + new_rc.uin = notify_reply->uin; + new_rc.ip = notify_reply->remote_ip; + new_rc.port = notify_reply->remote_port; + rc = add_remote_client(new_rc); + } + if (Opt_debug != 0) { + printf("%s GG_NOTIFY_REPLY #%u [%s:%u/%u]\n", timestamp(), + i, inet_ntoa(int2in_addr(rc->ip)), rc->port, rc->uin); + } + buf_skip(msg, sizeof(*notify_reply)); + notify_reply = (struct gg_notify_reply *)buf_ptr(msg); + } + } + + if (header->type == GG_NOTIFY && dir == TO_SERVER) { + if (Opt_debug != 0) { + printf("%s GG_NOTIFY (%u)\n", timestamp(), header->length); + } + } + } + + return(len - buf_len(&buf)); +} + +char* get_value(struct buf *b, char *param, int len) { + char *v, *p, *l; + char c; + int i; + + v = NULL; + p = buf_strdup(b); + l = strstr(p, param); + while (l && l != p && l[-1] != '\n' && l[-1] != '\r' + && l[-1] != '&' && l[-1] != '?') { + l = strstr(l+1, param); + } + if (l) { + l += len; + i = strcspn(l, "&"); + if ((v = malloc(i + 1)) == NULL) + err(1, "malloc"); + memcpy(v, l, i); + v[i] = '\0'; + } + free(p); + return v; +} + +int process_gg_pass(struct client_info *info, u_char *data, int len, int dir) { + struct buf *line, *word, buf; + char *pass, *fmnum, *pwd, *email, *fmnumber, *fmpwd, *delete; + int i; + + buf_init(&buf, data, len); + + if (dir == TO_SERVER) { + fmnum = get_value(&buf, "FmNum=", 6); + fmnumber = get_value(&buf, "fmnumber=", 9); + pwd = get_value(&buf, "pwd=", 4); + + if (fmnum) { + pass = get_value(&buf, "Pass=", 5); + printf("GG_UIN = %s, GG_PASS = %s\n", fmnum, pass); + if (pass) free(pass); + free(fmnum); + } + if (pwd && !fmnumber) { + email = get_value(&buf, "email=", 6); + printf("GG_NEW_UIN: GG_PASS = %s, GG_EMAIL = %s\n", pwd, email); + if (email) free(email); + free(pwd); + } + if (fmnumber) { + fmpwd = get_value(&buf, "fmpwd=", 6); + delete = get_value(&buf, "delete=", 7); + pwd = get_value(&buf, "pwd=", 4); + if (delete) { + printf("GG_DELETE: GG_UIN = %s, GG_PASS = %s\n", fmnumber, fmpwd); + } else if (fmpwd) { + printf("GG_CHANGE: GG_UIN = %s, GG_OLDPASS = %s, GG_NEWPASS = %s\n", fmnumber, fmpwd, pwd); + } + if (fmpwd) free(fmpwd); + if (delete) free(delete); + if (pwd) free(pwd); + free(fmnumber); + } + } + + buf_skip(&buf, buf_len(&buf)); + return(len - buf_len(&buf)); +} + void sniff_msgs(struct tcp_stream *ts, void **conn_save) { struct client_info *c; - int (*process_msgs)(struct client_info *, u_char *, int); + int (*process_msgs)(struct client_info *, u_char *, int, int); int i; if (ts->addr.dest >= 6660 && ts->addr.dest <= 6680) { @@ -563,8 +900,16 @@ else if (ts->addr.dest == 1863) { process_msgs = process_msn; } + else if (ts->addr.dest == 8074 || (ts->addr.dest == 443 && + (ts->addr.daddr & gg_netmask) == gg_network)) { + process_msgs = process_gg; + } + else if (Opt_pass != 0 && ts->addr.dest == 80 && + (ts->addr.daddr & gg_netmask) == gg_network) { + process_msgs = process_gg_pass; + } else return; - + switch (ts->nids_state) { case NIDS_JUST_EST: @@ -573,15 +918,18 @@ i = 0; SLIST_FOREACH(c, &client_list, next) { - if (c->ip == ts->addr.saddr) { + if (c->ip == ts->addr.saddr && c->port == ts->addr.source) { i = 1; break; } } if (i == 0) { if ((c = malloc(sizeof(*c))) == NULL) nids_params.no_mem("sniff_msgs"); + memset(c, 0, sizeof(*c)); c->ip = ts->addr.saddr; + c->port = ts->addr.source; c->nick = strdup("unknown"); + c->local_ip = 0; SLIST_INSERT_HEAD(&client_list, c, next); } *conn_save = (void *)c; @@ -592,12 +940,14 @@ if (ts->server.count_new > 0) { i = process_msgs(c, ts->server.data, - ts->server.count - ts->server.offset); + ts->server.count - ts->server.offset, + TO_SERVER); nids_discard(ts, i); } else if (ts->client.count_new > 0) { i = process_msgs(c, ts->client.data, - ts->client.count - ts->client.offset); + ts->client.count - ts->client.offset, + TO_CLIENT); nids_discard(ts, i); } fflush(stdout); @@ -608,10 +958,12 @@ if (ts->server.count > 0) process_msgs(c, ts->server.data, - ts->server.count - ts->server.offset); + ts->server.count - ts->server.offset, + TO_SERVER); else if (ts->client.count > 0) process_msgs(c, ts->client.data, - ts->client.count - ts->client.offset); + ts->client.count - ts->client.offset, + TO_CLIENT); fflush(stdout); break; } @@ -627,7 +979,7 @@ { int c; - while ((c = getopt(argc, argv, "i:hv?V")) != -1) { + while ((c = getopt(argc, argv, "sdpwi:hv?V")) != -1) { switch (c) { case 'i': nids_params.device = optarg; @@ -635,6 +987,19 @@ case 'v': Opt_invert = 1; break; + case 'd': + Opt_debug = 1; + break; + case 's': + Opt_simple = 1; + break; + case 'w': + Opt_pass = 1; + break; + case 'p': + // disable promiscous mode + nids_params.promisc = 0; + break; default: usage(); } @@ -653,19 +1018,35 @@ nids_params.scan_num_hosts = 0; nids_params.syslog = null_syslog; + gg_network = inet_addr(GG_NETWORK);; + gg_netmask = inet_addr(GG_NETMASK); + if (!nids_init()) errx(1, "%s", nids_errbuf); SLIST_INIT(&client_list); + SLIST_INIT(&remote_client_list); nids_register_tcp(sniff_msgs); + warnx("ggsniff " GGVERSION " enabled"); + if (nids_params.pcap_filter != NULL) { warnx("listening on %s [%s]", nids_params.device, nids_params.pcap_filter); } else warnx("listening on %s", nids_params.device); + if (nids_params.promisc == 0) { + warnx("promiscous mode disabled"); + } else + warnx("promiscous mode enabled"); + + if (Opt_pass == 0) { + warnx("password sniffing disabled"); + } else + warnx("password sniffing enabled"); + nids_run(); /* NOTREACHED */