CVE-2020-15173 (C, published 2020-09-09T23:15Z)
Description: value: In ACCEL-PPP (an implementation of PPTP/PPPoE/L2TP/SSTP), there is a buffer overflow when receiving an l2tp control packet ith an AVP which type is a string and no hidden flags, length set to less than 6. If your application is used in open networks or there are untrusted nodes in the network it is highly recommended to apply the patch. The problem was patched with commit 2324bcd5ba12cf28f47357a8f03cd41b7c04c52b As a workaround changes of commit 2324bcd5ba12cf28f47357a8f03cd41b7c04c52b can be applied to older versions.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-120 \u2014 Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
Repository: https://github.com/accel-ppp/accel-ppp
Files changed:
accel-pppd/ctrl/l2tp/l2tp_prot.h
accel-pppd/ctrl/l2tp/packet.c
Commit message: l2tp: fix RCE through buffer overflow & fix LE/BE compatibility

Unsufficent checks of valid l2tp header & avp length cause possible
RCE through buffer overflow, reported by https://github.com/WinMin
swings & leommxj, Chaitin Security Research Lab. Add missed header
length and avp length validation to fix the issue.

Order of struct bitfields is implementation-defined so current code
doesn't play well with big-endian arch. switch to explicit flag bit
checking/gathering to fix the issue.

RFC 2661 and 3931 requires that length, seqeuence flags must be set
and offset flag must not be set, so avp-premissive can't help in
this cases.


Vulnerable code:
	uint8_t P:1;
	uint8_t O:1;
	uint8_t reserved2:1;
	uint8_t S:1;
	uint8_t reserved1:2;
	uint8_t L:1;
	uint8_t T:1;
	uint8_t ver:4;
	uint8_t reserved3:4;
/*#define L2TP_T(hdr) (hdr->flags >> 15)
#define L2TP_L(hdr) ((hdr->flags >> 14) & 1)
#define L2TP_S(hdr) ((hdr->flags >> 10) & 1)
#define L2TP_O(hdr) ((hdr->flags >> 8) & 1)
#define L2TP_VER(hdr) (hdr->flags & 0xf)*/
	uint16_t length:10;
	uint16_t reserved:4;
	uint16_t H:1;
	uint16_t M:1;

	if (pack->hdr.ver == 2) {
	} else {
	pack->hdr.ver = ver;
	pack->hdr.T = 1;
	pack->hdr.L = 1;
	pack->hdr.S = 1;
	if (avp->length < sizeof(struct l2tp_avp_t) + 2) {
			 ntohs(avp->type), avp->length);
	attr_len = avp->length - sizeof(struct l2tp_avp_t);
	int n, length;
	uint8_t *buf;
	struct l2tp_dict_attr_t *da;
	struct l2tp_attr_t *attr, *RV = NULL;
	uint8_t *ptr;
	struct l2tp_packet_t *pack;
	socklen_t len = sizeof(addr);
	struct msghdr msg;
	char msg_control[128];
	struct cmsghdr *cmsg;
  *p = NULL;
		msg.msg_controllen = 128;

	n = recvfrom(fd, buf, L2TP_MAX_PACKET_SIZE, 0, &addr, &len);

	if (n < 6) {
	if (hdr->T == 0)

	if (n < ntohs(hdr->length)) {
			log_warn("l2tp: short packet received (%i/%i)\n", n, ntohs(hdr->length));

	if (hdr->ver == 2) {
		if (hdr->L == 0) {
			if (conf_verbose)
				log_warn("l2tp: incorrect message received (L=0)\n");
			if (!conf_avp_permissive)
			    goto out_err_hdr;
		}

		if (hdr->S == 0) {
				log_warn("l2tp: incorrect message received (S=0)\n");
			if (!conf_avp_permissive)
			    goto out_err_hdr;
		if (hdr->O == 1) {
			if (conf_verbose)
				log_warn("l2tp: incorrect message received (O=1)\n");
			if (!conf_avp_permissive)
			    goto out_err_hdr;
		}
	} else if (hdr->ver != 3) {
			log_warn("l2tp: protocol version %i is not supported\n", hdr->ver);
	length = ntohs(hdr->length) - sizeof(*hdr);
	while (length) {
		*(uint16_t *)ptr = ntohs(*(uint16_t *)ptr);
		avp = (struct l2tp_avp_t *)ptr;
		if (avp->length > length) {
			if (conf_verbose)
				log_warn("l2tp: unknown avp received (type=%i, M=%u)\n", ntohs(avp->type), avp->M);
			if (avp->M && !conf_avp_permissive)
			if (da->M != -1 && da->M != avp->M) {
				if (conf_verbose)
					log_warn("l2tp: incorrect avp received (type=%i, M=%i, must be %i)\n", ntohs(avp->type), avp->M, da->M);
			if (da->H != -1 && da->H != avp->H) {
				if (conf_verbose)
					log_warn("l2tp: incorrect avp received (type=%i, H=%i, must be %i)\n", ntohs(avp->type), avp->H, da->H);
			if (avp->H) {
			}
			attr = mempool_alloc(attr_pool);
			memset(attr, 0, sizeof(*attr));
			list_add_tail(&attr->entry, &pack->attrs);

			if (avp->H) {
				orig_avp_len = avp->length;
			attr->M = avp->M;
		ptr += avp->length;
		length -= avp->length;
	uint8_t *buf = mempool_alloc(buf_pool);
	uint8_t *ptr;
	int n;
	int len = sizeof(pack->hdr);

	ptr = buf + sizeof(pack->hdr);
		avp->M = attr->M;
		avp->H = attr->H;
		avp->length = sizeof(*avp) + attr->length;
		*(uint16_t *)ptr = htons(*(uint16_t *)ptr);
	n = sendto(sock, buf, ntohs(pack->hdr.length), 0,
		   &pack->addr, sizeof(pack->addr));
	if (n != ntohs(pack->hdr.length)) {
			log_warn("l2tp: short write (%i/%i)\n", n, ntohs(pack->hdr.length));

Fixed code:
#define L2TP_FLAG_T   0x8000
#define L2TP_FLAG_L   0x4000
#define L2TP_FLAG_S   0x0800
#define L2TP_FLAG_O   0x0200
#define L2TP_FLAG_P   0x0100
#define L2TP_VER_MASK 0x000f

	uint16_t flags;
#define L2TP_AVP_FLAG_M   0x8000
#define L2TP_AVP_FLAG_H   0x4000
#define L2TP_AVP_LEN_MASK 0x03ff
	uint16_t flags;
	switch (pack->hdr.flags & L2TP_VER_MASK) {
	case 2:
		break;
	case 3:
		break;
	default:
		print("[L2TP unknown version]\n");
		return;
	pack->hdr.flags = L2TP_FLAG_T | L2TP_FLAG_L | L2TP_FLAG_S | (ver & L2TP_VER_MASK);
	uint16_t avp_len;
	avp_len = avp->flags & L2TP_AVP_LEN_MASK;
	if (avp_len < sizeof(struct l2tp_avp_t) + 2) {
			 ntohs(avp->type), avp_len);
	attr_len = avp_len - sizeof(struct l2tp_avp_t);
	struct l2tp_packet_t *pack;
	struct l2tp_attr_t *RV = NULL;
	socklen_t addr_len;
	uint8_t *buf, *ptr;
	int n, length;
	*p = NULL;
		struct msghdr msg;
		struct cmsghdr *cmsg;
		char msg_control[128];

		msg.msg_controllen = sizeof(msg_control);
	addr_len = sizeof(addr);
	n = recvfrom(fd, buf, L2TP_MAX_PACKET_SIZE, 0, &addr, &addr_len);
	if (n < sizeof(*hdr)) {
	hdr->flags = ntohs(hdr->flags);
	if (!(hdr->flags & L2TP_FLAG_T))
	if (!(hdr->flags & L2TP_FLAG_L)) {
			log_warn("l2tp: incorrect control message received (L=0)\n");
	if (!(hdr->flags & L2TP_FLAG_S)) {
		if (conf_verbose)
			log_warn("l2tp: incorrect control message received (S=0)\n");
		goto out_err_hdr;
	}
	switch (hdr->flags & L2TP_VER_MASK) {
	case 2:
		if (hdr->flags & L2TP_FLAG_O) {
				log_warn("l2tp: incorrect control message received (O=1)\n");
			goto out_err_hdr;
		break;
	case 3:
		break;
	default:
		if (conf_verbose)
			log_warn("l2tp: protocol version %i is not supported\n",
				 hdr->flags & L2TP_VER_MASK);
		goto out_err_hdr;
	}
	length = ntohs(hdr->length);
	if (length < sizeof(*hdr)) {
			log_warn("l2tp: short packet received (%i/%zu)\n", length, sizeof(*hdr));
		goto out_err_hdr;
	} else if (n < length) {
		if (conf_verbose)
			log_warn("l2tp: short packet received (%i/%i)\n", n, length);
	length -= sizeof(*hdr);
	while (length > 0) {
		struct l2tp_dict_attr_t *da;
		struct l2tp_attr_t *attr;
		uint16_t avp_len;
		if (length < sizeof(*avp)) {
			if (conf_verbose)
				log_warn("l2tp: short avp received\n");
			goto out_err;
		}

		avp = (struct l2tp_avp_t *)ptr;
		avp->flags = ntohs(avp->flags);
		avp_len = avp->flags & L2TP_AVP_LEN_MASK;
		if (avp_len < sizeof(*avp)) {
			if (conf_verbose)
				log_warn("l2tp: short avp received\n");
			goto out_err;
		} else if (length < avp_len) {
			if (conf_verbose) {
				log_warn("l2tp: unknown avp received (type=%i, M=%u)\n",
					 ntohs(avp->type), !!(avp->flags & L2TP_AVP_FLAG_M));
			}
			if ((avp->flags & L2TP_AVP_FLAG_M) && !conf_avp_permissive)
			if (da->M != -1 && !da->M != !(avp->flags & L2TP_AVP_FLAG_M)) {
				if (conf_verbose) {
					log_warn("l2tp: incorrect avp received (type=%i, M=%i, must be %i)\n",
						 ntohs(avp->type), !!(avp->flags & L2TP_AVP_FLAG_M), da->M);
				}
			if (da->H != -1 && !da->H != !(avp->flags & L2TP_AVP_FLAG_H)) {
				if (conf_verbose) {
					log_warn("l2tp: incorrect avp received (type=%i, H=%i, must be %i)\n",
						 ntohs(avp->type), !!(avp->flags & L2TP_AVP_FLAG_H), da->H);
				}
			if (avp->flags & L2TP_AVP_FLAG_H) {
				orig_avp_len = avp_len;
			attr = mempool_alloc(attr_pool);
			memset(attr, 0, sizeof(*attr));
			attr->M = !!(avp->flags & L2TP_AVP_FLAG_M);
			list_add_tail(&attr->entry, &pack->attrs);
		ptr += avp_len;
		length -= avp_len;
	struct l2tp_hdr_t *hdr;
	uint8_t *buf, *ptr;
	int n, len;
	buf = mempool_alloc(buf_pool);
	hdr = (struct l2tp_hdr_t *)buf;
	ptr = (uint8_t *)(hdr + 1);
	len = sizeof(pack->hdr);
		avp->flags = htons((attr->M ? L2TP_AVP_FLAG_M : 0) |
				   (attr->H ? L2TP_AVP_FLAG_H : 0) |
				   ((sizeof(*avp) + attr->length) & L2TP_AVP_LEN_MASK));
	hdr->flags = htons(pack->hdr.flags);
	n = sendto(sock, buf, len, 0, &pack->addr, sizeof(pack->addr));
	if (n != len) {
			log_warn("l2tp: short write (%i/%i)\n", n, len);