void parse_http_content_form(struct TCPRECORD *sess, struct NetFrame *frame, const unsigned char *px, unsigned length) { struct TCP_STREAM *stream = &sess->to_server; unsigned offset=0; struct HTTPREQUEST *req = &stream->app.httpreq; enum { POST_NAME_PRE, POST_NAME, POST_NAME_AFTER, POST_VALUE }; while (offset<length) switch (req->value_state) { case POST_NAME_PRE: req->tmp_length = 0; req->value_state = POST_NAME; break; case POST_NAME: while (offset<length && px[offset] != '=') { if (req->tmp_length < sizeof(req->tmp)) req->tmp[req->tmp_length++] = px[offset]; offset++; } if (offset<length) { req->value_state = POST_NAME_AFTER; offset++; req->parm_name = stringtab_lookup(sess->eng->stringtab, req->tmp, req->tmp_length); req->tmp_length = 0; } break; case POST_NAME_AFTER: req->value_state = POST_VALUE; break; case POST_VALUE: while (offset<length && px[offset] != '&') { if (req->tmp_length < sizeof(req->tmp)) req->tmp[req->tmp_length++] = px[offset]; offset++; } if (offset<length) { req->value_state = POST_NAME_PRE; offset++; http_parse_form_field(sess, frame, req->parm_name, req->tmp, req->tmp_length ); } break; } }
/** * This code analyzes a <name=value> pair within a cookie, within the * context of an HTTP request, which includes relavent information * about the host that it comes from */ void parse_http_cookie(struct TCPRECORD *sess, struct NetFrame *frame, const unsigned char *name, unsigned name_length, const unsigned char *value, unsigned value_length) { struct HTTPREQUEST *req = &sess->layer7.httpreq; unsigned char *dec; unsigned dec_length = value_length; if (dec_length > 1000) dec_length = 1000; switch (toupper(name[0])) { case 'C': /* MYSPACE COUNTRY CODE * Example: * COUNTRYCODE=MFMGCisGAQQBgjdYA%2FmgRTBDBgorBgEEAYI3WAMBoDUwMwIDAgABAgJmAwICAMAECOOUmLvDDv%2BRBBB6gRxsNuYMZ2M7SXM7N4fdBAgyFGPRkgkD7Q%3D%3D; */ if (match_name("COUNTRYCODE", name, name_length) && ends_with_t(".myspace.com", req->host)) { unsigned char *dec0; unsigned dec0_length; unsigned i; /* First, "uudecode" this */ dec0 = alloca(value_length); dec0_length = 0; for (i=0; i<value_length; i++) { if (value[i] != '%') dec0[dec0_length++] = value[i]; else { unsigned c=0; i++; if (i<value_length && isxdigit(value[i])) c = hexval(value[i++])<<4; if (i<value_length && isxdigit(value[i])) c |= hexval(value[i++]); dec0[dec0_length++] = (unsigned char)c; i--; } } /* Second, "base64 decode" this */ dec = alloca(dec0_length); dec_length = base64_decode(dec, dec_length, value, value_length); /* Third, "asn.1 decode" this */ { unsigned tag, len; const unsigned char *px = dec; unsigned offset = 0; unsigned length = dec_length; unsigned max_offset; tag = asn1_tag(px,length,&offset); len = asn1_length(frame,px,length, &offset); /* Process the big object */ if (tag == 0x30 && len != 0xFFFFFFFF) { max_offset = offset+len; tag = asn1_tag(px,max_offset,&offset); len = asn1_length(frame,px,max_offset, &offset); /* Process the OID */ if (tag == 0x06) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_OID("MySpace-CountryCode", px+offset, len), 0); } offset += len; } } } break; case 'D': /* go.com (including such properties as espn.com) put in their cookies some sort of location information. Here are some examples that were googled from the web: Z2JyO2VuZztsb25kb247YnJvYWRiYW5kOzU7NTs0Oy0xOzA1MS41MDA7LTAwMC4xMTc7ODI2OzEwMTk4OzQ3ODI7NTsK Z2JyO2VuZztsb25kb247YnJvYWRiYW5kOzU7NTs1Oy0xOzA1MS41MDA7LTAwMC4xMTc7ODI2OzEwMTk4OzQ3ODI7NTsK dXNhO21kO2NvbGxlZ2UgcGFyazt0MTs1OzQ7NDs1MTE7MDM4Ljk5NzstMDc2LjkyODs4NDA7MjE7MTU7NjsK dXNhO3R4O2RhbGxhczticm9hZGJhbmQ7NTs0OzQ7NjIzOzAzMi43ODc7LTA5Ni43OTk7ODQwOzQ0Ozc3OzY7Cg== dXNhO3R4O2RhbGxhczticm9hZGJhbmQ7NTs0OzQ7NjIzOzAzMi43ODc7LTA5Ni43OTk7ODQwOzQ0Ozc3OzY7Cg== dXNhO2dhO2F0bGFudGE7YnJvYWRiYW5kOzU7NTs1OzUyNDswMzMuNzQ5Oy0wODQuMzg4Ozg0MDsxMTszOzY7Cg== dXNhO29yO2JlYXZlcnRvbjticm9hZGJhbmQ7NTszOzM7ODIwOzA0NS40OTE7LTEyMi44MDU7ODQwOzM4OzYyOzY7Cg== dXNhO3R4O2RhbGxhczticm9hZGJhbmQ7NTs0OzQ7NjIzOzAzMi43ODc7LTA5Ni43OTk7ODQwOzQ0Ozc3OzY7Cg== dXNhO3R4O2RhbGxhczticm9hZGJhbmQ7NTs0OzM7NjIzOzAzMi43ODc7LTA5Ni43OTk7ODQwOzQ0Ozc3OzY7Cg== */ if (match_name("DE2", name, name_length) && ends_with_t(".go.com", req->host)) { dec = alloca(dec_length); dec_length = base64_decode(dec, dec_length, value, value_length); JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("GO-LOC", dec, dec_length), 0); } break; case 'E': if (starts_with("EMAIL", name, name_length)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("e-mail", value, value_length), 0); } if (starts_with("E-MAIL", name, name_length)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("e-mail", value, value_length), 0); } break; case 'G': /* d78da8e7eb998e8f571c4c641b104c60cxsAAABVUyxnYSxsYXdyZW5jZXZpbGxlLCwsLCw1MjQ= bf8e3d7c0474fa9da14b6551e6846ec7cxUAAABVUyxnYSxhdGxhbnRhLCwsLCw1MjQ= */ if (match_name("GEO", name, name_length) && ends_with_t(".youtube.com", req->host)) { unsigned i; unsigned comma_count=0; dec = alloca(dec_length); dec_length = base64_decode(dec, dec_length, value, value_length); for (i=dec_length; i>0; i--) { if (dec[i-1] == ',') { /*US,ga,lawrenceville,,,,,524*/ if (++comma_count == 7) break; } } if (i>3 && isalpha(dec[i-2]) && isalpha(dec[i-3])) { unsigned offset=i-3; while (i<dec_length && comma_count > 5) { if (dec[i] == ',') comma_count--; i++; } JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("YouTube-Loc", dec+offset, i-offset-1), 0); } } /*Example: * [email protected]/769779 */ if (match_name("GMAILCHAT", name, name_length) && ends_with_t("mail.google.com", req->host)) { unsigned j; for (j=0; j<value_length && value[j] != '/'; j++) ; JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("e-mail", value, j), 0); } break; case 'I': if (match_name("ID", name, name_length) && ends_with_t(".doubleclick.net", req->host)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("DoubleClick", value, value_length), 0); } break; case 'L': if (match_name("LOGIN", name, name_length) || match_name("LOGIN_X", name, name_length)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("login", value, value_length), 0); if (ends_with_t(".facebook.com", req->host)) { req->login = stringtab_lookup(sess->eng->stringtab, value, value_length); JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("Facebook-user", req->login->str, req->login->length), 0); } if (contains("@", value, value_length)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("e-mail", value, value_length), 0); } else if (contains("%40", value, value_length)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("e-mail", value, value_length), 0); } } break; case 'M': /*http://facebook.com *m_user = warnerc2%40gpc.edu%3A71101757%3ASwUchOEuvzIbYo7E*/ if (match_name("M_USER", name, name_length)) { if (ends_with_t(".facebook.com", req->host)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("e-mail", value, value_length), 0); } } /* http://login.live.com/login.srf * [email protected] */ if (match_name("MSPPRE", name, name_length)) { if (ends_with_t(".live.com", req->host)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("e-mail", value, value_length), 0); } } /* Canadian Broadcasting */ if (match_name("MyCBCSignIn", name, name_length)) { if (ends_with_t(".cbc.ca", req->host)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("e-mail", value, value_length), 0); } } if (match_name("ME", name, name_length)) { if (ends_with_t(".myspace.com", req->host)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("e-mail", value, value_length), 0); } } break; case 'P': if (starts_with("PASSWORD", name, name_length)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("password", value, value_length), 0); } break; case 'U': if (starts_with("USERNAME", name, name_length)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_PRINT("username", value, value_length), 0); } break; case 'W': if (starts_with("WATCHED_VIDEO_ID_LIST_", name, name_length)) { size_t l = sizeof("WATCHED_VIDEO_ID_LIST_")-1; JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("YouTube-ID", name+l, name_length-l), 0); } break; } }
static unsigned parse_ssi_entry(struct TCPRECORD *sess, struct NetFrame *frame, const unsigned char *px, unsigned length) { enum { SSI_BUDDYLEN_HI, SSI_BUDDYLEN_LOW, SSI_BUDDY, SSI_BUDDY_DONE, SSI_GROUPID_HI, SSI_GROUPID_LO, SSI_BUDDYID_HI, SSI_BUDDYID_LO, SSI_TYPE_HI, SSI_TYPE_LOW, SSI_TLVLEN_HI, SSI_TLVLEN_LO, SNAC_TLV_START, SNAC_TLV_TAG_HI, SNAC_TLV_TAG_LO, SNAC_TLV_LEN_HI, SNAC_TLV_LEN_LO, SNAC_TLV_DATA, SNAC_TLV_DONE, SNAC_IGNORE, }; struct AIMPARSER *aim = &sess->layer7.aim; unsigned offset = 0; while (offset<length) switch (aim->ssi_state) { case SSI_BUDDYLEN_HI: aim->tlv_len = px[offset++]; aim->ssi_state++; break; case SSI_BUDDYLEN_LOW: aim->tlv_len <<= 8; aim->tlv_len |= px[offset++]; aim->ssi_state++; strfrag_init(sess->str); strfrag_init(sess->str+1); break; case SSI_BUDDY: if (aim->tlv_len) { unsigned sublen; if (aim->tlv_len < length-offset) sublen = aim->tlv_len; else sublen = length-offset; strfrag_append(sess->str+1, px+offset, sublen); offset += sublen; aim->tlv_len -= sublen; } if (aim->tlv_len == 0) { aim->ssi_state = SSI_BUDDY_DONE; } break; case SSI_BUDDY_DONE: aim->ssi_state++; break; case SSI_GROUPID_HI: case SSI_GROUPID_LO: /* just ignore these fields */ aim->ssi_state++; offset++; break; case SSI_BUDDYID_HI: case SSI_BUDDYID_LO: /* just ignore these fields */ aim->ssi_state++; offset++; break; case SSI_TYPE_HI: aim->ssi_buddy_type = px[offset++]; aim->ssi_state++; break; case SSI_TYPE_LOW: aim->ssi_buddy_type <<= 8; aim->ssi_buddy_type |= px[offset++]; aim->ssi_state++; if (sess->str[1].length) switch (aim->ssi_buddy_type) { case 0x0000: /* individual */ /* TODO: I should also remember what group it is in */ if (aim->ssi_group) JOTDOWN(sess->eng->ferret, JOT_DST("ID-IP",frame), JOT_PRINT("AIM-Buddy", sess->str[1].the_string, sess->str[1].length), JOT_PRINT("AIM-Group", aim->ssi_group->str, aim->ssi_group->length), 0); else JOTDOWN(sess->eng->ferret, JOT_DST("ID-IP",frame), JOT_PRINT("AIM-Buddy", sess->str[1].the_string, sess->str[1].length), 0); break; case 0x0001: /* group */ aim->ssi_group = stringtab_lookup(sess->eng->stringtab, sess->str[1].the_string, sess->str[1].length); strfrag_finish(&sess->str[1]); break; default: /*TODO: add SAMPLE */ break; } break; case SSI_TLVLEN_HI: aim->ssi_len = px[offset++]; aim->ssi_state++; break; case SSI_TLVLEN_LO: aim->ssi_len <<= 8; aim->ssi_len |= px[offset++]; aim->ssi_state++; break; case SNAC_TLV_START: case SNAC_TLV_TAG_HI: case SNAC_TLV_TAG_LO: case SNAC_TLV_LEN_HI: case SNAC_TLV_LEN_LO: case SNAC_TLV_DATA: case SNAC_TLV_DONE: while (offset<length && aim->ssi_len > 0) switch (aim->ssi_state) { case SNAC_TLV_START: strfrag_init(sess->str); aim->ssi_state++; break; case SNAC_TLV_TAG_HI: aim->tlv_tag = px[offset++]; aim->ssi_len--; aim->ssi_state++; break; case SNAC_TLV_TAG_LO: aim->tlv_tag <<= 8; aim->tlv_tag |= px[offset++]; aim->ssi_len--; aim->ssi_state++; break; case SNAC_TLV_LEN_HI: aim->tlv_len = px[offset++]; aim->ssi_len--; aim->ssi_state++; break; case SNAC_TLV_LEN_LO: aim->tlv_len <<= 8; aim->tlv_len |= px[offset++]; aim->ssi_len--; aim->ssi_state++; break; case SNAC_TLV_DATA: if (aim->tlv_len && aim->ssi_len) { unsigned sublen; if (aim->tlv_len < length-offset) sublen = aim->tlv_len; else sublen = length-offset; if (sublen > aim->ssi_len) sublen = aim->ssi_len; parse_tlv(sess, frame, px+offset, sublen); offset += sublen; aim->tlv_len -= sublen; aim->ssi_len -= sublen; } /* We can get here 3 ways. * #1 - the TLV len could have started at zero, in * which case there is no real data to process. * #2 - the TLV len had a value that crossed packets, * and we slowly decremented it by bits # #3 - we got here right after processing a chunk */ if (aim->tlv_len == 0 || aim->ssi_len == 0) { /* If done parsing the TLV, then do a 'close' operation by * sending a NULL data pointer */ parse_tlv(sess, frame, 0, 0); aim->ssi_state = SNAC_TLV_DONE; } break; case SNAC_TLV_DONE: aim->ssi_state = SNAC_TLV_START; break; } if (aim->ssi_len == 0) return offset; /* return the number of bytes we analyzed */ break; case SNAC_IGNORE: /* Just ignore the remainder of the data from this point on */ offset = length; break; } return offset; }
/** * Parses the <name=value> pairs in HTTP URLs and HTTP POST content data */ void http_parse_form_field(struct TCPRECORD *sess, struct NetFrame *frame, struct StringT *name, const unsigned char *value, unsigned value_length) { struct TCP_STREAM *stream = &sess->to_server; struct HTTPREQUEST *req = &stream->app.httpreq; if (name == NULL) return; switch (toupper(name->str[0])) { case 'E': if (match_name_t("EMAIL", name)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("e-mail", value, value_length), 0); if (ends_with_t(".myspace.com", req->host)) { req->login = stringtab_lookup(sess->eng->stringtab, value, value_length); if (req->password) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("MySpace-user", req->login->str, req->login->length), JOT_URLENC("password", req->password->str, req->password->length), 0); } else JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("MySpace-user", req->login->str, req->login->length), 0); } } break; case 'P': if (match_name_t("PASSWORD", name)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("form-password", value, value_length), 0); if (ends_with_t(".myspace.com", req->host)) { req->password = stringtab_lookup(sess->eng->stringtab, value, value_length); if (req->login) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("MySpace-user", req->login->str, req->login->length), JOT_URLENC("password", req->password->str, req->password->length), 0); } } } if (match_name_t("PASSWD", name)) { JOTDOWN(sess->eng->ferret, JOT_SRC("ID-IP", frame), JOT_URLENC("password", value, value_length), 0); } break; case 'Q': if (match_name_t("Q", name)) { if (ends_with_t(".google.com", req->host)) { if (!starts_with("cache:", req->url, req->url_length)) { JOTDOWN(sess->eng->ferret, JOT_SRC("IP", frame), JOT_URLENC("search", value, value_length), 0); } } } break; case 'V': if (match_name_t("VIDEO_ID", name)) { if (ends_with_t(".youtube.com", req->host)) { JOTDOWN(sess->eng->ferret, JOT_SRC("Watches", frame), JOT_URLENC("YouTube", value, value_length), 0); req->youtube_video_id = stringtab_lookup(sess->eng->stringtab, value, value_length); } } break; } }