Пример #1
0
void process_simple_smtp_request(struct TCPRECORD *sess, struct TCP_STREAM *to_server, struct NetFrame *frame, const unsigned char *px, unsigned length)
{
	struct FerretEngine *eng = sess->eng;
	struct Ferret *ferret = eng->ferret;
	char command[16];
	const char *parm;
	unsigned parm_length;
	unsigned i;
	unsigned  x;

	/* IF CLOSING CONNECTION */
	if (px == NULL) {
		return;
	}

	frame->layer7_protocol = LAYER7_SMTP;

	if (to_server->app.smtpreq.is_data) {
		process_simple_smtp_data(sess, to_server, frame, px, length);
		return;
	}

	/* Remove leading whitespace */
	for (i=0; i<length && isspace(px[i]); i++)
		;

	/* Grab command. This means parsing up to the first space
	 * character, or the first ':' character in the case of 
	 * mailfrom: or rcptto: */
	x=0;
again:
	while (i<length && !isspace(px[i]) && px[i] != ':') {
		if (x < sizeof(command) -1) {
			command[x++] = (char)toupper(px[i]);
			command[x] = '\0';
		}
		i++;
	}
	if (i<length && px[i] == ':')
		i++;

	/* skip space after command */
	while (i<length && isspace(px[i]))
		i++;

	if (stricmp(command, "mail")==0 || stricmp(command, "rcpt")==0) {
		if (i >= length)
			return;
		goto again;
	}

	SAMPLE(ferret,"SMTP", JOT_SZ("command", command));

	/* Grab parm */
	parm = (const char*)px+i;
	x=i;
	while (i<length && px[i] != '\n')
		i++;
	parm_length = i-x;

	if (parm_length && parm[parm_length-1] == '\n')
		parm_length--;
	if (parm_length && parm[parm_length-1] == '\r')
		parm_length--;

	JOTDOWN(ferret,
		JOT_SZ("proto", "SMTP"),
		JOT_SZ("op", command),
		JOT_PRINT("parm", parm, parm_length),
		JOT_SRC("client", frame),
		JOT_DST("server", frame),
		0);

	/* test parms */
	if (stricmp(command, "MAILFROM")==0) {
		strip_address(&parm, &parm_length);

		if (sess)
			smtp_copy(to_server->app.smtpreq.from, parm, parm_length);

		JOTDOWN(ferret,
			JOT_SRC("IP", frame),
			JOT_PRINT("e-mail", parm, parm_length),
			0);
	}
	if (stricmp(command, "RCPTTO")==0) {
		strip_address(&parm, &parm_length);

		if (sess)
			smtp_copy(to_server->app.smtpreq.to, parm, parm_length);
		JOTDOWN(ferret,
			JOT_SRC("IP", frame),
			JOT_PRINT("friend",			   parm, parm_length),
			0);
	}

	if (stricmp(command, "DATA")==0 && sess) {
		to_server->app.smtpreq.is_data = 1;
	}
	if (stricmp(command, "RSET")==0 && sess) {
		to_server->app.smtpreq.is_data = 0;
	}


}
Пример #2
0
void process_simple_smtp_data(struct TCPRECORD *sess, struct TCP_STREAM *to_server, struct NetFrame *frame, const unsigned char *px, unsigned length)
{
	struct FerretEngine *eng = sess->eng;
	struct Ferret *ferret = eng->ferret;
	unsigned offset=0;
	unsigned command;
	unsigned command_length;
	unsigned parm;
	unsigned parm_length;

	if (sess == NULL)
		return;

	frame->layer7_protocol = LAYER7_SMTP;

	while (offset<length) {

		/* Handle end-of-email '.' issue */
		if (offset<length && px[offset] == '.') {
			if (offset+1<length && px[offset] == '\n' && offset+2<length && px[offset] == '\r' && px[offset+1] == '\n') {
				to_server->app.smtpreq.is_body = 0;
				to_server->app.smtpreq.is_data = 0;
				return;
			}
		}
		if (to_server->app.smtpreq.is_body) {
			while (offset<length && px[offset] != '\n')
				offset++;
			if (offset<length && px[offset] == '\n')
				offset++;
			continue;
		}


		while (offset<length && isspace(px[offset]) && px[offset] != '\n')
			offset++;
		command = offset;
		
		while (offset<length && px[offset] != ':' && px[offset] != '\n')
			offset++;
		command_length = offset-command;
		if (command_length == 0) {
			to_server->app.smtpreq.is_body = 1;
			continue;
		}

		while (command_length && isspace(px[offset+command_length]))
			command_length--;
		if (command_length && px[offset+command_length] == ':')
			command_length--;
		while (command_length && isspace(px[offset+command_length]))
			command_length--;
	
		while (offset<length && px[offset] == ':')
			offset++;
		while (offset<length && isspace(px[offset]) && px[offset] != '\n')
			offset++;

		parm = offset;
		if ((offset<length && px[offset] == '\n') || (offset+1<length && px[offset] == '\r' && px[offset+1] == '\n')) {
			to_server->app.smtpreq.is_body = 1;
			return;
		}
again:
		while (offset<length && px[offset] != '\n')
			offset++;
		if (offset<length && px[offset] == '\n')
			offset++;
		if (offset<length && px[offset] != '\n' && isspace(px[offset]) && (offset+1<length && px[offset] != '\r' && px[offset] != '\n'))
			goto again;
		parm_length = offset-parm;
		while (parm_length && isspace(px[parm+parm_length-1]))
			parm_length--;

		JOTDOWN(ferret,
				JOT_SZ("proto","RFC822msg"),
				JOT_PRINT("header",			 	px+command,					command_length),
				JOT_PRINT("value",			 	px+parm, parm_length),
				JOT_SRC("client", frame),
				JOT_DST("server", frame),
				0);
		if (is_command("subject", px+command, command_length)) {
			smtp_copy(to_server->app.smtpreq.subject, px+parm, parm_length);
		}
		if (is_command("X-Mailer", px+command, command_length)) {
			JOTDOWN(ferret,
				JOT_SRC("ID-IP", frame),
				JOT_PRINT("X-Mailer",			   px+parm, parm_length),
				0);
		}
		if (is_command("X-MimeOLE", px+command, command_length)) {
			JOTDOWN(ferret,
				JOT_SRC("ID-IP", frame),
				JOT_PRINT("X-MimeOLE",			   px+parm, parm_length),
				0);
		}
	}
}
Пример #3
0
void process_dns(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length)
{
	unsigned offset;
	struct DNS dns[1];
	unsigned record_count;
	unsigned total_records;
	unsigned i;

	/* Count the number of DNS packets we process. This includes
	 * all types of DNS */
	ferret->statistics.dns++;

	memset(dns, 0, sizeof(dns[0]));

	if (length < 12) {
		/* Regress: defcon2008-dns2.pcap(95639) */
		; //FRAMERR(frame, "dns: frame too short\n");
		return;
	}

	/* Parse the DNS header, the 'fixed' portion of the packet
	 * before the variable length records 
		+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
		|                      ID                       |
		+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
		|QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
		+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
		|                    QDCOUNT                    |
		+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
		|                    ANCOUNT                    |
		+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
		|                    NSCOUNT                    |
		+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
		|                    ARCOUNT                    |
		+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
	ID              A 16 bit identifier assigned by the program that
					generates any kind of query.  This identifier is copied
					the corresponding reply and can be used by the requester
					to match up replies to outstanding queries.
	QR              A one bit field that specifies whether this message is a
					query (0), or a response (1).
	OPCODE          A four bit field that specifies kind of query in this
					message.  This value is set by the originator of a query
					and copied into the response.  The values are:
					0               a standard query (QUERY)
					1               an inverse query (IQUERY)
					2               a server status request (STATUS)
					3-15            reserved for future use
	AA              Authoritative Answer - this bit is valid in responses,
					and specifies that the responding name server is an
					authority for the domain name in question section.

					Note that the contents of the answer section may have
					multiple owner names because of aliases.  The AA bit
					corresponds to the name which matches the query name, or
					the first owner name in the answer section.
	TC              TrunCation - specifies that this message was truncated
					due to length greater than that permitted on the
					transmission channel.
	RD              Recursion Desired - this bit may be set in a query and
					is copied into the response.  If RD is set, it directs
					the name server to pursue the query recursively.
					Recursive query support is optional.
	RA              Recursion Available - this be is set or cleared in a
					response, and denotes whether recursive query support is
					available in the name server.
	Z               Reserved for future use.  Must be zero in all queries
					and responses.
	RCODE           Response code - this 4 bit field is set as part of
					responses.  The values have the following
					interpretation:
					0               No error condition
					1               Format error - The name server was
									unable to interpret the query.
					2               Server failure - The name server was
									unable to process this query due to a
									problem with the name server.
					3               Name Error - Meaningful only for
									responses from an authoritative name
									server, this code signifies that the
									domain name referenced in the query does
									not exist.
					4               Not Implemented - The name server does
									not support the requested kind of query.
					5               Refused - The name server refuses to
									perform the specified operation for
									policy reasons.  For example, a name
									server may not wish to provide the
									information to the particular requester,
									or a name server may not wish to perform
									a particular operation (e.g., zone
					6-15            Reserved for future use.
	QDCOUNT         an unsigned 16 bit integer specifying the number of
					entries in the question section.
	ANCOUNT         an unsigned 16 bit integer specifying the number of
					resource records in the answer section.
	NSCOUNT         an unsigned 16 bit integer specifying the number of name
					server resource records in the authority records
					section.
	ARCOUNT         an unsigned 16 bit integer specifying the number of
					resource records in the additional records section.
	*/

	dns->id = ex16be(px+0);
	dns->is_response = ((px[2]&0x80) != 0);
	dns->opcode = (px[2]>>3)&0x01F;
	dns->flags = ex16be(px+2)&0x7F0;
	dns->rcode = px[3]&0x0F;

	dns->question_count = ex16be(px+4);
	dns->answer_count = ex16be(px+6);
	dns->authority_count = ex16be(px+8);
	dns->additional_count = ex16be(px+10);

	/* Remember a total count of the records. There are a lot of corrupted packets
	 * with data after the counted records, so we need to stop parsing once all
	 * the counted records have been parsed. */
	total_records = dns->question_count + dns->answer_count + dns->authority_count + dns->additional_count;

	offset = 12;
	record_count = 0;


	/* After parsing the fixed header, we no PRE-PROCESS the variable length records.
	 * All we want to do at this point is to find their locations within the packet.
	 * The reason we want to pre-process these is that some records will refer to
	 * other records in the same packet. A good example are CNAME records 
	 * that require looking up other records in order to fully resolve. We can
	 * do the resolution easier if we can preprocess the list first */
	while (offset < length && record_count < 100) {
		struct DNSRECORD *rec = &dns->records[record_count];

		/* Even if there is remaining data in the packet, do not parse past
		 * the total count of all the records */
		if (record_count >= total_records) {
			SAMPLE(ferret,"dns", JOT_NUM("too-many-records", total_records));
			break;
		}

		/* NAME
		 * The first part of a DNS record is the variable length name. The name
		 * consists of a sequence of LABELS. Each label starts with a tag. The 
		 * tag can be one of three things:
		 *	- a value of zero, which ends the name
		 *	- a length from 1-63, which means we need to continue processing
		 *    more labels
		 *  - a two-byte 'pointer' to the remainder of the name, which means that
		 *	  we stop pre-processing the name. We don't actually parse out the
		 *	  full name at this stage in the code (so we won't follow that pointer),
		 *	  we are just concerned with skipping the name at this point. */
		rec->name_offset = offset;
		while (offset < length) {

			/* Test for end label */
			if (px[offset] == 0x00) {
				offset++;
				break;
			}

			/* Test for compression 'pointer' */
			if (px[offset] & 0xC0) {
				offset += 2;
				break;
			}

			/* Skip the 'length' number of bytes, plus the length byte itself */
			offset += px[offset] + 1;

			if (offset > length) {
				FRAMERR(frame, "dns: past end of packet\n");
				return;
			}
		}

		/* Now parse out the 'type' and 'class' fields. These are the
		 * 4 bytes immediately following the name */
		if (offset + 4 > length) {
			FRAMERR(frame, "dns: past end of packet\n");
			return;
		}
		rec->type = ex16be(px+offset+0);
		rec->clss = ex16be(px+offset+2);
		offset += 4;
		record_count++;


		/* If this is a 'question' record, then we don't do any further processing.
		 * Since question records are just asking for data, they don't contain
		 * any data themselves. Otherwise, if the record will contain data that
		 * we need to also parse */
		if (record_count <= dns->question_count)
			continue;
		
		/* This bit of code parses out the remainder of the data in the record. For
		 * the most part, we just need to parse the 'length' field for the record
		 * data, remember it for use later, then skip the remainder of this record
		 * and continue processing the next record */
		if (offset + 6 > length) {
			/* Regress: defcon2008-dns2.pcap(88069) */
			FRAMERR(frame, "dns: past end of packet\n");
			return;
		}
		rec->ttl = ex32be(px+offset+0);
		rec->rdata_length = ex16be(px+offset+4);
		offset += 6;
		rec->rdata_offset = offset;
		offset += rec->rdata_length;

		if (offset > length) {
			FRAMERR(frame, "dns: past end of packet\n");
			return;
		}
	}
	dns->record_count = record_count;

	/* We stored the records in one large array, but there are four kinds
	 * of records (questions, answers, authority, additional). We figure out
	 * which records belong to which type according to the counts */
	if (dns->question_count > record_count) {
		dns->question_count = record_count;
		FRAMERR(frame, "%s: bad record count\n", "DNS");
	}
	if (dns->answer_count > record_count - dns->question_count) {
		dns->answer_count = record_count - dns->question_count;
		/* Regress: defcon2008-dns2.pcap(158112) */
		; //FRAMERR(frame, "%s: bad record count\n", "DNS");
	}
	if (dns->authority_count > record_count - dns->question_count - dns->answer_count) {
		dns->authority_count = record_count - dns->question_count - dns->answer_count;
		FRAMERR(frame, "%s: bad record count\n", "DNS");
	}
	if (dns->additional_count > record_count - dns->question_count - dns->answer_count - dns->authority_count) {
		dns->additional_count = record_count - dns->question_count - dns->answer_count - dns->authority_count;
		FRAMERR(frame, "%s: bad record count\n", "DNS");
	}
	dns->questions = &dns->records[0];
	dns->answers = &dns->records[dns->question_count];
	dns->authorities = &dns->records[dns->question_count + dns->answer_count];
	dns->additionals = &dns->records[dns->question_count + dns->answer_count + dns->authority_count];

	/* 
	 * First, we parse out all the question records. These don't contain any data
	 * themselves, but they do give us interesting information about what a client
	 * is looking for. Also, some protocols, such as NetBIOS and mDNS/Bonjour will
	 * tell us additional information about the client.
	 */
	if (dns->is_response && dns->rcode == 0)
	for (i=0; i<dns->question_count; i++) {
		struct DNSRECORD *rec = &dns->questions[i];
		dns_parse_question_record(ferret, frame, px, length, rec, dns);
	}

	/* Now parse all the resource records after the questions */
	for (i=0; i<dns->answer_count; i++) {
		struct DNSRECORD *rec = &dns->answers[i];
		dns_parse_resource_record(ferret, frame, px, length, rec, dns);
	}
	for (i=0; i<dns->authority_count; i++) {
		struct DNSRECORD *rec = &dns->authorities[i];
		dns_parse_resource_record(ferret, frame, px, length, rec, dns);
	}
	for (i=0; i<dns->additional_count; i++) {
		struct DNSRECORD *rec = &dns->additionals[i];
		dns_parse_resource_record(ferret, frame, px, length, rec, dns);
	}

#if 0
	switch (dns->opcode) {
	case 0x00: /*query request*/
	case 0x10: /*query response */

		switch (dns->rcode) {
		case 0:
		case 3: /* No such name */
			SAMPLE(ferret,"DNS", JOT_NUM("rcode", dns->rcode));
			break;
		case 2: /* Server error */
			SAMPLE(ferret,"DNS", JOT_NUM("rcode", dns->rcode));
			break;
		default:
			FRAMERR(frame, "dns: unknown rcode=%d (opcode=%d)\n", dns->rcode, dns->opcode);
		}
		break;
	case 0x06: /*release*/
		switch (dns->rcode) {
		case 0:
			for (i=0; i<dns->additional_count; i++) {
				char name[256];
				unsigned name_length;
				struct DNSRECORD *rec = &dns->additionals[i];

				if (rec->type == 0x8001)
					FRAMERR(frame, "test\n");

				name_length = dns_extract_name(frame, px, length, rec->name_offset, name, sizeof(name));

				switch (rec->type) {
				case 0x0020: /*NETBIOS */
					switch (rec->clss) {
					case 0x0001: /*INTERNET*/
						{
							unsigned ip_address = ex32be(px+rec->rdata_offset+2);
							char netbios_name[256];

							if (rec->rdata_length != 6)
								FRAMERR(frame, "dns: data not 4-bytes long, was %d-bytes instead (class=%d, type=%d, name=%s)\n", rec->rdata_length, rec->clss, rec->type, name);

							translate_netbios_name(frame, name, netbios_name, sizeof(netbios_name));

							JOTDOWN(ferret,
								JOT_SZ("proto","NETBIOS"),
								JOT_SZ("op","release"),
								JOT_DST("ip.src", frame),
								JOT_PRINT("name",		 	netbios_name,				strlen(netbios_name)),
								JOT_IPv4("address", ip_address),
								0);

							JOTDOWN(ferret,
								JOT_IPv4("ID-IP", ip_address),
								JOT_PRINT("netbios",	 	netbios_name,				strlen(netbios_name)),
								0);

						}
						break;
					default:
						FRAMERR(frame, "dns: unknown class=%d (type=%d, name=%s)\n", rec->clss, rec->type, name);
					}
					break;
				default:
					FRAMERR(frame, "dns: unknown type=%d (class=%d, name=%s)\n", rec->type, rec->clss, name);
				}
			}
		}
		break;
	case 0x05: /*netbios registration request*/
		if (frame->dst_port == 53)
			dns_dynamic_update(ferret, frame, px, length, dns);
		else
			process_request_update(ferret, frame, px, length, dns);
		break;
	case 0x08:
		for (i=0; i<dns->additional_count; i++)
			DECODEANSWER(ferret, frame, px, length, dns, &dns->additionals[i], "refresh");
		break;
	case 0x01: /*inverse query request*/
	case 0x11: /*inverse query reqsponse*/
	case 0x02: /*status request*/
	case 0x12: /*status response*/
	case 0x04: /*notify request*/
	case 0x14: /*notify response*/
	case 0x15: /*update response*/
	case 0x0f: /*multi-home registration*/
		for (i=0; i<dns->additional_count; i++)
			DECODEANSWER(ferret, frame, px, length, dns, &dns->additionals[i], "multi-home");
		break;
	default:
		FRAMERR(frame, "dns: unknown opcode %d\n", dns->opcode);
	}
#endif
}
Пример #4
0
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;
}
Пример #5
0
/**
 * This parses a TLV record instead of a SNAC packet, which is itself 
 * inside of a FLAP PDU. We identify the precise item by the SNAC-family,
 * SNAC-subtype, and TLV-tag */
static void 
parse_tlv(struct TCPRECORD *sess, struct NetFrame *frame, const unsigned char *px, unsigned length)
{
	struct FerretEngine *eng = sess->eng;
	struct Ferret *ferret = eng->ferret;
	struct AIMPARSER *aim = &sess->layer7.aim;
	unsigned h;

	/* This function is going to process the data within a SNAC TLV field. 
	 * We are just going to handle this all in a big switch/case statement. */
#define HASH(x,y,z) (((x)<<16)|((y)<<8)|(z))
	h = HASH(aim->pdu.family, aim->pdu.subtype, aim->tlv_tag);

	/* If we are in the middle of parsing the string, just grab
	 * it in our re-assembly buffer */
	if (px != NULL) {
		strfrag_append(sess->str, px, length);
		return;
	}
	
	/* Process the string we've reassembled. We are going to hash on the full context
	 * of the PDU rather than just the TLV tag */
	switch (h) {
	case 0x00170203: /* family=Sign-on, subtype=Logon, tag=client-id-string */
		JOTDOWN(ferret, 
			JOT_SRC("ID-IP",frame),
			JOT_PRINT("AIM-Client-ID", sess->str->the_string, sess->str->length),
			0);
		break;
	case 0x0017020e: /* family=Sign-on, subtype=Logon, tag=country */
		JOTDOWN(ferret, 
			JOT_SRC("ID-IP",frame),
			JOT_PRINT("AIM-Country", sess->str->the_string, sess->str->length),
			0);
		break;
	case 0x0017020f: /* family=Sign-on, subtype=Logon, tag=language */
		JOTDOWN(ferret, 
			JOT_SRC("ID-IP",frame),
			JOT_PRINT("AIM-Language", sess->str->the_string, sess->str->length),
			0);
		break;
	case 0x00170225: /* family=Sign-on, subtype=Logon, tag=password hash */
		JOTDOWN(ferret, 
			JOT_SRC("ID-IP",frame),
			JOT_HEXSTR("AIM-Password-Hash", sess->str->the_string, sess->str->length),
			0);
		break;
		break;
	case 0x00170201: /* family=Sign-on, subtype=Logon, tag=screen-name */
	case 0x00170601: /* family=Sign-on, subtype=Sign-on, tag=screen-name  */
		/* This is the sign-on 'screen-name' in the packet that the user sends to
		 * the logon server (logon.oscar.aol.com). The server will respond with
		 * a 'challenge'. The user will then send the screen-name and hash of 
		 * challenge and password to the real server he wants to connect to */
		JOTDOWN(ferret, 
			JOT_SRC("ID-IP",frame),
			JOT_PRINT("AIM-Screen-Name", sess->str->the_string, sess->str->length),
			0);
		break;
	case 0x00170301: /* family=Logon, subtype=Reply, tag=screen-name  */
		JOTDOWN(ferret, 
			JOT_DST("ID-IP",frame), /* logon reply screen name sent from server*/
			JOT_PRINT("AIM-Screen-Name", sess->str->the_string, sess->str->length),
			0);
		break;
	case 0x00170311: /* family=Logon, subtype=Reply, tag=email  */
		JOTDOWN(ferret, 
			JOT_DST("ID-IP",frame), /* logon reply screen name sent from server*/
			JOT_PRINT("e-mail", sess->str->the_string, sess->str->length),
			0);
		break;
	case 0x00170349: /* family=Logon, subtype=Reply, tag=auth-protocol  */
		JOTDOWN(ferret, 
			JOT_DST("ID-IP",frame), /* logon reply screen name sent from server*/
			JOT_HEXSTR("AIM-digest-sig", sess->str->the_string, sess->str->length),
			0);
		break;
	case 0x00170700: /*AIM Sign-on(0x17), Sign-on Reply(7), Challenge(10)*/
		/* This is the 'challenge' sent back by logon.oscar.aol.com. The user
		 * will hash this with his password in order to logon to all the other
		 * servers.
		 *
		 * Because of this, we need to attach this string to the session going
		 * in the reverse direction. That will enable us to log the authentication
		 * process in case we want to log the hashes */
		JOTDOWN(ferret, 
			JOT_SRC("ID-IP",frame),
			JOT_PRINT("AIM-Challenge", sess->str->the_string, sess->str->length),
			0);
		break;
	case 0x0017038e: /* authorization cookie */
		/* This is a long string to pull out, but it gives anybody who has
		 * this cookie the ability to log onto any AIM service */
		JOTDOWN(ferret, 
			JOT_DST("ID-IP",frame),
			JOT_HEXSTR("AIM-Auth-Cookie", sess->str->the_string, sess->str->length),
			0);
		break;	
	case  0x00030b00: /* INCOMING oncoming buddy name */
	case  0x00020600:
		if (sess->str->length) {
			JOTDOWN(ferret, 
				JOT_DST("ID-IP",frame),
				JOT_PRINT("AIM-Buddy", sess->str->the_string, sess->str->length),
				0);
			JOTDOWN(ferret, 
				JOT_DST("ID-IP",frame),
				JOT_PRINT("friend", sess->str->the_string, sess->str->length),
				0);
		}
		break;
	case 0x00021500: /* OUTGOING user info query*/
		if (sess->str->length) {
			JOTDOWN(ferret, 
				JOT_SRC("ID-IP",frame),
				JOT_PRINT("AIM-Buddy", sess->str->the_string, sess->str->length),
				0);
			JOTDOWN(ferret, 
				JOT_SRC("ID-IP",frame),
				JOT_PRINT("friend", sess->str->the_string, sess->str->length),
				0);
		}
		break;
	case 0x00040700: /* Messaging, incoming */
		if (sess->str->length) {
			JOTDOWN(ferret, 
				JOT_DST("ID-IP",frame),
				JOT_PRINT("AIM-Buddy", sess->str->the_string, sess->str->length),
				0);
			JOTDOWN(ferret, 
				JOT_DST("ID-IP",frame),
				JOT_PRINT("friend", sess->str->the_string, sess->str->length),
				0);

			strfrag_xfer(sess->str+1, sess->str);
		}
		break;
	case 0x00040702: /* Messaging, INCOMING */
		decode_message(sess, frame, sess->str[0].the_string, sess->str[0].length, 0);
		break;
	case 0x00040600: /* Messaging, outgoing */
		if (sess->str->length) {
			JOTDOWN(ferret, 
				JOT_SRC("ID-IP",frame),
				JOT_PRINT("AIM-Buddy", sess->str->the_string, sess->str->length),
				0);
			JOTDOWN(ferret, 
				JOT_SRC("ID-IP",frame),
				JOT_PRINT("friend", sess->str->the_string, sess->str->length),
				0);

			strfrag_xfer(sess->str+1, sess->str);
		}
		break;
	case 0x00040602: /* Messaging, outgoing */
		decode_message(sess, frame, sess->str[0].the_string, sess->str[0].length, 1);
		break;
	case 0x00020604: /* Buddy Info - away message */
		if (sess->str->length) {
			JOTDOWN(ferret, 
				JOT_DST("ID-IP",frame),
				JOT_PRINT("AIM-Buddy", sess->str[1].the_string, sess->str[1].length),
				JOT_PRINT("Away-Message", sess->str[0].the_string, sess->str[0].length),
				0);
		}
		break;
	case 0x00041400: /* typing, outgoing */
		if (sess->str->length) {
			JOTDOWN(ferret, 
				JOT_SRC("ID-IP",frame),
				JOT_PRINT("AIM-Buddy", sess->str->the_string, sess->str->length),
				0);
			JOTDOWN(ferret, 
				JOT_SRC("ID-IP",frame),
				JOT_PRINT("friend", sess->str->the_string, sess->str->length),
				0);
		}
		break;
	case 0x00040605: /* File transfer */
		parse_message_filexfer_rendezvous(sess, frame, sess->str->the_string, sess->str->length);
		break;

	case 0x00040701:
	case 0x00040703:
	case 0x00040705:
	case 0x0004070b:
	case 0x0004070f:
	case 0x00040713:
	case 0x00040716:
	case 0x0004071d:
	case 0x00040603: /* Messaging, outgoing, server-ack requested */
		break;
	case 0x00130601: /* SNAC Server Side Information Entry List*/
	case 0x001306c9: 
	case 0x001306d6: 
	case 0x0013066a:
	case 0x0013066d: 
	case 0x00130631: 
		/* This is the start of an SSI Entry list, maybe we should 
		 * remember this??? */
		break;
	case 0x00130731:
		if (sess->str[1].length && sess->str[0].length) {
			JOTDOWN(ferret, 
				JOT_DST("ID-IP",frame),
				JOT_PRINT("AIM-Buddy", sess->str[1].the_string, sess->str[1].length),
				JOT_PRINT("AIM-Description", sess->str[0].the_string, sess->str[0].length),
				0);
		}
		break;
	/* Others that I've seen */
	case 0x0017064b:
	case 0x0017065a:
	case 0x0017024c:
	case 0x00170216:
	case 0x00170217:
	case 0x00170218:
	case 0x00170219:
	case 0x0017021a:
	case 0x00170214:
	case 0x0017024a:
	case 0x00170305:
	case 0x00170306:
	case 0x00170313:
	case 0x00170354:
	case 0x00170340:
	case 0x00170343:
	case 0x00170341:
	case 0x00170342:
	case 0x00170348:
	case 0x00170344:
	case 0x00170347:
	case 0x00170345:
	case 0x00170346:
	case 0x00020301:
	case 0x00020302:
	case 0x00020305:
	case 0x00020303:
	case 0x00020304:
	case 0x00030302:
	case 0x00030301:
	case 0x00030304:
	case 0x00040504:
	case 0x00090302:
	case 0x00090301:
	case 0x00020405:
	case 0x00011e1d:
	case 0x00011e06:
	case 0x0004060d:
	case 0x00020601: /* Buddy Info - User Class */
	case 0x00020603: /* Buddy Info - Online Since AND Away Msg Encoding ??? */
	case 0x00020605: /* Buddy Info - Member Since */
	case 0x0002060b: /* Buddy Info - unknown timestamp */
	case 0x0002060d: /* Buddy Info - Capabilities List */
	case 0x0002060f: /* Buddy Info - Session Length */
	case 0x0002061d: /* Buddy Info - Available Message */
	case 0x0002061f: /* Buddy Info - unknown */
	case 0x00020623: /* Buddy Info - unknown timestamp */
	case 0x00020626: /* Buddy Info - unknown timestamp (member since?) */
	case 0x00020627: /* Buddy Info - unknown timestamp */

		break;
	case 0x001306C8: /*SSI: members of this group */
		break;
	default:
		/* TODO: add SAMPLE here */
		switch (h&0xFFFFFF00) {
		case 0x00130300:
			break;
		default:
			FRAMERR(frame, "%s: unknown TLV tag: 0x%08x\n", "AIM", h);
		}
		break;
	}
	strfrag_finish(sess->str);
}
Пример #6
0
void decode_message(struct TCPRECORD *sess, struct NetFrame *frame, const unsigned char *px, unsigned length, unsigned is_outgoing)
{
	struct FerretEngine *eng = sess->eng;
	struct Ferret *ferret = eng->ferret;
	const unsigned char *msg = px;
	unsigned msg_length = length;
	unsigned msg_offset = 0;

	if (msg_length > 2 && ex16be(msg+msg_offset) == 0x501) {
		/*unsigned flags = ex16be(msg+msg_offset);*/
		unsigned len=0;
		msg_offset += 2;
		
		if (msg_offset+2 < msg_length) {
			len = ex16be(msg+msg_offset);
			msg_offset += len+2;
		}

		if (msg_offset+2 < msg_length)
			msg_offset += 2; /* block info */

		if (msg_offset+2 < msg_length) {
			len = ex16be(msg+msg_offset);
			msg_offset += 2; /* block length */
		}

		msg_offset += 4; /* character set */
		if (len > 4)
			len -= 4; /* subtract the charset info from the block lenght*/

		if (msg_offset > msg_length) {
			FRAMERR(frame, "%s: integer overflow\n", "AIM");
			return;
		}

		if (msg_offset + len > msg_length)
			len = msg_length - msg_offset;

		if (len > 6 && strnicmp((const char*)msg+msg_offset, "<HTML>", 6)==0) {
			unsigned char *msg2 = alloca(len);
			unsigned msg2_len;

			msg2_len = strip_html_tags(msg+msg_offset, len, msg2, len);

			if (is_outgoing)
				JOTDOWN(ferret, 
					JOT_SRC("ID-IP",frame),
					JOT_PRINT("AIM-Message-To", sess->str[1].the_string, sess->str[1].length),
					JOT_PRINT("AIM-Message", msg2, msg2_len),
					0);
			else
				JOTDOWN(ferret, 
					JOT_DST("ID-IP",frame),
					JOT_PRINT("AIM-Message-From", sess->str[1].the_string, sess->str[1].length),
					JOT_PRINT("AIM-Message", msg2, msg2_len),
					0);

		} else  {
			if (is_outgoing)
				JOTDOWN(ferret, 
					JOT_SRC("ID-IP",frame),
					JOT_PRINT("AIM-Message-To", sess->str[1].the_string, sess->str[1].length),
					JOT_PRINT("AIM-Message", msg+msg_offset, msg_length-msg_offset),
					0);
			else
				JOTDOWN(ferret, 
					JOT_DST("ID-IP",frame),
					JOT_PRINT("AIM-Message-From", sess->str[1].the_string, sess->str[1].length),
					JOT_PRINT("AIM-Message", msg+msg_offset, msg_length-msg_offset),
					0);
		}

	} else {
		while (msg_offset<msg_length && msg[msg_offset] < 26)
			msg_offset++;

		if (is_outgoing)
			JOTDOWN(ferret, 
				JOT_SRC("ID-IP",frame),
				JOT_PRINT("AIM-Message-To", sess->str[1].the_string, sess->str[1].length),
				JOT_PRINT("AIM-Message", msg+msg_offset, msg_length-msg_offset),
				0);
		else
			JOTDOWN(ferret, 
				JOT_DST("ID-IP",frame),
				JOT_PRINT("AIM-Message-From", sess->str[1].the_string, sess->str[1].length),
				JOT_PRINT("AIM-Message", msg+msg_offset, msg_length-msg_offset),
				0);
	}
}
Пример #7
0
void process_ymsg_server_response(
		struct TCPRECORD *sess, 
		struct NetFrame *frame, 
		struct StringReassembler *ymsg_packet)
{
	struct FerretEngine *eng = sess->eng;
	struct Ferret *ferret = eng->ferret;
	unsigned service = sess->layer7.ymsg.service;
	unsigned status = sess->layer7.ymsg.status;
	struct Atom atom;

	switch (service) {
	/*YAHOO_SERVICE_AUTH		Authentication */
	case 0x57: 
		switch (status) {
		case 1: /*Ack*/
			{
				struct Atom challenge;
				struct Atom algorithm;
			
				atom = ymsg_get_enumerated_item(ymsg_packet, "1"); /* user account name */
				challenge = ymsg_get_enumerated_item(ymsg_packet, "94"); /* challenge from the server */
				algorithm = ymsg_get_enumerated_item(ymsg_packet, "13"); /* which authentication algorithm to use */
			
				/* Note the username */
				if (atom.len) {
					JOTDOWN(ferret,
						JOT_DST("ID-IP", frame),
						JOT_PRINT("username", atom.px+atom.offset, atom.len),
						0);
					strncpy_s(sess->layer7.ymsg.username, sizeof(sess->layer7.ymsg.username), atom.px+atom.offset, atom.len);
				}

				/* Note the password hash */
				if (atom.len && challenge.len && algorithm.len && atom_is_number(algorithm)) {
					strfrag_init(sess->str+1);
					strfrag_append(sess->str+1, challenge.px+challenge.offset, challenge.len);
					sess->layer7.ymsg.pwhash_algorithm = atom_to_number(algorithm);
				}
			}
			break;
		default:
			FRAMERR_UNPARSED(frame, "YMSG.service", service);
		}
		break;
	case 85: /* Ref: 2009-01-24-1.pcap(6320) */
		atom = ymsg_get_enumerated_item(ymsg_packet, "3"); /* user account name */
		if (atom.len) {
			JOTDOWN(ferret,
				JOT_DST("ID-IP", frame),
				JOT_PRINT("username", atom.px+atom.offset, atom.len),
				0);
			strncpy_s(sess->layer7.ymsg.username, sizeof(sess->layer7.ymsg.username), atom.px+atom.offset, atom.len);
		}
		break;
	case 241:
		/* Ref: 2009-01-24-1.pcap(6323) */
		/*TODO*/
		break;
	case 240:
		/* Ref: 2009-01-24-1.pcap(6326) */
		/*TODO*/
		break;
	case 239:
		/* Ref: 2009-01-24-1.pcap(6326) */
		/*TODO*/
		break;
	case 18:
		/* Ref: 2009-01-24-1.pcap(6326) */
		/*TODO*/
		break;
	case 11: /* new mail has arrived */
		/* Ref: 2009-01-24-1.pcap(8505) */
		/*TODO*/
		break;
	default:
		FRAMERR_UNPARSED(frame, "YMSG.service", service);
	}
}