예제 #1
0
void process_icmpv6(struct Ferret *ferret, struct NetFrame *frame, const unsigned char *px, unsigned length)
{
    unsigned type = px[0];
    unsigned code = px[1];
    unsigned checksum = ex16be(px+2);

    UNUSEDPARM(length);
    UNUSEDPARM(frame);
    UNUSEDPARM(checksum);

    frame->layer4_protocol = LAYER4_ICMP;

    JOTDOWN(ferret,
            JOT_SZ("TEST","icmp"),
            JOT_NUM("type",type),
            JOT_NUM("code",code),
            0);

    if (frame->dst_ipv6[0] == 0xFF)
        JOTDOWN(ferret,
                JOT_MACADDR("ID-MAC", frame->src_mac),
                JOT_IPv6("ipv6", frame->src_ipv6, 16),
                0);
}
예제 #2
0
/**
 * This is the main function of the DNS parser.
 *
 * This is where the each DNS 'answer' (or 'additional' or 'authoritative') 
 * record is parsed. Mostly, we ignore the return code, though some functions
 * pay attention to and provide slightly different information depending
 * upon the opcode.
 */
static void 
dns_parse_resource_record(struct Ferret *ferret, struct NetFrame *frame, 
						  const unsigned char *px, unsigned length, 
						  struct DNSRECORD *rec, struct DNS *dns)
{
	char name[512]; /* reserve a longer name than the max theoretical limit */
	unsigned name_length;
	char name2[512]; /* reserve a longer name than the max theoretical limit */
	unsigned name2_length;
	unsigned ip_address;
	unsigned offset = rec->rdata_offset;
	unsigned offset_max = MIN(rec->rdata_offset+rec->rdata_length, length);

	/* MULTICAST DNS (mDNS): handle the multicast DNS records differently
	 * from normal DNS records. */
	if (!dns->is_response && frame->dst_port == 5353) {
		bonjour_parse_resource_record(ferret, frame, px, length, rec, dns);
		return; 
	} else if (dns->is_response && (frame->src_port == 5353 || (frame->dst_port == 5353 && frame->src_port != 53))) {
		bonjour_parse_resource_record(ferret, frame, px, length, rec, dns);
		return;
	}

	/* NETBIOS: handle NetBIOS records differently from normal DNS records */
	if (!dns->is_response && frame->dst_port == 137) {
		netbios_parse_resource_record(ferret, frame, px, length, rec, dns);
		return; 
	} else if (dns->is_response && frame->src_port == 137) {
		netbios_parse_resource_record(ferret, frame, px, length, rec, dns);
		return;
	}


	/* First, let's extract a pretty version of the name */
	name_length = dns_extract_name(frame, px, length, rec->name_offset, name, sizeof(name));
	
	if (rec->type == 0x8001)
		FRAMERR(frame, "TODO\n");

	if (rec->clss == 0xfe)
		return;

	/* RFC2671 - Extension Mechanisms for DNS (EDNS0) */
	if (rec->type == 41) {
		/* Regress: defcon2008/dump000.pca(12541) */
		/* TODO: parse this */
		return;
	}

	/* Haven't implemented dynamic update yet
	 * TODO: */
	if (dns->opcode == 21 || dns->opcode == 5)
		return;

	switch (rec->type<<16 | rec->clss) {
	case TYPECLASS(1,0x8001): /* type=A(IPv4 address), class=INTERNET(cache flush) */
		bonjour_parse_resource_record(ferret, frame, px, length, rec, dns);
		break;
	case TYPECLASS(1,1): /* type=A(IPv4 address), class=INTERNET */
		if (!is_valid_opcode(dns->opcode, 0x10, 5, -1)) {
			FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode);
			return;
		}

		ip_address = ex32be(px+rec->rdata_offset);
		if (rec->rdata_length != 4)
			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);

		JOTDOWN(ferret,
			JOT_PRINT("ID-DNS", name,	name_length),
			JOT_IPv4("address",	ip_address),
			0);
		break;
	case TYPECLASS(2,1): /* type=NS, class=INTERNET */
		if (!is_valid_opcode(dns->opcode, 0x10, -1)) {
			FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode);
			return;
		}
		name2_length = dns_extract_name(frame, px, length, rec->rdata_offset, name2, sizeof(name2));
		ip_address = dns_resolve_alias(frame, px, length, dns, name2, 0);

		JOTDOWN(ferret,
			JOT_PRINT("ID-DNS",	name, name_length),
			JOT_PRINT("Name-Server", name2, name2_length),
			JOT_IPv4("address", ip_address),
			0);
		break;
	case TYPECLASS(5,1): /*type=CNAME(aliased canonical name), class=INTERNET */
		if (!is_valid_opcode(dns->opcode, 0x10, 5, -1)) {
			FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode);
			return;
		}
		name2_length = dns_extract_name(frame, px, length, rec->rdata_offset, name2, sizeof(name2));

		ip_address = dns_resolve_alias(frame, px, length, dns, name2, 0);

		if (ip_address != 0) {
			JOTDOWN(ferret,
				JOT_PRINT("ID-DNS", name,	name_length),
				JOT_IPv4("alias",ip_address),
				0);
		}
		JOTDOWN(ferret,
			JOT_PRINT("ID-DNS", name,	name_length),
			JOT_PRINT("alias", name2, name2_length),
			0);
		break;
	case TYPECLASS(6,1): /*type=SOA, class=INTERNET*/
		if (!is_valid_opcode(dns->opcode, 0x10, -1)) {
			FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode);
			return;
		}
		/*
		 * Authoritative Name Server
		 */
		name2_length = dns_extract_name(frame, px, length, offset, name2, sizeof(name2));
		JOTDOWN(ferret,
			JOT_PRINT("ID-DNS",	name, name_length),
			JOT_SZ("SOA", "Start of zone authority"),
			JOT_PRINT("Name-Server", name2, name2_length),
			0);
		ip_address = dns_resolve_alias(frame, px, length, dns, name2, 0);
		if (ip_address)
		JOTDOWN(ferret,
			JOT_PRINT("ID-DNS",	name, name_length),
			JOT_SZ("SOA", "Start of zone authority"),
			JOT_PRINT("Name-Server", name2, name2_length),
			JOT_IPv4("address", ip_address),
			0);
		skip_name(px, length, &offset);

		/* Contact */
		if (offset < offset_max) {
			name2_length = dns_extract_name(frame, px, length, offset, name2, sizeof(name2));
			JOTDOWN(ferret,
				JOT_PRINT("ID-DNS",	name, name_length),
				JOT_SZ("SOA", "Start of zone authority"),
				JOT_PRINT("Contact", name2, name2_length),
				0);
			skip_name(px, length, &offset);
		}

		break;
	case TYPECLASS(10,1): /* type=NULL, class=INTERNET*/
		/* Regress: defcon2008-dns2.pcap(100803): name=Vaaaaiaqaac.tunnel.fastcoder.net */
		/* I'm not sure what this is, other than passing data as Null records.
		 * This would be a good thing for an intrusion-detection system to trigger
		 * on. */
		break;
	case TYPECLASS(12,0x8001): /*type=PTR, class=INTERNET */
		bonjour_parse_resource_record(ferret, frame, px, length, rec, dns);
		break;
	case TYPECLASS(12,1): /*type=PTR(pointer reverse lookup), class=INTERNET */
		if (!is_valid_opcode(dns->opcode, 0x10, -1)) {
			FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode);
			return;
		}
		if (name_length > 6 && memcmp(name+name_length-6, ".local", 6) == 0) {

			JOTDOWN(ferret,
				JOT_SRC("ID-IP", frame),
				JOT_PRINT("Service", name,name_length),
				0);

			/* Extract MAC address */
			{
				const unsigned char *p_name;
				unsigned name_length;
				const unsigned char *p_mac = find_mac(px, MIN(length, rec->rdata_offset+rec->rdata_length), rec->rdata_offset, &p_name, &name_length);
				if (p_mac) {
					JOTDOWN(ferret,
						JOT_SRC("ID-IP", frame),
						JOT_PRINT("mac",		 	p_mac,						19),
						0);
					JOTDOWN(ferret,
						JOT_SRC("ID-IP", frame),
						JOT_PRINT("name",		 	p_name,						name_length),
						0);
				}
			}

		} else if (endsWith(name, ".in-addr.arpa")) {
			/* Extract a 4-byte IPv4 address 
			 * Example: "18.0.0.10.in-addr.arpa"*/
			unsigned ipv4=0;
			unsigned i;
			unsigned j=0;

			for (i=0; i<4; i++) {
				unsigned num = 0;

				for (; name[j] && name[j] != '.'; j++) {
					if ('0' <= name[j] && name[j] <= '9')
						num = num * 10 + name[j]-'0';
				}
				while (name[j] == '.')
					j++;
				ipv4 |= num<<(i*8);
			}
			/* Now get the name it points to */
			name2_length = dns_extract_name(frame, px, length, offset, name2, sizeof(name2));

			JOTDOWN(ferret,
				JOT_PRINT("ID-DNS", name2, name2_length),
				JOT_IPv4("ID-IP", ipv4),
				JOT_SRC("dnssrv", frame),
				0);
		} else
			; //FRAMERR(frame, "dns: unknown PTR record\n");
		break;
	case TYPECLASS(13,0x8001): /*type=HINFO, class=INTERNET */
		bonjour_parse_resource_record(ferret, frame, px, length, rec, dns);
		break;
	case TYPECLASS(15,1): /*type=MX, class=INTERNET */
		/* Regress: defcon2008-dns2.pcap(18661) */
		break;
	case TYPECLASS(16,0x8001):		/*type=TXT, class=INTERNET(cache flush)*/
		bonjour_parse_resource_record(ferret, frame, px, length, rec, dns);
		break;
	case TYPECLASS(16,1):		/*type=TXT, class=INTERNET */
		if (!is_valid_opcode(dns->opcode, 0x10, 5, -1)) {
			FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode);
			return;
		}

		if (stricmp(name, "current.cvd.clamav.net") == 0) {
			/* This is a single string containing a version string, like:
			 * 0.91.1:44:3855:1186270141:1
			 */
			break;
		} else if (starts_with("_DM-NOTIFICATION.", name, strlen(name))) {
			/* Regress: defcon2008\dump001.pcap(87082) */
			/* TODO */
			break;
		} else if (endsWith(name, "._workstation._tcp.local")) {
			/* Regress: defcon2008-dns2.pcap(56127): "mike-desktop [00:0c:29:f6:58:ca]._workstation._tcp.local" */
			break;
		} else if (endsWith(name, ".asn.cymru.com")) {
			/* Regress: defcon2008-dns2.pcap(98958) */
			/* This is a system for mapping IP to ASN numbers:
			 * http://www.team-cymru.org/Services/ip-to-asn.html */
			break;
		} else if (endsWith(name, ".wrs.trendmicro.com")) {
			/* Regress: defcon2008-dns2.pcap(184904) */
			/* Appears to check whether IP addresses are trustworthy */
			break;
		} else {
			FRAMERR(frame, "%s: unknown TXT record %s", "DNS", name);
		}
		break;
	case TYPECLASS(0x1c,1): /*type=AAAA(IPv6 address), class=INTERNET*/
	case TYPECLASS(0x1c,255): /*type=AAAA(IPv6 address), class=INTERNET*/
		if (!is_valid_opcode(dns->opcode, 0x10, 5, -1)) {
			FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode);
			return;
		}
		switch (dns->opcode) {
		case 0x10:
			{
				const unsigned char *ipv6_address = px+rec->rdata_offset;
				if (rec->rdata_length != 16)
					FRAMERR(frame, "dns: data not 16-bytes long, was %d-bytes instead (class=%d, type=%d, name=%s)\n", rec->rdata_length, rec->clss, rec->type, name);

				JOTDOWN(ferret,
					JOT_SZ("proto","DNS"),
					JOT_SZ("op","lookup"),
					JOT_SRC("ip.src", frame),
					JOT_PRINT("name", name, name_length),
					JOT_IPv6("address", ipv6_address,				16),
					0);
			}
		case 5: /* dynamic update*/
			/* Regress: defcon2008-dns2.pcap(7958) */
			break;
		default:
			FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode);
		}
		break;
	case TYPECLASS(33,1): /*type=SRV, class=INTERNET */
		if (!is_valid_opcode(dns->opcode, 0x10, -1)) {
			FRAMERR(frame, "%s: unknown opcode=%d\n", "DNS", dns->opcode);
			return;
		}

		if (rec->rdata_length < 7)
			FRAMERR(frame, "dns: unknown type=%d (class=%d, name=%s)\n", rec->type, rec->clss, name);
		else {
			unsigned port = px[rec->rdata_offset+4]<<8 | px[rec->rdata_offset+5];
			name2_length = dns_extract_name(frame, px, length, rec->rdata_offset+6, name2, sizeof(name2));
			ip_address = dns_resolve_alias(frame, px, length, dns, name2, 0);

			if (ip_address != 0) {
				JOTDOWN(ferret,
					JOT_PRINT("ID-DNS", name,	name_length),
					JOT_PRINT("Server", name2,	name2_length),
					JOT_NUM("Port", port),
					JOT_IPv4("IPv4",ip_address),
					0);
			} else
				JOTDOWN(ferret,
					JOT_PRINT("ID-DNS", name,	name_length),
					JOT_PRINT("Server", name2,	name2_length),
					JOT_NUM("Port", port),
					0);
		}
		break;
	default:
		FRAMERR(frame, "dns: unknown type=%d (class=%d, name=%s)\n", rec->type, rec->clss, name);
	}
}